diff --git a/glance/tests/functional/data/keystone_data.py b/glance/tests/functional/data/keystone_data.py deleted file mode 100644 index 62df83901b..0000000000 --- a/glance/tests/functional/data/keystone_data.py +++ /dev/null @@ -1,181 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack, LLC -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import re -import sys - -import keystone.manage - -DEFAULT_FIXTURE = [ -# Tenants - ('tenant', 'add', 'openstack'), - ('tenant', 'add', 'pattieblack'), - ('tenant', 'add', 'froggy'), - ('tenant', 'add', 'bacon'), - ('tenant', 'add', 'prosciutto'), -# Users - ('user', 'add', 'pattieblack', 'secrete', 'pattieblack'), - ('user', 'add', 'froggy', 'secrete', 'froggy'), - ('user', 'add', 'bacon', 'secrete', 'bacon'), - ('user', 'add', 'prosciutto', 'secrete', 'prosciutto'), - ('user', 'add', 'admin', 'secrete', 'openstack'), -# Roles - ('role', 'add', 'Admin'), - ('role', 'add', 'KeystoneServiceAdmin'), - ('role', 'grant', 'Admin', 'admin'), - ('role', 'grant', 'KeystoneServiceAdmin', 'admin'), -# Tokens - ('token', 'add', '887665443383', 'pattieblack', 'pattieblack', - '2015-02-05T00:00'), - ('token', 'add', '383344566788', 'froggy', 'froggy', - '2015-02-05T00:00'), - ('token', 'add', '111111111111', 'bacon', 'bacon', - '2015-02-05T00:00'), - ('token', 'add', '222222222222', 'prosciutto', 'prosciutto', - '2015-02-05T00:00'), - ('token', 'add', '999888777666', 'admin', 'openstack', - '2015-02-05T00:00'), -#Services - #2 Service Name:swift Type:object-store - ('service', 'add', 'swift', - 'object-store', 'Swift-compatible service'), - ('service', 'add', 'object_store', - 'object-store', 'Swift-compatible service'), - #3 Service Name:cdn Type:object-store - ('service', 'add', 'cdn', - 'object-store', 'Swift-compatible service'), - #4 Service Name:nova Type:compute - ('service', 'add', 'nova', - 'compute', 'OpenStack Compute Service'), - ('service', 'add', 'compute', - 'compute', 'OpenStack Compute Service'), - ('service', 'add', 'compute_v1', - 'compute', 'OpenStack Compute Service'), - #5 Service Name:nova_compat Type:Compute - ('service', 'add', 'nova_compat', - 'compute', 'OpenStack Compute Service'), - #6 Service Name:glance Type:image - ('service', 'add', 'glance', - 'image', 'OpenStack Image Service'), - ('service', 'add', 'image', - 'image', 'OpenStack Image Service'), - #7 Service Name:keystone Type:identity - ('service', 'add', 'identity', - 'identity', 'OpenStack Identity Service'), -# Keeping for compatibility for a while till dashboard catches up - ('endpointTemplates', 'add', 'RegionOne', 'swift', - 'http://swift.publicinternets.com/v1/AUTH_%tenant_id%', - 'http://swift.admin-nets.local:8080/', - 'http://127.0.0.1:8080/v1/AUTH_%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova_compat', - 'http://nova.publicinternets.com/v1.0/', - 'http://127.0.0.1:8774/v1.0', 'http://localhost:8774/v1.0', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova', - 'http://nova.publicinternets.com/v1.1/', 'http://127.0.0.1:8774/v1.1', - 'http://localhost:8774/v1.1', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'glance', - 'http://127.0.0.1:%api_port%/v1', - 'http://nova.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:9292/v1.1/%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'cdn', - 'http://cdn.publicinternets.com/v1.1/%tenant_id%', - 'http://cdn.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:7777/v1.1/%tenant_id%', '1', '0'), -# endpointTemplates - ('endpointTemplates', 'add', 'RegionOne', 'object_store', - 'http://swift.publicinternets.com/v1/AUTH_%tenant_id%', - 'http://swift.admin-nets.local:8080/', - 'http://127.0.0.1:8080/v1/AUTH_%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'compute', - 'http://nova.publicinternets.com/v1.0/', 'http://127.0.0.1:8774/v1.0', - 'http://localhost:8774/v1.0', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'compute_v1', - 'http://nova.publicinternets.com/v1.1/', 'http://127.0.0.1:8774/v1.1', - 'http://localhost:8774/v1.1', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'image', - 'http://glance.publicinternets.com/v1.1/%tenant_id%', - 'http://nova.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:9292/v1.1/%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'cdn', - 'http://cdn.publicinternets.com/v1.1/%tenant_id%', - 'http://cdn.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:7777/v1.1/%tenant_id%', '1', '0'), -# Tenant endpointsGlobal endpoint not added - ('endpoint', 'add', 'openstack', '1'), - ('endpoint', 'add', 'openstack', '2'), - ('endpoint', 'add', 'openstack', '3'), - ('endpoint', 'add', 'openstack', '4'), - ('endpoint', 'add', 'openstack', '5'), - ('endpoint', 'add', 'pattieblack', '1'), - ('endpoint', 'add', 'pattieblack', '2'), - ('endpoint', 'add', 'pattieblack', '3'), - ('endpoint', 'add', 'pattieblack', '4'), - ('endpoint', 'add', 'pattieblack', '5'), - ('endpoint', 'add', 'froggy', '1'), - ('endpoint', 'add', 'froggy', '2'), - ('endpoint', 'add', 'froggy', '3'), - ('endpoint', 'add', 'froggy', '4'), - ('endpoint', 'add', 'froggy', '5'), - ('endpoint', 'add', 'bacon', '1'), - ('endpoint', 'add', 'bacon', '2'), - ('endpoint', 'add', 'bacon', '3'), - ('endpoint', 'add', 'bacon', '4'), - ('endpoint', 'add', 'bacon', '5'), - ('endpoint', 'add', 'prosciutto', '1'), - ('endpoint', 'add', 'prosciutto', '2'), - ('endpoint', 'add', 'prosciutto', '3'), - ('endpoint', 'add', 'prosciutto', '4'), - ('endpoint', 'add', 'prosciutto', '5'), -] - - -def _get_subsitutions(args): - substitutions = {} - for arg in args: - matches = re.match('(.*)=(.*)', arg) - if matches: - token = '%%%s%%' % matches.group(1) - value = matches.group(2) - substitutions[token] = value - return substitutions - - -def _expand(cmd, substitutions): - expanded = () - for word in cmd: - for token in substitutions.keys(): - word = word.replace(token, substitutions[token]) - expanded = expanded + (word,) - return expanded - - -def load_fixture(fixture=DEFAULT_FIXTURE, args=None): - substitutions = _get_subsitutions(sys.argv) - - keystone.manage.parse_args(args) - - for cmd in fixture: - expanded = _expand(cmd, substitutions) - keystone.manage.process(*expanded) - - -def main(): - load_fixture() - - -if __name__ == '__main__': - main() diff --git a/glance/tests/functional/keystone_utils.py b/glance/tests/functional/keystone_utils.py deleted file mode 100644 index 691f643871..0000000000 --- a/glance/tests/functional/keystone_utils.py +++ /dev/null @@ -1,316 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack, LLC -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Base test class for keystone-related tests""" - -import datetime -import os -import shutil -import sys -import time - -from glance.tests import functional -from glance.tests.utils import execute, find_executable - - -pattieblack_token = '887665443383' -pattieblack_id = '2' -froggy_token = '383344566788' -froggy_id = '3' -admin_token = '999888777666' -bacon_token = '111111111111' -bacon_id = '4' -prosciutto_token = '222222222222' - - -class KeystoneServer(functional.Server): - """ - Class used to easily manage starting and stopping a keystone - server during functional test runs. - """ - def __init__(self, server_control, server_name, test_dir, port, - auth_port, admin_port): - super(KeystoneServer, self).__init__(test_dir, port) - self.no_venv = True - - self.server_control = server_control - self.server_name = server_name - self.auth_port = auth_port - self.admin_port = admin_port - - default_sql_connection = 'sqlite:///%s/keystone.db' % test_dir - self.sql_connection = os.environ.get('GLANCE_TEST_KEYSTONE_SQL', - default_sql_connection) - - self.pid_file = os.path.join(self.test_dir, '%s.pid' % server_name) - self.log_file = os.path.join(self.test_dir, '%s.log' % server_name) - self.conf_base = """[DEFAULT] -verbose = %(verbose)s -debug = %(debug)s -default_store = sqlite -log_file = %(log_file)s -backends = keystone.backends.sqlalchemy -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'glance' : 'X-Image-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(auth_port)s -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -service_ssl = False -admin_ssl = False - -[keystone.backends.sqlalchemy] -sql_connection = %(sql_connection)s -backend_entities = ['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', - 'User', 'Credentials', 'EndpointTemplates', 'Token', - 'Service'] -sql_idle_timeout = 30 - -[pipeline:admin] -pipeline = urlrewritefilter admin_api - -[pipeline:keystone-legacy-auth] -pipeline = urlrewritefilter legacy_auth RAX-KEY-extension service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlrewritefilter] -paste.filter_factory = keystone.middleware.url:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory - -[filter:RAX-KEY-extension] -paste.filter_factory = - keystone.contrib.extensions.service.raxkey.frontend:filter_factory -""" - - -class AuthServer(KeystoneServer): - """ - Server object that starts/stops/manages the keystone auth server - """ - - def __init__(self, server_control, test_dir, auth_port, admin_port): - super(AuthServer, self).__init__(server_control, 'auth', - test_dir, auth_port, - auth_port, admin_port) - - -class AdminServer(KeystoneServer): - """ - Server object that starts/stops/manages the keystone admin server - """ - - def __init__(self, server_control, test_dir, auth_port, admin_port): - super(AdminServer, self).__init__(server_control, 'admin', - test_dir, admin_port, - auth_port, admin_port) - - -def patch_copy(base, src, offset, old, new): - base.insert(src + offset, base[src].replace(old, new)) - - -def conf_patch(server, **subs): - # First, pull the configuration file - paste_base = server.paste_conf_base.split('\n') - - # Need to find the pipeline - for idx, text in enumerate(paste_base): - if text.startswith('[pipeline:glance-'): - # OK, the lines to repeat in modified form - # are this and the next one... - modidx = idx - break - - # Now we need to add a new pipeline, replacing the default context field... - server.deployment_flavor = 'tokenauth+keystoneshim' - patch_copy(paste_base, modidx, 2, ']', '-tokenauth+keystoneshim]') - patch_copy(paste_base, modidx + 1, 2, 'context', 'tokenauth keystone_shim') - - # Put the conf back together and append the keystone pieces - server.paste_conf_base = '\n'.join(paste_base) + """ -[filter:tokenauth] -paste.filter_factory = keystone.middleware.auth_token:filter_factory -service_protocol = http -service_host = 127.0.0.1 -service_port = %%(bind_port)s -auth_host = 127.0.0.1 -auth_port = %(admin_port)s -auth_protocol = http -auth_uri = http://127.0.0.1:%(admin_port)s/ -admin_token = 999888777666 -delay_auth_decision = 1 - -[filter:keystone_shim] -paste.filter_factory = glance.common.wsgi:filter_factory -glance.filter_factory = - keystone.middleware.glance_auth_token:KeystoneContextMiddleware -""" % subs - - -class KeystoneTests(functional.FunctionalTest): - """ - Base test class for keystone-related tests. - """ - - KEYSTONE = None - - def setUp(self): - """ - Look up keystone-control. - """ - - if not self.inited: - KeystoneTests.inited = True - - # Try looking up the keystone executable - cmdname = 'keystone-control' - KeystoneTests.KEYSTONE = find_executable(cmdname) - - # If we don't have keystone-control, disable ourself - if self.KEYSTONE is None: - KeystoneTests.disabled = True - KeystoneTests.disabled_message = "Keystone not installed" - - # Make sure to call superclass - super(KeystoneTests, self).setUp() - - if self.disabled: - return - - # Also need keystone auth and admin ports... - self.auth_port = functional.get_unused_port() - self.admin_port = functional.get_unused_port() - - # Set up the servers - self.auth_server = AuthServer(self.KEYSTONE, self.test_dir, - self.auth_port, self.admin_port) - self.admin_server = AdminServer(self.KEYSTONE, self.test_dir, - self.auth_port, self.admin_port) - - # Include their pid files, too - self.pid_files.extend([self.auth_server.pid_file, - self.admin_server.pid_file]) - - # Have to patch the api and registry config files for keystone - # integration - conf_patch(self.api_server, auth_port=self.auth_port, - admin_port=self.admin_port) - conf_patch(self.registry_server, auth_port=self.auth_port, - admin_port=self.admin_port) - self.registry_server.paste_conf_base += ( - 'context_class = glance.registry.context.RequestContext\n') - - def tearDown(self): - super(KeystoneTests, self).tearDown() - if not self.disabled: - self._reset_database(self.auth_server.sql_connection) - - 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)[0] - datafile = os.path.join(os.path.dirname(__file__), 'data', - 'keystone_data.py') - - cmd = "python %s -c %s api_port=%d" % \ - (datafile, keystone_conf, self.api_server.bind_port) - execute(cmd) - - # 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 wait_for_keystone_servers(self, timeout=3): - """ - Tight loop, waiting for both Auth and Admin server to be - available on the ports. Returns when both are pingable. - There is a timeout on waiting for the servers to come up. - - :param timeout: Optional, defaults to 3 seconds - """ - now = datetime.datetime.now() - timeout_time = now + datetime.timedelta(seconds=timeout) - while (timeout_time > now): - if (self.ping_server(self.auth_port) and - self.ping_server(self.admin_port)): - return - now = datetime.datetime.now() - time.sleep(0.05) - self.assertFalse(True, "Failed to start keystone servers.") - - def stop_servers(self): - """ - Called to stop the started servers in a normal fashion. Note - that cleanup() will stop the servers using a fairly draconian - method of sending a SIGTERM signal to the servers. Here, we - use the glance-control and keystone-control stop method to - gracefully shut the servers down. This method also asserts - that the shutdown was clean, and so it is meant to be called - during a normal test case sequence. - """ - - # Spin down the auth server... - exitcode, out, err = self.auth_server.stop() - self.assertEqual(0, exitcode, - "Failed to spin down the Auth server. " - "Got: %s" % err) - - # ...and the admin server... - exitcode, out, err = self.admin_server.stop() - self.assertEqual(0, exitcode, - "Failed to spin down the Admin server. " - "Got: %s" % err) - - # Now on to everything else... - super(KeystoneTests, self).stop_servers() diff --git a/glance/tests/functional/test_private_images.py b/glance/tests/functional/test_private_images.py deleted file mode 100644 index 119a92519b..0000000000 --- a/glance/tests/functional/test_private_images.py +++ /dev/null @@ -1,903 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack, LLC -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Functional test case that verifies private images functionality""" - -import httplib2 -import json -import os - -from glance.tests import functional -from glance.tests.functional import keystone_utils -from glance.tests.utils import (execute, - skip_if_disabled, - minimal_headers, - minimal_add_command, - ) - -FIVE_KB = 5 * 1024 -FIVE_GB = 5 * 1024 * 1024 * 1024 - - -class TestPrivateImagesApi(keystone_utils.KeystoneTests): - """ - Functional tests to verify private images functionality. - """ - - @skip_if_disabled - def test_private_images_notadmin(self): - """ - Test that we can upload an owned image; that we can manipulate - its is_public setting; and that appropriate authorization - checks are applied to other (non-admin) users. - """ - self.cleanup() - self.start_servers() - - # First, we need to push an image up - image_data = "*" * FIVE_KB - headers = minimal_headers('Image1', public=False) - headers['X-Auth-Token'] = keystone_utils.pattieblack_token - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'POST', headers=headers, - body=image_data) - self.assertEqual(response.status, 201) - data = json.loads(content) - self.assertEqual(data['image']['size'], FIVE_KB) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], False) - self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) - - image_id = data['image']['id'] - - # Next, make sure froggy can't list the image - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Shouldn't show up in the detail list, either - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Also check that froggy can't get the image metadata - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 404) - - # Froggy shouldn't be able to get the image, either. - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 404) - - # Froggy shouldn't be able to give themselves permission too - # easily... - headers = {'X-Auth-Token': keystone_utils.froggy_token, - 'X-Image-Meta-Is-Public': 'True'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 404) - - # Froggy shouldn't be able to give themselves ownership, - # either - headers = {'X-Auth-Token': keystone_utils.froggy_token, - 'X-Image-Meta-Owner': 'froggy'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 404) - - # Froggy can't delete it, either - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'DELETE', headers=headers) - self.assertEqual(response.status, 404) - - # Pattieblack should be able to see the image in lists - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # And in the detail list - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - self.assertEqual(data['images'][0]['is_public'], False) - self.assertEqual(data['images'][0]['owner'], - keystone_utils.pattieblack_id) - - # Pattieblack should be able to get the image metadata - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - # And of course the image itself - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, "*" * FIVE_KB) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - # Pattieblack should be able to manipulate is_public - headers = {'X-Auth-Token': keystone_utils.pattieblack_token, - 'X-Image-Meta-Is-Public': 'True'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], True) - self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) - - # Pattieblack can't give the image away, however - headers = {'X-Auth-Token': keystone_utils.pattieblack_token, - 'X-Image-Meta-Owner': 'froggy'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], True) - self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) - - # Now that the image is public, froggy can see it - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Should also be in details - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - self.assertEqual(data['images'][0]['is_public'], True) - self.assertEqual(data['images'][0]['owner'], - keystone_utils.pattieblack_id) - - # Froggy can get the image metadata now... - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "True") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - # And of course the image itself - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, "*" * FIVE_KB) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "True") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - # Froggy still can't change is-public - headers = {'X-Auth-Token': keystone_utils.froggy_token, - 'X-Image-Meta-Is-Public': 'False'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 403) - - # Or give themselves ownership - headers = {'X-Auth-Token': keystone_utils.froggy_token, - 'X-Image-Meta-Owner': 'froggy'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 403) - - # Froggy can't delete it, either - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'DELETE', headers=headers) - self.assertEqual(response.status, 403) - - # But pattieblack can - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'DELETE', headers=headers) - self.assertEqual(response.status, 200) - - self.stop_servers() - - @skip_if_disabled - def test_private_images_admin(self): - """ - Test that admin users can manipulate is_public and owner - settings on an image. - """ - self.cleanup() - self.start_servers() - - # Need to push an image up - image_data = "*" * FIVE_KB - headers = minimal_headers('Image1', public=False) - headers['X-Auth-Token'] = keystone_utils.pattieblack_token - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'POST', headers=headers, - body=image_data) - self.assertEqual(response.status, 201) - data = json.loads(content) - self.assertEqual(data['image']['size'], FIVE_KB) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], False) - self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) - - image_id = data['image']['id'] - - # Make sure admin does not see image by default - headers = {'X-Auth-Token': keystone_utils.admin_token} - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Shouldn't show up in the detail list, either - headers = {'X-Auth-Token': keystone_utils.froggy_token} - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Admin should see the image if we're looking for private - # images specifically - headers = {'X-Auth-Token': keystone_utils.admin_token} - path = "http://%s:%d/v1/images?is_public=false" % ("0.0.0.0", - self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Also in the detail list... - headers = {'X-Auth-Token': keystone_utils.admin_token} - path = ("http://%s:%d/v1/images/detail?is_public=false" % - ("0.0.0.0", self.api_port)) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - self.assertEqual(data['images'][0]['is_public'], False) - self.assertEqual(data['images'][0]['owner'], - keystone_utils.pattieblack_id) - - image_id = data['images'][0]['id'] - - # Admin should be able to get the image metadata - headers = {'X-Auth-Token': keystone_utils.admin_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - # And of course the image itself - headers = {'X-Auth-Token': keystone_utils.admin_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, "*" * FIVE_KB) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - # Admin should be able to manipulate is_public - headers = {'X-Auth-Token': keystone_utils.admin_token, - 'X-Image-Meta-Is-Public': 'True'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], True) - self.assertEqual(data['image']['owner'], - keystone_utils.pattieblack_id) - - # Admin should also be able to change the ownership of the - # image - headers = {'X-Auth-Token': keystone_utils.admin_token, - 'X-Image-Meta-Owner': 'froggy'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], True) - self.assertEqual(data['image']['owner'], 'froggy') - - # Even setting it to no owner - headers = {'X-Auth-Token': keystone_utils.admin_token, - 'X-Image-Meta-Owner': ''} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], True) - self.assertEqual(data['image']['owner'], None) - - # Make sure pattieblack can see it, since it's unowned but - # public - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # But if we change it back to private... - headers = {'X-Auth-Token': keystone_utils.admin_token, - 'X-Image-Meta-Is-Public': 'False'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], False) - self.assertEqual(data['image']['owner'], None) - - # Now pattieblack can't see it in the list... - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Or in the details list... - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # But pattieblack should be able to access the image metadata - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertFalse('x-image-meta-owner' in response) - - # And of course the image itself - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'GET', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(content, "*" * FIVE_KB) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertFalse('x-image-meta-owner' in response) - - # Pattieblack can't change is-public, though - headers = {'X-Auth-Token': keystone_utils.pattieblack_token, - 'X-Image-Meta-Is-Public': 'True'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 404) - - # Or give themselves ownership - headers = {'X-Auth-Token': keystone_utils.pattieblack_token, - 'X-Image-Meta-Owner': keystone_utils.pattieblack_id} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 404) - - # They can't delete it, either - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'DELETE', headers=headers) - self.assertEqual(response.status, 404) - - self.stop_servers() - - @skip_if_disabled - def test_private_images_anon(self): - """ - Test that anonymous users can access images but not manipulate - them. - """ - self.cleanup() - self.start_servers() - - # Make sure anonymous user can't push up an image - image_data = "*" * FIVE_KB - headers = minimal_headers('Image1', public=False) - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'POST', headers=headers, - body=image_data) - self.assertEqual(response.status, 403) - - # Now push up an image for anonymous user to try to access - image_data = "*" * FIVE_KB - headers = minimal_headers('Image1', public=False) - headers['X-Auth-Token'] = keystone_utils.pattieblack_token - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'POST', headers=headers, - body=image_data) - self.assertEqual(response.status, 201) - data = json.loads(content) - self.assertEqual(data['image']['size'], FIVE_KB) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], False) - self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) - - image_id = data['image']['id'] - - # Make sure anonymous user can't list the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Shouldn't show up in the detail list, either - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Also check that anonymous can't get the image metadata - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD') - self.assertEqual(response.status, 404) - - # Nor the image, either. - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 404) - - # Anonymous shouldn't be able to make the image public... - headers = {'X-Image-Meta-Is-Public': 'True'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 403) - - # Nor change ownership... - headers = {'X-Image-Meta-Owner': 'froggy'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 403) - - # Nor even delete it... - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'DELETE') - self.assertEqual(response.status, 403) - - # Now, let's use our admin credentials and change the - # ownership to None... - headers = {'X-Auth-Token': keystone_utils.admin_token, - 'X-Image-Meta-Owner': ''} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], False) - self.assertEqual(data['image']['owner'], None) - - # Anonymous user still can't list image... - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Nor see it in details... - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # But they should be able to access the metadata... - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD') - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertFalse('x-image-meta-owner' in response) - - # And even the image itself... - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 200) - self.assertEqual(content, "*" * FIVE_KB) - self.assertEqual(response['x-image-meta-name'], "Image1") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertFalse('x-image-meta-owner' in response) - - # Anonymous still shouldn't be able to make the image - # public... - headers = {'X-Image-Meta-Is-Public': 'True'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 403) - - # Nor change ownership... - headers = {'X-Image-Meta-Owner': 'froggy'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 403) - - # Nor even delete it... - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'DELETE') - self.assertEqual(response.status, 403) - - # Now make the image public... - headers = {'X-Auth-Token': keystone_utils.admin_token, - 'X-Image-Meta-Is-Public': 'True'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], True) - self.assertEqual(data['image']['owner'], None) - - # Now the user should see it in the list... - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Especially in the details... - path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port) - http = httplib2.Http() - response, content = http.request(path, 'GET') - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - self.assertEqual(data['images'][0]['is_public'], True) - self.assertEqual(data['images'][0]['owner'], None) - - # But still can't change ownership... - headers = {'X-Image-Meta-Owner': 'froggy'} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'PUT', headers=headers) - self.assertEqual(response.status, 403) - - # Or delete it... - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'DELETE') - self.assertEqual(response.status, 403) - - self.stop_servers() - - -class TestPrivateImagesCli(keystone_utils.KeystoneTests): - """ - Functional tests to verify private images functionality through - bin/glance. - """ - - def setUp(self): - """ - Clear environment to ensure that pre-existing $OS_* variables - do not leak into test. - """ - self._clear_os_env() - super(TestPrivateImagesCli, self).setUp() - - def _do_test_glance_cli(self, cmd): - """ - Test that we can upload an owned image with a given command line; - that we can manipulate its is_public setting; and that appropriate - authorization checks are applied to other (non-admin) users. - """ - self.cleanup() - self.start_servers() - - # Add a non-public image using the given glance command line - exitcode, out, err = execute("echo testdata | %s" % cmd + - " --silent-upload") - - self.assertEqual(0, exitcode) - image_id = out.strip()[25:] - # Verify the attributes of the image - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "MyImage") - self.assertEqual(response['x-image-meta-is_public'], "False") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - image_id = response['x-image-meta-id'] - - # Test that we can update is_public through the CLI - args = (self.api_port, keystone_utils.pattieblack_token, image_id) - cmd = ("bin/glance --port=%d --os_auth_token=%s update %s " - "is_public=True") - exitcode, out, err = execute(cmd % args) - - self.assertEqual(0, exitcode) - self.assertEqual('Updated image %s' % image_id, out.strip()) - - # Verify the appropriate change was made - headers = {'X-Auth-Token': keystone_utils.pattieblack_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "MyImage") - self.assertEqual(response['x-image-meta-is_public'], "True") - self.assertEqual(response['x-image-meta-owner'], - keystone_utils.pattieblack_id) - - # Test that admin can change the owner - args = (self.api_port, keystone_utils.admin_token, image_id) - cmd = "bin/glance --port=%d --os_auth_token=%s update %s owner=froggy" - exitcode, out, err = execute(cmd % args) - - self.assertEqual(0, exitcode) - self.assertEqual('Updated image %s' % image_id, out.strip()) - - # Verify the appropriate change was made - headers = {'X-Auth-Token': keystone_utils.admin_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "MyImage") - self.assertEqual(response['x-image-meta-is_public'], "True") - self.assertEqual(response['x-image-meta-owner'], "froggy") - - # Test that admin can remove the owner - args = (self.api_port, keystone_utils.admin_token, image_id) - cmd = "bin/glance --port=%d --os_auth_token=%s update %s owner=" - exitcode, out, err = execute(cmd % args) - - self.assertEqual(0, exitcode) - self.assertEqual('Updated image %s' % image_id, out.strip()) - - # Verify the appropriate change was made - headers = {'X-Auth-Token': keystone_utils.admin_token} - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - http = httplib2.Http() - response, content = http.request(path, 'HEAD', headers=headers) - self.assertEqual(response.status, 200) - self.assertEqual(response['x-image-meta-name'], "MyImage") - self.assertEqual(response['x-image-meta-is_public'], "True") - self.assertFalse('x-image-meta-owner' in response) - - self.stop_servers() - - def _clear_os_env(self): - os.environ.pop('OS_AUTH_URL', None) - os.environ.pop('OS_AUTH_STRATEGY', None) - os.environ.pop('OS_AUTH_USER', None) - os.environ.pop('OS_AUTH_KEY', None) - os.environ.pop('OS_REGION_NAME', None) - - @skip_if_disabled - def test_glance_cli_noauth_strategy(self): - """ - Test the CLI with the noauth strategy defaulted to. - """ - suffix = '--os_auth_token=%s' % keystone_utils.pattieblack_token - cmd = minimal_add_command(self.api_port, 'MyImage', suffix, False) - self._do_test_glance_cli(cmd) - - @skip_if_disabled - def test_glance_cli_keystone_strategy_switches(self): - """ - Test the CLI with the keystone (v1) strategy enabled via - command line switches. - """ - substitutions = (self.auth_port, 'keystone', - 'pattieblack', 'secrete') - suffix = ("--os_auth_url=http://localhost:%d/v1.0 " - "--os_auth_strategy=%s --os_username=%s --os_password=%s " - % substitutions) - cmd = minimal_add_command(self.api_port, 'MyImage', suffix, False) - self._do_test_glance_cli(cmd) - - @skip_if_disabled - def test_glance_cli_keystone_strategy_environment(self): - """ - Test the CLI with the keystone strategy enabled via - environment variables. - """ - os.environ['OS_AUTH_URL'] = 'http://localhost:%d/v1.0' % self.auth_port - os.environ['OS_AUTH_STRATEGY'] = 'keystone' - os.environ['OS_AUTH_USER'] = 'pattieblack' - os.environ['OS_AUTH_KEY'] = 'secrete' - os.environ['OS_REGION_NAME'] = 'RegionOne' - cmd = minimal_add_command(self.api_port, 'MyImage', public=False) - self._do_test_glance_cli(cmd) - - @skip_if_disabled - def test_glance_cli_keystone_strategy_without_auth_url(self): - """ - Test the CLI with the keystone strategy enabled but - auth url missing. - """ - substitutions = (self.api_port, 'keystone', 'pattieblack', 'secrete') - cmd = ("bin/glance --port=%d --os_auth_strategy=%s " - "--os_username=%s --os_password=%s index" % substitutions) - - exitcode, out, err = execute(cmd, raise_error=False) - - self.assertEqual(1, exitcode) - - msg = ("--os_auth_url option or OS_AUTH_URL environment variable " - "required when keystone authentication strategy is enabled") - self.assertTrue(msg in err, 'expected "%s" in "%s"' % (msg, err)) diff --git a/glance/tests/functional/test_shared_images.py b/glance/tests/functional/test_shared_images.py deleted file mode 100644 index 1a46ea569d..0000000000 --- a/glance/tests/functional/test_shared_images.py +++ /dev/null @@ -1,549 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack, LLC -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Functional test case that verifies private images functionality""" - -import httplib2 -import json - -from glance.tests import functional -from glance.tests.functional import keystone_utils -from glance.tests.utils import execute, skip_if_disabled, minimal_headers - -FIVE_KB = 5 * 1024 -FIVE_GB = 5 * 1024 * 1024 * 1024 - - -class TestSharedImagesApi(keystone_utils.KeystoneTests): - def _push_image(self): - # First, we need to push an image up - image_data = "*" * FIVE_KB - headers = minimal_headers('Image1', public=False) - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'POST', - keystone_utils.pattieblack_token, - headers=headers, - body=image_data) - self.assertEqual(response.status, 201) - data = json.loads(content) - self.assertEqual(data['image']['size'], FIVE_KB) - self.assertEqual(data['image']['name'], "Image1") - self.assertEqual(data['image']['is_public'], False) - self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) - return content - - def _request(self, path, method, auth_token, headers=None, body=None): - http = httplib2.Http() - headers = headers or {} - headers['X-Auth-Token'] = auth_token - if body: - return http.request(path, method, headers=headers, - body=body) - else: - return http.request(path, method, headers=headers) - - @skip_if_disabled - def test_share_image(self): - self.cleanup() - self.start_servers() - - # First, we need to push an image up - data = json.loads(self._push_image()) - image_id = data['image']['id'] - - # Now add froggy as a shared image member - args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) - path = "http://%s:%d/v1/images/%s/members/%s" % args - - response, _ = self._request(path, 'PUT', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 204) - - # Ensure pattieblack can still see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Ensure froggy can see the image now - response, content = self._request(path, 'GET', - keystone_utils.froggy_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # ensure that no one else can see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.bacon_token) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - self.stop_servers() - - @skip_if_disabled - def test_share_and_replace_members(self): - self.cleanup() - self.start_servers() - # First, we need to push an image up - data = json.loads(self._push_image()) - image_id = data['image']['id'] - - # Now add froggy as a shared image member - args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) - path = "http://%s:%d/v1/images/%s/members/%s" % args - - response, _ = self._request(path, 'PUT', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 204) - - # Ensure pattieblack can still see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Ensure froggy can see the image now - response, content = self._request(path, 'GET', - keystone_utils.froggy_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # ensure that no one else can see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.bacon_token) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Build path for the next couple of checks - path = ("http://%s:%d/v1/images/%s/members" % - ("0.0.0.0", self.api_port, image_id)) - - # Make sure update with invalid data gets rejected with 400 - body = { - 'test': [ - { - 'member_id': keystone_utils.bacon_id, - 'can_share': False, - }, - ], - } - response, _ = self._request(path, 'PUT', - keystone_utils.pattieblack_token, - body=json.dumps(body)) - self.assertEqual(response.status, 400) - - # Replace froggy with bacon - body = { - 'memberships': [ - { - 'member_id': keystone_utils.bacon_id, - 'can_share': False, - }, - ], - } - - response, content = self._request(path, 'PUT', - keystone_utils.pattieblack_token, - body=json.dumps(body)) - self.assertEqual(response.status, 204) - - # Ensure bacon can see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.bacon_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # ensure that no one else can see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.froggy_token) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - self.stop_servers() - - @skip_if_disabled - def test_unshare_image(self): - self.cleanup() - self.start_servers() - # First, we need to push an image up - data = json.loads(self._push_image()) - image_id = data['image']['id'] - - # Now add froggy as a shared image member - args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) - path = "http://%s:%d/v1/images/%s/members/%s" % args - - response, _ = self._request(path, 'PUT', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 204) - - # Ensure pattieblack can still see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Ensure froggy can see the image now - response, content = self._request(path, 'GET', - keystone_utils.froggy_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # ensure that no one else can see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.bacon_token) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Now remove froggy as a shared image member - args = ("0.0.0.0", self.api_port, image_id, - keystone_utils.froggy_id) - path = "http://%s:%d/v1/images/%s/members/%s" % args - - response, content = self._request(path, 'DELETE', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 204) - - # ensure that no one else can see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.froggy_token) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # ensure that no one else can access the image - path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", self.api_port, - image_id) - response, content = self._request(path, 'GET', - keystone_utils.froggy_token) - self.assertEqual(response.status, 404) - - # Ensure pattieblack can still see the image - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - self.stop_servers() - - @skip_if_disabled - def test_share_and_can_share_image(self): - self.cleanup() - self.start_servers() - # First, we need to push an image up - data = json.loads(self._push_image()) - image_id = data['image']['id'] - - # Now add froggy as a shared image member - body = json.dumps({'member': {'can_share': True}}) - args = ("0.0.0.0", self.api_port, image_id, keystone_utils.froggy_id) - path = "http://%s:%d/v1/images/%s/members/%s" % args - - response, content = self._request(path, 'PUT', - keystone_utils.pattieblack_token, - body=body) - self.assertEqual(response.status, 204) - - # Ensure froggy can see the image now - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.froggy_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Froggy is going to share with bacon - args = ("0.0.0.0", self.api_port, image_id, keystone_utils.bacon_id) - path = "http://%s:%d/v1/images/%s/members/%s" % args - - response, _ = self._request(path, 'PUT', - keystone_utils.froggy_token) - self.assertEqual(response.status, 204) - - # Ensure bacon can see the image now - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'GET', - keystone_utils.bacon_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['images']), 1) - self.assertEqual(data['images'][0]['id'], image_id) - self.assertEqual(data['images'][0]['size'], FIVE_KB) - self.assertEqual(data['images'][0]['name'], "Image1") - - # Ensure prosciutto cannot see the image - response, content = self._request(path, 'GET', - keystone_utils.prosciutto_token) - self.assertEqual(response.status, 200) - self.assertEqual(content, '{"images": []}') - - # Redundant, but prove prosciutto cannot share it - path = "http://%s:%d/v1/images/%s/members/%s" % \ - ("0.0.0.0", self.api_port, image_id, 'franknbeans') - response, _ = self._request(path, 'PUT', - keystone_utils.prosciutto_token) - self.assertEqual(response.status, 404) - - self.stop_servers() - - @skip_if_disabled - def test_get_members(self): - self.cleanup() - self.start_servers() - # First, we need to push an image up - data = json.loads(self._push_image()) - - # Now add froggy as a shared image member - args = ("0.0.0.0", self.api_port, data['image']['id'], - keystone_utils.froggy_id) - path = "http://%s:%d/v1/images/%s/members/%s" % args - - response, content = self._request(path, 'PUT', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 204) - - path = "http://%s:%d/v1/images/%s/members" % \ - ("0.0.0.0", self.api_port, data['image']['id']) - - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - body = json.loads(content) - self.assertEqual(body['members'][0]['can_share'], False) - self.assertEqual(body['members'][0]['member_id'], - keystone_utils.froggy_id) - - self.stop_servers() - - -class TestSharedImagesCli(keystone_utils.KeystoneTests): - def _push_image(self, name=1): - # First, we need to push an image up - image_data = "*" * FIVE_KB - headers = minimal_headers(str(name), public=False) - path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port) - response, content = self._request(path, 'POST', - keystone_utils.pattieblack_token, - headers=headers, - body=image_data) - self.assertEqual(response.status, 201) - data = json.loads(content) - self.assertEqual(data['image']['size'], FIVE_KB) - self.assertEqual(data['image']['name'], str(name)) - self.assertEqual(data['image']['is_public'], False) - self.assertEqual(data['image']['owner'], keystone_utils.pattieblack_id) - return content - - def _request(self, path, method, auth_token, headers=None, body=None): - http = httplib2.Http() - headers = headers or {} - headers['X-Auth-Token'] = auth_token - if body: - return http.request(path, method, headers=headers, - body=body) - else: - return http.request(path, method, headers=headers) - - def _outsplit(self, out): - return [l.strip() for l in out.strip().split('\n')] - - @skip_if_disabled - def test_share_image(self): - self.cleanup() - self.start_servers() - - # Push an image up - data = json.loads(self._push_image()) - - image_id = data['image']['id'] - - # Test that we can add froggy as a shared image member - args = (self.api_port, keystone_utils.pattieblack_token, - image_id, keystone_utils.froggy_id) - cmd = "bin/glance --port=%d --os_auth_token=%s member-add %s %s" % args - exitcode, out, err = execute(cmd) - - self.assertEqual(0, exitcode) - - # Verify the membership of the image - path = ("http://%s:%d/v1/images/%s/members" % - ("0.0.0.0", self.api_port, image_id)) - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['members']), 1) - self.assertEqual(data['members'][0]['member_id'], - keystone_utils.froggy_id) - self.assertEqual(data['members'][0]['can_share'], False) - - # Test that we can replace a shared image membership list - cmd = ("bin/glance --port=%d --os_auth_token=%s members-replace %s %s " - "--can-share" % - (self.api_port, keystone_utils.pattieblack_token, - image_id, keystone_utils.bacon_id)) - exitcode, out, err = execute(cmd) - - self.assertEqual(0, exitcode) - - # Verify the membership of the image - path = ("http://%s:%d/v1/images/%s/members" % - ("0.0.0.0", self.api_port, image_id)) - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['members']), 1) - self.assertEqual(data['members'][0]['member_id'], - keystone_utils.bacon_id) - self.assertEqual(data['members'][0]['can_share'], True) - - # Test that we can delete an image membership - cmd = ("bin/glance --port=%d --os_auth_token=%s member-delete %s %s" % - (self.api_port, keystone_utils.pattieblack_token, - image_id, keystone_utils.bacon_id)) - exitcode, out, err = execute(cmd) - - self.assertEqual(0, exitcode) - - # Verify the membership of the image - path = ("http://%s:%d/v1/images/%s/members" % - ("0.0.0.0", self.api_port, image_id)) - response, content = self._request(path, 'GET', - keystone_utils.pattieblack_token) - self.assertEqual(response.status, 200) - data = json.loads(content) - self.assertEqual(len(data['members']), 0) - - self.stop_servers() - - @skip_if_disabled - def test_list_shares(self): - self.cleanup() - self.start_servers() - - # Push an image up - data = json.loads(self._push_image(1)) - - image1_id = data['image']['id'] - - # Push a second image up - data = json.loads(self._push_image(2)) - - image2_id = data['image']['id'] - - # Share images with froggy and bacon - cmd = ("bin/glance --port=%d --os_auth_token=%s member-add %s %s" % - (self.api_port, keystone_utils.pattieblack_token, - image1_id, keystone_utils.froggy_id)) - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - cmd = ("bin/glance --port=%d --os_auth_token=%s member-add %s %s" % - (self.api_port, keystone_utils.pattieblack_token, - image1_id, keystone_utils.bacon_id)) - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - cmd = ("bin/glance --port=%d --os_auth_token=%s member-add %s %s " - "--can-share" % - (self.api_port, keystone_utils.pattieblack_token, - image2_id, keystone_utils.froggy_id)) - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - cmd = ("bin/glance --port=%d --os_auth_token=%s member-add %s %s " - "--can-share" % - (self.api_port, keystone_utils.pattieblack_token, - image2_id, keystone_utils.bacon_id)) - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - - # Get the list of image members - cmd = ("bin/glance --port=%d --os_auth_token=%s image-members %s" % - (self.api_port, keystone_utils.pattieblack_token, image1_id)) - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - - result = self._outsplit(out) - self.assertTrue(keystone_utils.froggy_id in result) - self.assertTrue(keystone_utils.bacon_id in result) - - # Try again for can_share - cmd = ("bin/glance --port=%d --os_auth_token=%s image-members %s" % - (self.api_port, keystone_utils.pattieblack_token, image2_id)) - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - - result = self._outsplit(out) - self.assertEqual(result[-1], '(*: Can share image)') - self.assertTrue(keystone_utils.froggy_id + ' *' in result[:-2]) - self.assertTrue(keystone_utils.bacon_id + ' *' in result[:-2]) - - # Get the list of member images - cmd = ("bin/glance --port=%d --os_auth_token=%s member-images %s" % - (self.api_port, keystone_utils.pattieblack_token, - keystone_utils.froggy_id)) - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - - result = self._outsplit(out) - self.assertEqual(result[-1], '(*: Can share image)') - self.assertTrue(('%s' % image1_id) in result) - self.assertTrue(('%s *' % image2_id) in result) - - self.stop_servers() diff --git a/tools/pip-requires b/tools/pip-requires index d524334f00..07432ad82d 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -27,7 +27,7 @@ iso8601>=0.1.4 # commit hash so we can code to a constant version of keystone. # Trying to hit a moving target causes our local unittests to fail # when they should remain unaffected. --e git://github.com/openstack/keystone.git@b9dde8d0e317203e349b8e4bca5bc9923c11974e#egg=keystone +-e git://github.com/openstack/keystone.git@ab6be05068068b0902db44b1d60f56eea4fe1215#egg=keystone # Note you will need gcc buildtools installed and must # have installed libxml headers for lxml to be successfully