**** CubicPower OpenStack Study ****
# Copyright 2012 OpenStack Foundation
# 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 os
import signal
import tempfile
import uuid
import requests
from glance.openstack.common import jsonutils
from glance.tests import functional
from glance.tests.functional.store import test_http
TENANT1 = str(uuid.uuid4())
TENANT2 = str(uuid.uuid4())
TENANT3 = str(uuid.uuid4())
TENANT4 = str(uuid.uuid4())
**** CubicPower OpenStack Study ****
class TestImages(functional.FunctionalTest):
**** CubicPower OpenStack Study ****
def setUp(self):
super(TestImages, self).setUp()
self.cleanup()
self.api_server.deployment_flavor = 'noauth'
self.start_servers(**self.__dict__.copy())
**** CubicPower OpenStack Study ****
def _url(self, path):
return 'http://127.0.0.1:%d%s' % (self.api_port, path)
**** CubicPower OpenStack Study ****
def _headers(self, custom_headers=None):
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': TENANT1,
'X-Roles': 'member',
}
base_headers.update(custom_headers or {})
return base_headers
**** CubicPower OpenStack Study ****
def test_image_lifecycle(self):
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Create an image (with two deployer-defined properties)
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'type': 'kernel',
'foo': 'bar', 'disk_format': 'aki',
'container_format': 'aki', 'abc': 'xyz'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image_location_header = response.headers['Location']
# Returned image entity should have a generated id and status
image = jsonutils.loads(response.text)
image_id = image['id']
checked_keys = set([
u'status',
u'name',
u'tags',
u'created_at',
u'updated_at',
u'visibility',
u'self',
u'protected',
u'id',
u'file',
u'min_disk',
u'foo',
u'abc',
u'type',
u'min_ram',
u'schema',
u'disk_format',
u'container_format',
u'owner',
])
self.assertEqual(set(image.keys()), checked_keys)
expected_image = {
'status': 'queued',
'name': 'image-1',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image_id,
'protected': False,
'file': '/v2/images/%s/file' % image_id,
'min_disk': 0,
'foo': 'bar',
'abc': 'xyz',
'type': 'kernel',
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
# Image list should now have one entry
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['id'], image_id)
# Create another image (with two deployer-defined properties)
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-2', 'type': 'kernel',
'bar': 'foo', 'disk_format': 'aki',
'container_format': 'aki', 'xyz': 'abc'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Returned image entity should have a generated id and status
image = jsonutils.loads(response.text)
image2_id = image['id']
checked_keys = set([
u'status',
u'name',
u'tags',
u'created_at',
u'updated_at',
u'visibility',
u'self',
u'protected',
u'id',
u'file',
u'min_disk',
u'bar',
u'xyz',
u'type',
u'min_ram',
u'schema',
u'disk_format',
u'container_format',
u'owner',
])
self.assertEqual(set(image.keys()), checked_keys)
expected_image = {
'status': 'queued',
'name': 'image-2',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image2_id,
'protected': False,
'file': '/v2/images/%s/file' % image2_id,
'min_disk': 0,
'bar': 'foo',
'xyz': 'abc',
'type': 'kernel',
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
# Image list should now have two entries
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(2, len(images))
self.assertEqual(images[0]['id'], image2_id)
self.assertEqual(images[1]['id'], image_id)
# Image list should list only image-2 as image-1 doesn't contain the
# property 'bar'
path = self._url('/v2/images?bar=foo')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['id'], image2_id)
# Image list should list only image-1 as image-2 doesn't contain the
# property 'foo'
path = self._url('/v2/images?foo=bar')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['id'], image_id)
# Image list should list only image-1 based on the filter
# 'foo=bar&abc=xyz'
path = self._url('/v2/images?foo=bar&abc=xyz')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['id'], image_id)
# Image list should list only image-2 based on the filter
# 'bar=foo&xyz=abc'
path = self._url('/v2/images?bar=foo&xyz=abc')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['id'], image2_id)
# Image list should not list anything as the filter 'foo=baz&abc=xyz'
# is not satisfied by either images
path = self._url('/v2/images?foo=baz&abc=xyz')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Get the image using the returned Location header
response = requests.get(image_location_header, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual(image_id, image['id'])
self.assertFalse('checksum' in image)
self.assertNotIn('size', image)
self.assertNotIn('virtual_size', image)
self.assertEqual('bar', image['foo'])
self.assertEqual(False, image['protected'])
self.assertEqual('kernel', image['type'])
self.assertTrue(image['created_at'])
self.assertTrue(image['updated_at'])
self.assertEqual(image['updated_at'], image['created_at'])
# The image should be mutable, including adding and removing properties
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
data = jsonutils.dumps([
{'op': 'replace', 'path': '/name', 'value': 'image-2'},
{'op': 'replace', 'path': '/disk_format', 'value': 'vhd'},
{'op': 'replace', 'path': '/container_format', 'value': 'ami'},
{'op': 'replace', 'path': '/foo', 'value': 'baz'},
{'op': 'add', 'path': '/ping', 'value': 'pong'},
{'op': 'replace', 'path': '/protected', 'value': True},
{'op': 'remove', 'path': '/type'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# Returned image entity should reflect the changes
image = jsonutils.loads(response.text)
self.assertEqual('image-2', image['name'])
self.assertEqual('vhd', image['disk_format'])
self.assertEqual('baz', image['foo'])
self.assertEqual('pong', image['ping'])
self.assertTrue(image['protected'])
self.assertFalse('type' in image, response.text)
# Adding 11 image properties should fail since configured limit is 10
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
changes = []
for i in range(11):
changes.append({'op': 'add',
'path': '/ping%i' % i,
'value': 'pong'})
data = jsonutils.dumps(changes)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(413, response.status_code, response.text)
# Adding 3 image locations should fail since configured limit is 2
for i in range(3):
file_path = os.path.join(self.test_dir, 'fake_image_%i' % i)
with open(file_path, 'w') as fap:
fap.write('glance')
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
changes = []
for i in range(3):
changes.append({'op': 'add', 'path': '/locations/-',
'value': {'url': 'file://{0}'.format(
os.path.join(self.test_dir,
'fake_image_%i' % i)),
'metadata': {}},
})
data = jsonutils.dumps(changes)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(413, response.status_code, response.text)
# Ensure the v2.0 json-patch content type is accepted
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.0-json-patch'
headers = self._headers({'content-type': media_type})
data = jsonutils.dumps([{'add': '/ding', 'value': 'dong'}])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# Returned image entity should reflect the changes
image = jsonutils.loads(response.text)
self.assertEqual('dong', image['ding'])
# Updates should persist across requests
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual(image_id, image['id'])
self.assertEqual('image-2', image['name'])
self.assertEqual('baz', image['foo'])
self.assertEqual('pong', image['ping'])
self.assertTrue(image['protected'])
self.assertFalse('type' in image, response.text)
# Try to download data before its uploaded
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers()
response = requests.get(path, headers=headers)
self.assertEqual(204, response.status_code)
def _verify_image_checksum_and_status(checksum, status):
# Checksum should be populated and status should be active
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual(checksum, image['checksum'])
self.assertEqual(status, image['status'])
# Upload some image data
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='ZZZZZ')
self.assertEqual(204, response.status_code)
expected_checksum = '8f113e38d28a79a5a451b16048cc2b72'
_verify_image_checksum_and_status(expected_checksum, 'active')
# `disk_format` and `container_format` cannot
# be replaced when the image is active.
immutable_paths = ['/disk_format', '/container_format']
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
path = self._url('/v2/images/%s' % image_id)
for immutable_path in immutable_paths:
data = jsonutils.dumps([
{'op': 'replace', 'path': immutable_path, 'value': 'ari'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
# Try to download the data that was just uploaded
path = self._url('/v2/images/%s/file' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
self.assertEqual(expected_checksum, response.headers['Content-MD5'])
self.assertEqual(response.text, 'ZZZZZ')
# Uploading duplicate data should be rejected with a 409. The
# original data should remain untouched.
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='XXX')
self.assertEqual(409, response.status_code)
_verify_image_checksum_and_status(expected_checksum, 'active')
# Ensure the size is updated to reflect the data uploaded
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
self.assertEqual(5, jsonutils.loads(response.text)['size'])
# Deletion should not work on protected images
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(403, response.status_code)
# Unprotect image for deletion
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
doc = [{'op': 'replace', 'path': '/protected', 'value': False}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# Remove all locations of the image then the image size shouldn't be
# able to access
path = self._url('/v2/images/%s' % image2_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
doc = [{'op': 'replace', 'path': '/locations', 'value': []}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertNotIn('size', image)
self.assertNotIn('virtual_size', image)
self.assertEqual('queued', image['status'])
# Deletion should work. Deleting image-1
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# This image should be no longer be directly accessible
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(404, response.status_code)
# And neither should its data
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers()
response = requests.get(path, headers=headers)
self.assertEqual(404, response.status_code)
# Image list should now contain just image-2
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['id'], image2_id)
# Deleting image-2 should work
path = self._url('/v2/images/%s' % image2_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# Image list should now be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
self.stop_servers()
**** CubicPower OpenStack Study ****
def _verify_image_checksum_and_status(checksum, status):
# Checksum should be populated and status should be active
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual(checksum, image['checksum'])
self.assertEqual(status, image['status'])
# Upload some image data
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='ZZZZZ')
self.assertEqual(204, response.status_code)
expected_checksum = '8f113e38d28a79a5a451b16048cc2b72'
_verify_image_checksum_and_status(expected_checksum, 'active')
# `disk_format` and `container_format` cannot
# be replaced when the image is active.
immutable_paths = ['/disk_format', '/container_format']
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
path = self._url('/v2/images/%s' % image_id)
for immutable_path in immutable_paths:
data = jsonutils.dumps([
{'op': 'replace', 'path': immutable_path, 'value': 'ari'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
# Try to download the data that was just uploaded
path = self._url('/v2/images/%s/file' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
self.assertEqual(expected_checksum, response.headers['Content-MD5'])
self.assertEqual(response.text, 'ZZZZZ')
# Uploading duplicate data should be rejected with a 409. The
# original data should remain untouched.
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='XXX')
self.assertEqual(409, response.status_code)
_verify_image_checksum_and_status(expected_checksum, 'active')
# Ensure the size is updated to reflect the data uploaded
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
self.assertEqual(5, jsonutils.loads(response.text)['size'])
# Deletion should not work on protected images
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(403, response.status_code)
# Unprotect image for deletion
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
doc = [{'op': 'replace', 'path': '/protected', 'value': False}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# Remove all locations of the image then the image size shouldn't be
# able to access
path = self._url('/v2/images/%s' % image2_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
doc = [{'op': 'replace', 'path': '/locations', 'value': []}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertNotIn('size', image)
self.assertNotIn('virtual_size', image)
self.assertEqual('queued', image['status'])
# Deletion should work. Deleting image-1
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# This image should be no longer be directly accessible
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(404, response.status_code)
# And neither should its data
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers()
response = requests.get(path, headers=headers)
self.assertEqual(404, response.status_code)
# Image list should now contain just image-2
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['id'], image2_id)
# Deleting image-2 should work
path = self._url('/v2/images/%s' % image2_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# Image list should now be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_permissions(self):
# Create an image that belongs to TENANT1
path = self._url('/v2/images')
headers = self._headers({'Content-Type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'raw',
'container_format': 'bare'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image_id = jsonutils.loads(response.text)['id']
# Upload some image data
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='ZZZZZ')
self.assertEqual(204, response.status_code)
# TENANT1 should see the image in their list
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(image_id, images[0]['id'])
# TENANT1 should be able to access the image directly
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
# TENANT2 should not see the image in their list
path = self._url('/v2/images')
headers = self._headers({'X-Tenant-Id': TENANT2})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# TENANT2 should not be able to access the image directly
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'X-Tenant-Id': TENANT2})
response = requests.get(path, headers=headers)
self.assertEqual(404, response.status_code)
# TENANT2 should not be able to modify the image, either
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({
'Content-Type': 'application/openstack-images-v2.1-json-patch',
'X-Tenant-Id': TENANT2,
})
doc = [{'op': 'replace', 'path': '/name', 'value': 'image-2'}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(404, response.status_code)
# TENANT2 should not be able to delete the image, either
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'X-Tenant-Id': TENANT2})
response = requests.delete(path, headers=headers)
self.assertEqual(404, response.status_code)
# Publicize the image as an admin of TENANT1
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({
'Content-Type': 'application/openstack-images-v2.1-json-patch',
'X-Roles': 'admin',
})
doc = [{'op': 'replace', 'path': '/visibility', 'value': 'public'}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code)
# TENANT3 should now see the image in their list
path = self._url('/v2/images')
headers = self._headers({'X-Tenant-Id': TENANT3})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(image_id, images[0]['id'])
# TENANT3 should also be able to access the image directly
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'X-Tenant-Id': TENANT3})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
# TENANT3 still should not be able to modify the image
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({
'Content-Type': 'application/openstack-images-v2.1-json-patch',
'X-Tenant-Id': TENANT3,
})
doc = [{'op': 'replace', 'path': '/name', 'value': 'image-2'}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
# TENANT3 should not be able to delete the image, either
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'X-Tenant-Id': TENANT3})
response = requests.delete(path, headers=headers)
self.assertEqual(403, response.status_code)
# Image data should still be present after the failed delete
path = self._url('/v2/images/%s/file' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
self.assertEqual(response.text, 'ZZZZZ')
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_property_protections_with_roles(self):
# Enable property protection
self.api_server.property_protection_file = self.property_file_roles
self.start_servers(**self.__dict__.copy())
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
## Create an image for role member with extra props
# Raises 403 since user is not allowed to set 'foo'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'member'})
data = jsonutils.dumps({'name': 'image-1', 'foo': 'bar',
'disk_format': 'aki',
'container_format': 'aki',
'x_owner_foo': 'o_s_bar'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
## Create an image for role member without 'foo'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'member'})
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
'container_format': 'aki',
'x_owner_foo': 'o_s_bar'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Returned image entity should have 'x_owner_foo'
image = jsonutils.loads(response.text)
image_id = image['id']
expected_image = {
'status': 'queued',
'name': 'image-1',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image_id,
'protected': False,
'file': '/v2/images/%s/file' % image_id,
'min_disk': 0,
'x_owner_foo': 'o_s_bar',
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
# Create an image for role spl_role with extra props
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'spl_role'})
data = jsonutils.dumps({'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'spl_create_prop': 'create_bar',
'spl_create_prop_policy': 'create_policy_bar',
'spl_read_prop': 'read_bar',
'spl_update_prop': 'update_bar',
'spl_delete_prop': 'delete_bar'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
# Attempt to replace, add and remove properties which are forbidden
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'spl_role'})
data = jsonutils.dumps([
{'op': 'replace', 'path': '/spl_read_prop', 'value': 'r'},
{'op': 'replace', 'path': '/spl_update_prop', 'value': 'u'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code, response.text)
# Attempt to replace, add and remove properties which are forbidden
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'spl_role'})
data = jsonutils.dumps([
{'op': 'add', 'path': '/spl_new_prop', 'value': 'new'},
{'op': 'remove', 'path': '/spl_create_prop'},
{'op': 'remove', 'path': '/spl_delete_prop'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code, response.text)
# Attempt to replace, add and remove properties
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'spl_role'})
data = jsonutils.dumps([
{'op': 'replace', 'path': '/spl_update_prop', 'value': 'u'},
{'op': 'remove', 'path': '/spl_delete_prop'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# Returned image entity should reflect the changes
image = jsonutils.loads(response.text)
# 'spl_update_prop' has update permission for spl_role
# hence the value has changed
self.assertEqual('u', image['spl_update_prop'])
# 'spl_delete_prop' has delete permission for spl_role
# hence the property has been deleted
self.assertTrue('spl_delete_prop' not in image.keys())
# Image Deletion should work
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# This image should be no longer be directly accessible
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(404, response.status_code)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_property_protections_with_policies(self):
# Enable property protection
self.api_server.property_protection_file = self.property_file_policies
self.api_server.property_protection_rule_format = 'policies'
self.start_servers(**self.__dict__.copy())
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
## Create an image for role member with extra props
# Raises 403 since user is not allowed to set 'foo'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'member'})
data = jsonutils.dumps({'name': 'image-1', 'foo': 'bar',
'disk_format': 'aki',
'container_format': 'aki',
'x_owner_foo': 'o_s_bar'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
## Create an image for role member without 'foo'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'member'})
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
'container_format': 'aki'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Returned image entity
image = jsonutils.loads(response.text)
image_id = image['id']
expected_image = {
'status': 'queued',
'name': 'image-1',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image_id,
'protected': False,
'file': '/v2/images/%s/file' % image_id,
'min_disk': 0,
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
# Create an image for role spl_role with extra props
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'spl_role, admin'})
data = jsonutils.dumps({'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'spl_creator_policy': 'creator_bar',
'spl_default_policy': 'default_bar'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
self.assertEqual('creator_bar', image['spl_creator_policy'])
self.assertEqual('default_bar', image['spl_default_policy'])
# Attempt to replace a property which is permitted
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'replace', 'path': '/spl_creator_policy', 'value': 'r'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# Returned image entity should reflect the changes
image = jsonutils.loads(response.text)
# 'spl_creator_policy' has update permission for admin
# hence the value has changed
self.assertEqual('r', image['spl_creator_policy'])
# Attempt to replace a property which is forbidden
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'spl_role'})
data = jsonutils.dumps([
{'op': 'replace', 'path': '/spl_creator_policy', 'value': 'z'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code, response.text)
# Attempt to read properties
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'content-type': media_type,
'X-Roles': 'random_role'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
# 'random_role' is allowed read 'spl_default_policy'.
self.assertEqual(image['spl_default_policy'], 'default_bar')
# 'random_role' is forbidden to read 'spl_creator_policy'.
self.assertFalse('spl_creator_policy' in image)
# Attempt to add and remove properties which are permitted
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/spl_creator_policy'},
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# Returned image entity should reflect the changes
image = jsonutils.loads(response.text)
# 'spl_creator_policy' has delete permission for admin
# hence the value has been deleted
self.assertFalse('spl_creator_policy' in image)
# Attempt to read a property that is permitted
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'content-type': media_type,
'X-Roles': 'random_role'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
# Returned image entity should reflect the changes
image = jsonutils.loads(response.text)
self.assertEqual(image['spl_default_policy'], 'default_bar')
# Image Deletion should work
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# This image should be no longer be directly accessible
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(404, response.status_code)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_property_protections_special_chars_roles(self):
# Enable property protection
self.api_server.property_protection_file = self.property_file_roles
self.start_servers(**self.__dict__.copy())
# Verify both admin and unknown role can create properties marked with
# '@'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_all_permitted_admin': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
expected_image = {
'status': 'queued',
'name': 'image-1',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image_id,
'protected': False,
'file': '/v2/images/%s/file' % image_id,
'min_disk': 0,
'x_all_permitted_admin': '1',
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_all_permitted_joe_soap': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
expected_image = {
'status': 'queued',
'name': 'image-1',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image_id,
'protected': False,
'file': '/v2/images/%s/file' % image_id,
'min_disk': 0,
'x_all_permitted_joe_soap': '1',
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
# Verify both admin and unknown role can read properties marked with
# '@'
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual('1', image['x_all_permitted_joe_soap'])
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual('1', image['x_all_permitted_joe_soap'])
# Verify both admin and unknown role can update properties marked with
# '@'
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_all_permitted_joe_soap', 'value': '2'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertEqual('2', image['x_all_permitted_joe_soap'])
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_all_permitted_joe_soap', 'value': '3'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertEqual('3', image['x_all_permitted_joe_soap'])
# Verify both admin and unknown role can delete properties marked with
# '@'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_all_permitted_a': '1',
'x_all_permitted_b': '2'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_all_permitted_a'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertNotIn('x_all_permitted_a', image.keys())
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_all_permitted_b'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertNotIn('x_all_permitted_b', image.keys())
# Verify neither admin nor unknown role can create a property protected
# with '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_permitted_admin': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_permitted_joe_soap': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
# Verify neither admin nor unknown role can read properties marked with
# '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_read': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
self.assertNotIn('x_none_read', image.keys())
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertNotIn('x_none_read', image.keys())
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertNotIn('x_none_read', image.keys())
# Verify neither admin nor unknown role can update properties marked
# with '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_update': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
self.assertEqual('1', image['x_none_update'])
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_none_update', 'value': '2'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code, response.text)
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_none_update', 'value': '3'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(409, response.status_code, response.text)
# Verify neither admin nor unknown role can delete properties marked
# with '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_delete': '1',
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_none_delete'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code, response.text)
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_none_delete'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(409, response.status_code, response.text)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_property_protections_special_chars_policies(self):
# Enable property protection
self.api_server.property_protection_file = self.property_file_policies
self.api_server.property_protection_rule_format = 'policies'
self.start_servers(**self.__dict__.copy())
# Verify both admin and unknown role can create properties marked with
# '@'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_all_permitted_admin': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
expected_image = {
'status': 'queued',
'name': 'image-1',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image_id,
'protected': False,
'file': '/v2/images/%s/file' % image_id,
'min_disk': 0,
'x_all_permitted_admin': '1',
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_all_permitted_joe_soap': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
expected_image = {
'status': 'queued',
'name': 'image-1',
'tags': [],
'visibility': 'private',
'self': '/v2/images/%s' % image_id,
'protected': False,
'file': '/v2/images/%s/file' % image_id,
'min_disk': 0,
'x_all_permitted_joe_soap': '1',
'min_ram': 0,
'schema': '/v2/schemas/image',
}
for key, value in expected_image.items():
self.assertEqual(image[key], value, key)
# Verify both admin and unknown role can read properties marked with
# '@'
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual('1', image['x_all_permitted_joe_soap'])
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual('1', image['x_all_permitted_joe_soap'])
# Verify both admin and unknown role can update properties marked with
# '@'
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_all_permitted_joe_soap', 'value': '2'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertEqual('2', image['x_all_permitted_joe_soap'])
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_all_permitted_joe_soap', 'value': '3'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertEqual('3', image['x_all_permitted_joe_soap'])
# Verify both admin and unknown role can delete properties marked with
# '@'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_all_permitted_a': '1',
'x_all_permitted_b': '2'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_all_permitted_a'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertNotIn('x_all_permitted_a', image.keys())
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_all_permitted_b'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
image = jsonutils.loads(response.text)
self.assertNotIn('x_all_permitted_b', image.keys())
# Verify neither admin nor unknown role can create a property protected
# with '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_permitted_admin': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_permitted_joe_soap': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(403, response.status_code)
# Verify neither admin nor unknown role can read properties marked with
# '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_read': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
self.assertNotIn('x_none_read', image.keys())
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertNotIn('x_none_read', image.keys())
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'joe_soap'})
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertNotIn('x_none_read', image.keys())
# Verify neither admin nor unknown role can update properties marked
# with '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_update': '1'
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
self.assertEqual('1', image['x_none_update'])
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_none_update', 'value': '2'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code, response.text)
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'replace',
'path': '/x_none_update', 'value': '3'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(409, response.status_code, response.text)
# Verify neither admin nor unknown role can delete properties marked
# with '!'
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json',
'X-Roles': 'admin'})
data = jsonutils.dumps({
'name': 'image-1',
'disk_format': 'aki',
'container_format': 'aki',
'x_none_delete': '1',
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'admin'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_none_delete'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(403, response.status_code, response.text)
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type,
'X-Roles': 'joe_soap'})
data = jsonutils.dumps([
{'op': 'remove', 'path': '/x_none_delete'}
])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(409, response.status_code, response.text)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_tag_lifecycle(self):
# Create an image with a tag - duplicate should be ignored
path = self._url('/v2/images')
headers = self._headers({'Content-Type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'tags': ['sniff', 'sniff']})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image_id = jsonutils.loads(response.text)['id']
# Image should show a list with a single tag
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
tags = jsonutils.loads(response.text)['tags']
self.assertEqual(['sniff'], tags)
# Delete all tags
for tag in tags:
path = self._url('/v2/images/%s/tags/%s' % (image_id, tag))
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# Update image with too many tags via PUT
# Configured limit is 10 tags
for i in range(10):
path = self._url('/v2/images/%s/tags/foo%i' % (image_id, i))
response = requests.put(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# 11th tag should fail
path = self._url('/v2/images/%s/tags/fail_me' % image_id)
response = requests.put(path, headers=self._headers())
self.assertEqual(413, response.status_code)
# Make sure the 11th tag was not added
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
tags = jsonutils.loads(response.text)['tags']
self.assertEqual(10, len(tags))
# Update image tags via PATCH
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
doc = [
{
'op': 'replace',
'path': '/tags',
'value': ['foo'],
},
]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code)
# Update image with too many tags via PATCH
# Configured limit is 10 tags
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
tags = ['foo%d' % i for i in range(11)]
doc = [
{
'op': 'replace',
'path': '/tags',
'value': tags,
},
]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(413, response.status_code)
# Tags should not have changed since request was over limit
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
tags = jsonutils.loads(response.text)['tags']
self.assertEqual(['foo'], tags)
# Update image with duplicate tag - it should be ignored
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
doc = [
{
'op': 'replace',
'path': '/tags',
'value': ['sniff', 'snozz', 'snozz'],
},
]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code)
tags = jsonutils.loads(response.text)['tags']
self.assertEqual(['snozz', 'sniff'], tags)
# Image should show the appropriate tags
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
tags = jsonutils.loads(response.text)['tags']
self.assertEqual(['snozz', 'sniff'], tags)
# Attempt to tag the image with a duplicate should be ignored
path = self._url('/v2/images/%s/tags/snozz' % image_id)
response = requests.put(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# Create another more complex tag
path = self._url('/v2/images/%s/tags/gabe%%40example.com' % image_id)
response = requests.put(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# Double-check that the tags container on the image is populated
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
tags = jsonutils.loads(response.text)['tags']
self.assertEqual(['gabe@example.com', 'snozz', 'sniff'], tags)
# Query images by single tag
path = self._url('/v2/images?tag=sniff')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual('image-1', images[0]['name'])
# Query images by multiple tags
path = self._url('/v2/images?tag=sniff&tag=snozz')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual('image-1', images[0]['name'])
# Query images by tag and other attributes
path = self._url('/v2/images?tag=sniff&status=queued')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual('image-1', images[0]['name'])
# Query images by tag and a nonexistent tag
path = self._url('/v2/images?tag=sniff&tag=fake')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# The tag should be deletable
path = self._url('/v2/images/%s/tags/gabe%%40example.com' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# List of tags should reflect the deletion
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
tags = jsonutils.loads(response.text)['tags']
self.assertEqual(['snozz', 'sniff'], tags)
# Deleting the same tag should return a 404
path = self._url('/v2/images/%s/tags/gabe%%40example.com' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(404, response.status_code)
# The tags won't be able to to query the images after deleting
path = self._url('/v2/images?tag=gabe%%40example.com')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_images_container(self):
# Image list should be empty and no next link should be present
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
first = jsonutils.loads(response.text)['first']
self.assertEqual(0, len(images))
self.assertTrue('next' not in jsonutils.loads(response.text))
self.assertEqual('/v2/images', first)
# Create 7 images
images = []
fixtures = [
{'name': 'image-3', 'type': 'kernel', 'ping': 'pong'},
{'name': 'image-4', 'type': 'kernel', 'ping': 'pong'},
{'name': 'image-1', 'type': 'kernel', 'ping': 'pong'},
{'name': 'image-3', 'type': 'ramdisk', 'ping': 'pong'},
{'name': 'image-2', 'type': 'kernel', 'ping': 'ding'},
{'name': 'image-3', 'type': 'kernel', 'ping': 'pong'},
{'name': 'image-2', 'type': 'kernel', 'ping': 'pong'},
]
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
for fixture in fixtures:
data = jsonutils.dumps(fixture)
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
images.append(jsonutils.loads(response.text))
# Image list should contain 7 images
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(7, len(body['images']))
self.assertEqual('/v2/images', body['first'])
self.assertFalse('next' in jsonutils.loads(response.text))
# Begin pagination after the first image
template_url = ('/v2/images?limit=2&sort_dir=asc&sort_key=name'
'&marker=%s&type=kernel&ping=pong')
path = self._url(template_url % images[2]['id'])
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(2, len(body['images']))
response_ids = [image['id'] for image in body['images']]
self.assertEqual([images[6]['id'], images[0]['id']], response_ids)
# Continue pagination using next link from previous request
path = self._url(body['next'])
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(2, len(body['images']))
response_ids = [image['id'] for image in body['images']]
self.assertEqual([images[5]['id'], images[1]['id']], response_ids)
# Continue pagination - expect no results
path = self._url(body['next'])
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(0, len(body['images']))
# Delete first image
path = self._url('/v2/images/%s' % images[0]['id'])
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
# Ensure bad request for using a deleted image as marker
path = self._url('/v2/images?marker=%s' % images[0]['id'])
response = requests.get(path, headers=self._headers())
self.assertEqual(400, response.status_code)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_image_visibility_to_different_users(self):
self.cleanup()
self.api_server.deployment_flavor = 'fakeauth'
self.registry_server.deployment_flavor = 'fakeauth'
self.start_servers(**self.__dict__.copy())
owners = ['admin', 'tenant1', 'tenant2', 'none']
visibilities = ['public', 'private']
for owner in owners:
for visibility in visibilities:
path = self._url('/v2/images')
headers = self._headers({
'content-type': 'application/json',
'X-Auth-Token': 'createuser:%s:admin' % owner,
})
data = jsonutils.dumps({
'name': '%s-%s' % (owner, visibility),
'visibility': visibility,
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
def list_images(tenant, role='', visibility=None):
auth_token = 'user:%s:%s' % (tenant, role)
headers = {'X-Auth-Token': auth_token}
path = self._url('/v2/images')
if visibility is not None:
path += '?visibility=%s' % visibility
response = requests.get(path, headers=headers)
self.assertEqual(response.status_code, 200)
return jsonutils.loads(response.text)['images']
# 1. Known user sees public and their own images
images = list_images('tenant1')
self.assertEqual(len(images), 5)
for image in images:
self.assertTrue(image['visibility'] == 'public'
or 'tenant1' in image['name'])
# 2. Known user, visibility=public, sees all public images
images = list_images('tenant1', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 3. Known user, visibility=private, sees only their private image
images = list_images('tenant1', visibility='private')
self.assertEqual(len(images), 1)
image = images[0]
self.assertEqual(image['visibility'], 'private')
self.assertTrue('tenant1' in image['name'])
# 4. Unknown user sees only public images
images = list_images('none')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 5. Unknown user, visibility=public, sees only public images
images = list_images('none', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 6. Unknown user, visibility=private, sees no images
images = list_images('none', visibility='private')
self.assertEqual(len(images), 0)
# 7. Unknown admin sees all images
images = list_images('none', role='admin')
self.assertEqual(len(images), 8)
# 8. Unknown admin, visibility=public, shows only public images
images = list_images('none', role='admin', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 9. Unknown admin, visibility=private, sees only private images
images = list_images('none', role='admin', visibility='private')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'private')
# 10. Known admin sees all images
images = list_images('admin', role='admin')
self.assertEqual(len(images), 8)
# 11. Known admin, visibility=public, sees all public images
images = list_images('admin', role='admin', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 12. Known admin, visibility=private, sees all private images
images = list_images('admin', role='admin', visibility='private')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'private')
self.stop_servers()
**** CubicPower OpenStack Study ****
def list_images(tenant, role='', visibility=None):
auth_token = 'user:%s:%s' % (tenant, role)
headers = {'X-Auth-Token': auth_token}
path = self._url('/v2/images')
if visibility is not None:
path += '?visibility=%s' % visibility
response = requests.get(path, headers=headers)
self.assertEqual(response.status_code, 200)
return jsonutils.loads(response.text)['images']
# 1. Known user sees public and their own images
images = list_images('tenant1')
self.assertEqual(len(images), 5)
for image in images:
self.assertTrue(image['visibility'] == 'public'
or 'tenant1' in image['name'])
# 2. Known user, visibility=public, sees all public images
images = list_images('tenant1', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 3. Known user, visibility=private, sees only their private image
images = list_images('tenant1', visibility='private')
self.assertEqual(len(images), 1)
image = images[0]
self.assertEqual(image['visibility'], 'private')
self.assertTrue('tenant1' in image['name'])
# 4. Unknown user sees only public images
images = list_images('none')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 5. Unknown user, visibility=public, sees only public images
images = list_images('none', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 6. Unknown user, visibility=private, sees no images
images = list_images('none', visibility='private')
self.assertEqual(len(images), 0)
# 7. Unknown admin sees all images
images = list_images('none', role='admin')
self.assertEqual(len(images), 8)
# 8. Unknown admin, visibility=public, shows only public images
images = list_images('none', role='admin', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 9. Unknown admin, visibility=private, sees only private images
images = list_images('none', role='admin', visibility='private')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'private')
# 10. Known admin sees all images
images = list_images('admin', role='admin')
self.assertEqual(len(images), 8)
# 11. Known admin, visibility=public, sees all public images
images = list_images('admin', role='admin', visibility='public')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'public')
# 12. Known admin, visibility=private, sees all private images
images = list_images('admin', role='admin', visibility='private')
self.assertEqual(len(images), 4)
for image in images:
self.assertEqual(image['visibility'], 'private')
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_update_locations(self):
# Create an image
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
'container_format': 'aki'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Returned image entity should have a generated id and status
image = jsonutils.loads(response.text)
image_id = image['id']
self.assertEqual(image['status'], 'queued')
self.assertNotIn('size', image)
self.assertNotIn('virtual_size', image)
file_path = os.path.join(self.test_dir, 'fake_image')
with open(file_path, 'w') as fap:
fap.write('glance')
# Update locations for the queued image
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
data = jsonutils.dumps([{'op': 'replace', 'path': '/locations',
'value': [{'url': 'file://' + file_path,
'metadata': {}}]}])
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code, response.text)
# The image size should be updated
path = self._url('/v2/images/%s' % image_id)
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertEqual(image['size'], 6)
**** CubicPower OpenStack Study ****
class TestImageDirectURLVisibility(functional.FunctionalTest):
**** CubicPower OpenStack Study ****
def setUp(self):
super(TestImageDirectURLVisibility, self).setUp()
self.cleanup()
self.api_server.deployment_flavor = 'noauth'
**** CubicPower OpenStack Study ****
def _url(self, path):
return 'http://127.0.0.1:%d%s' % (self.api_port, path)
**** CubicPower OpenStack Study ****
def _headers(self, custom_headers=None):
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': TENANT1,
'X-Roles': 'member',
}
base_headers.update(custom_headers or {})
return base_headers
**** CubicPower OpenStack Study ****
def test_v2_not_enabled(self):
self.api_server.enable_v2_api = False
self.start_servers(**self.__dict__.copy())
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(300, response.status_code)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_v2_enabled(self):
self.api_server.enable_v2_api = True
self.start_servers(**self.__dict__.copy())
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_image_direct_url_visible(self):
self.api_server.show_image_direct_url = True
self.start_servers(**self.__dict__.copy())
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Create an image
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'type': 'kernel',
'foo': 'bar', 'disk_format': 'aki',
'container_format': 'aki',
'visibility': 'public'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Get the image id
image = jsonutils.loads(response.text)
image_id = image['id']
# Image direct_url should not be visible before location is set
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertFalse('direct_url' in image)
# Upload some image data, setting the image location
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='ZZZZZ')
self.assertEqual(204, response.status_code)
# Image direct_url should be visible
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertTrue('direct_url' in image)
# Image direct_url should be visible to non-owner, non-admin user
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json',
'X-Tenant-Id': TENANT2})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertIn('direct_url', image)
# Image direct_url should be visible in a list
path = self._url('/v2/images')
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)['images'][0]
self.assertTrue('direct_url' in image)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_image_multiple_location_url_visible(self):
self.api_server.show_multiple_locations = True
self.start_servers(**self.__dict__.copy())
# Create an image
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'type': 'kernel',
'foo': 'bar', 'disk_format': 'aki',
'container_format': 'aki'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Get the image id
image = jsonutils.loads(response.text)
image_id = image['id']
# Image locations should not be visible before location is set
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertTrue('locations' in image)
self.assertTrue(image["locations"] == [])
# Upload some image data, setting the image location
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='ZZZZZ')
self.assertEqual(204, response.status_code)
# Image locations should be visible
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertTrue('locations' in image)
loc = image['locations']
self.assertTrue(len(loc) > 0)
loc = loc[0]
self.assertTrue('url' in loc)
self.assertTrue('metadata' in loc)
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_image_direct_url_not_visible(self):
self.api_server.show_image_direct_url = False
self.start_servers(**self.__dict__.copy())
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Create an image
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'type': 'kernel',
'foo': 'bar', 'disk_format': 'aki',
'container_format': 'aki'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Get the image id
image = jsonutils.loads(response.text)
image_id = image['id']
# Upload some image data, setting the image location
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data='ZZZZZ')
self.assertEqual(204, response.status_code)
# Image direct_url should not be visible
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertFalse('direct_url' in image)
# Image direct_url should not be visible in a list
path = self._url('/v2/images')
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)['images'][0]
self.assertFalse('direct_url' in image)
self.stop_servers()
**** CubicPower OpenStack Study ****
class TestImageLocationSelectionStrategy(functional.FunctionalTest):
**** CubicPower OpenStack Study ****
def setUp(self):
super(TestImageLocationSelectionStrategy, self).setUp()
self.cleanup()
self.api_server.deployment_flavor = 'noauth'
self.foo_image_file = tempfile.NamedTemporaryFile()
self.foo_image_file.write("foo image file")
self.foo_image_file.flush()
self.addCleanup(self.foo_image_file.close)
ret = test_http.http_server("foo_image_id", "foo_image")
self.http_server_pid, self.http_port = ret
**** CubicPower OpenStack Study ****
def tearDown(self):
if self.http_server_pid is not None:
os.kill(self.http_server_pid, signal.SIGKILL)
super(TestImageLocationSelectionStrategy, self).tearDown()
**** CubicPower OpenStack Study ****
def _url(self, path):
return 'http://127.0.0.1:%d%s' % (self.api_port, path)
**** CubicPower OpenStack Study ****
def _headers(self, custom_headers=None):
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': TENANT1,
'X-Roles': 'member',
}
base_headers.update(custom_headers or {})
return base_headers
**** CubicPower OpenStack Study ****
def test_image_locations_with_order_strategy(self):
self.api_server.show_image_direct_url = True
self.api_server.show_multiple_locations = True
self.image_location_quota = 10
self.api_server.location_strategy = 'location_order'
preference = "http, swift, filesystem"
self.api_server.store_type_location_strategy_preference = preference
self.start_servers(**self.__dict__.copy())
# Create an image
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'type': 'kernel',
'foo': 'bar', 'disk_format': 'aki',
'container_format': 'aki'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Get the image id
image = jsonutils.loads(response.text)
image_id = image['id']
# Image locations should not be visible before location is set
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertTrue('locations' in image)
self.assertTrue(image["locations"] == [])
# Update image locations via PATCH
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
values = [{'url': 'file://%s' % self.foo_image_file.name,
'metadata': {'idx': '1'}},
{'url': 'http://127.0.0.1:%s/foo_image' % self.http_port,
'metadata': {'idx': '0'}}]
doc = [{'op': 'replace',
'path': '/locations',
'value': values}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code)
# Image locations should be visible
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertTrue('locations' in image)
self.assertEqual(image['locations'], values)
self.assertTrue('direct_url' in image)
self.assertEqual(image['direct_url'], values[0]['url'])
self.stop_servers()
**** CubicPower OpenStack Study ****
def test_image_locatons_with_store_type_strategy(self):
self.api_server.show_image_direct_url = True
self.api_server.show_multiple_locations = True
self.image_location_quota = 10
self.api_server.location_strategy = 'store_type'
preference = "http, swift, filesystem"
self.api_server.store_type_location_strategy_preference = preference
self.start_servers(**self.__dict__.copy())
# Create an image
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'image-1', 'type': 'kernel',
'foo': 'bar', 'disk_format': 'aki',
'container_format': 'aki'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
# Get the image id
image = jsonutils.loads(response.text)
image_id = image['id']
# Image locations should not be visible before location is set
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertTrue('locations' in image)
self.assertTrue(image["locations"] == [])
# Update image locations via PATCH
path = self._url('/v2/images/%s' % image_id)
media_type = 'application/openstack-images-v2.1-json-patch'
headers = self._headers({'content-type': media_type})
values = [{'url': 'file://%s' % self.foo_image_file.name,
'metadata': {'idx': '1'}},
{'url': 'http://127.0.0.1:%s/foo_image' % self.http_port,
'metadata': {'idx': '0'}}]
doc = [{'op': 'replace',
'path': '/locations',
'value': values}]
data = jsonutils.dumps(doc)
response = requests.patch(path, headers=headers, data=data)
self.assertEqual(200, response.status_code)
values.sort(key=lambda loc: int(loc['metadata']['idx']))
# Image locations should be visible
path = self._url('/v2/images/%s' % image_id)
headers = self._headers({'Content-Type': 'application/json'})
response = requests.get(path, headers=headers)
self.assertEqual(200, response.status_code)
image = jsonutils.loads(response.text)
self.assertTrue('locations' in image)
self.assertEqual(image['locations'], values)
self.assertTrue('direct_url' in image)
self.assertEqual(image['direct_url'], values[0]['url'])
self.stop_servers()
**** CubicPower OpenStack Study ****
class TestImageMembers(functional.FunctionalTest):
**** CubicPower OpenStack Study ****
def setUp(self):
super(TestImageMembers, self).setUp()
self.cleanup()
self.api_server.deployment_flavor = 'fakeauth'
self.registry_server.deployment_flavor = 'fakeauth'
self.start_servers(**self.__dict__.copy())
**** CubicPower OpenStack Study ****
def _url(self, path):
return 'http://127.0.0.1:%d%s' % (self.api_port, path)
**** CubicPower OpenStack Study ****
def _headers(self, custom_headers=None):
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': TENANT1,
'X-Roles': 'member',
}
base_headers.update(custom_headers or {})
return base_headers
**** CubicPower OpenStack Study ****
def test_image_member_lifecycle(self):
def get_header(tenant, role=''):
auth_token = 'user:%s:%s' % (tenant, role)
headers = {'X-Auth-Token': auth_token}
return headers
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
owners = ['tenant1', 'tenant2', 'admin']
visibilities = ['public', 'private']
image_fixture = []
for owner in owners:
for visibility in visibilities:
path = self._url('/v2/images')
headers = self._headers({
'content-type': 'application/json',
'X-Auth-Token': 'createuser:%s:admin' % owner,
})
data = jsonutils.dumps({
'name': '%s-%s' % (owner, visibility),
'visibility': visibility,
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image_fixture.append(jsonutils.loads(response.text))
# Image list should contain 4 images for tenant1
path = self._url('/v2/images')
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Image list should contain 3 images for TENANT3
path = self._url('/v2/images')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(3, len(images))
# Add Image member for tenant1-private image
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
body = jsonutils.dumps({'member': TENANT3})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(200, response.status_code)
image_member = jsonutils.loads(response.text)
self.assertEqual(image_fixture[1]['id'], image_member['image_id'])
self.assertEqual(TENANT3, image_member['member_id'])
self.assertTrue('created_at' in image_member)
self.assertTrue('updated_at' in image_member)
self.assertEqual('pending', image_member['status'])
# Image list should contain 3 images for TENANT3
path = self._url('/v2/images')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(3, len(images))
# Image list should contain 0 shared images for TENANT3
# because default is accepted
path = self._url('/v2/images?visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image list should contain 4 images for TENANT3 with status pending
path = self._url('/v2/images?member_status=pending')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Image list should contain 4 images for TENANT3 with status all
path = self._url('/v2/images?member_status=all')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Image list should contain 1 image for TENANT3 with status pending
# and visibility shared
path = self._url('/v2/images?member_status=pending&visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['name'], 'tenant1-private')
# Image list should contain 0 image for TENANT3 with status rejected
# and visibility shared
path = self._url('/v2/images?member_status=rejected&visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image list should contain 0 image for TENANT3 with status accepted
# and visibility shared
path = self._url('/v2/images?member_status=accepted&visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image list should contain 0 image for TENANT3 with status accepted
# and visibility private
path = self._url('/v2/images?visibility=private')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image tenant2-private's image members list should contain no members
path = self._url('/v2/images/%s/members' % image_fixture[3]['id'])
response = requests.get(path, headers=get_header('tenant2'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(0, len(body['members']))
# Tenant 1, who is the owner cannot change status of image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'accepted'})
response = requests.put(path, headers=get_header('tenant1'), data=body)
self.assertEqual(403, response.status_code)
# Tenant 1, who is the owner can get status of its own image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(body['status'], 'pending')
self.assertEqual(body['image_id'], image_fixture[1]['id'])
self.assertEqual(body['member_id'], TENANT3)
# Tenant 3, who is the member can get status of its own status
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(body['status'], 'pending')
self.assertEqual(body['image_id'], image_fixture[1]['id'])
self.assertEqual(body['member_id'], TENANT3)
# Tenant 2, who not the owner cannot get status of image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.get(path, headers=get_header('tenant2'))
self.assertEqual(404, response.status_code)
# Tenant 3 can change status of image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'accepted'})
response = requests.put(path, headers=get_header(TENANT3), data=body)
self.assertEqual(200, response.status_code)
image_member = jsonutils.loads(response.text)
self.assertEqual(image_fixture[1]['id'], image_member['image_id'])
self.assertEqual(TENANT3, image_member['member_id'])
self.assertEqual('accepted', image_member['status'])
# Image list should contain 4 images for TENANT3 because status is
# accepted
path = self._url('/v2/images')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Tenant 3 invalid status change
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'invalid-status'})
response = requests.put(path, headers=get_header(TENANT3), data=body)
self.assertEqual(400, response.status_code)
# Owner cannot change status of image
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'accepted'})
response = requests.put(path, headers=get_header('tenant1'), data=body)
self.assertEqual(403, response.status_code)
# Add Image member for tenant2-private image
path = self._url('/v2/images/%s/members' % image_fixture[3]['id'])
body = jsonutils.dumps({'member': TENANT4})
response = requests.post(path, headers=get_header('tenant2'),
data=body)
self.assertEqual(200, response.status_code)
image_member = jsonutils.loads(response.text)
self.assertEqual(image_fixture[3]['id'], image_member['image_id'])
self.assertEqual(TENANT4, image_member['member_id'])
self.assertTrue('created_at' in image_member)
self.assertTrue('updated_at' in image_member)
# Add Image member to public image
path = self._url('/v2/images/%s/members' % image_fixture[0]['id'])
body = jsonutils.dumps({'member': TENANT2})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(403, response.status_code)
# Image tenant1-private's members list should contain 1 member
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(1, len(body['members']))
# Admin can see any members
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
response = requests.get(path, headers=get_header('tenant1', 'admin'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(1, len(body['members']))
# Image members not found for private image not owned by TENANT 1
path = self._url('/v2/images/%s/members' % image_fixture[3]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(404, response.status_code)
# Image members forbidden for public image
path = self._url('/v2/images/%s/members' % image_fixture[0]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(403, response.status_code)
# Image Member Cannot delete Image membership
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.delete(path, headers=get_header(TENANT3))
self.assertEqual(403, response.status_code)
# Delete Image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.delete(path, headers=get_header('tenant1'))
self.assertEqual(204, response.status_code)
# Now the image has only no members
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(0, len(body['members']))
# Adding 11 image members should fail since configured limit is 10
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
for i in range(10):
body = jsonutils.dumps({'member': str(uuid.uuid4())})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(200, response.status_code)
body = jsonutils.dumps({'member': str(uuid.uuid4())})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(413, response.status_code)
# Delete Image members not found for public image
path = self._url('/v2/images/%s/members/%s' % (image_fixture[0]['id'],
TENANT3))
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(404, response.status_code)
self.stop_servers()
**** CubicPower OpenStack Study ****
def get_header(tenant, role=''):
auth_token = 'user:%s:%s' % (tenant, role)
headers = {'X-Auth-Token': auth_token}
return headers
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
owners = ['tenant1', 'tenant2', 'admin']
visibilities = ['public', 'private']
image_fixture = []
for owner in owners:
for visibility in visibilities:
path = self._url('/v2/images')
headers = self._headers({
'content-type': 'application/json',
'X-Auth-Token': 'createuser:%s:admin' % owner,
})
data = jsonutils.dumps({
'name': '%s-%s' % (owner, visibility),
'visibility': visibility,
})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image_fixture.append(jsonutils.loads(response.text))
# Image list should contain 4 images for tenant1
path = self._url('/v2/images')
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Image list should contain 3 images for TENANT3
path = self._url('/v2/images')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(3, len(images))
# Add Image member for tenant1-private image
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
body = jsonutils.dumps({'member': TENANT3})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(200, response.status_code)
image_member = jsonutils.loads(response.text)
self.assertEqual(image_fixture[1]['id'], image_member['image_id'])
self.assertEqual(TENANT3, image_member['member_id'])
self.assertTrue('created_at' in image_member)
self.assertTrue('updated_at' in image_member)
self.assertEqual('pending', image_member['status'])
# Image list should contain 3 images for TENANT3
path = self._url('/v2/images')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(3, len(images))
# Image list should contain 0 shared images for TENANT3
# because default is accepted
path = self._url('/v2/images?visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image list should contain 4 images for TENANT3 with status pending
path = self._url('/v2/images?member_status=pending')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Image list should contain 4 images for TENANT3 with status all
path = self._url('/v2/images?member_status=all')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Image list should contain 1 image for TENANT3 with status pending
# and visibility shared
path = self._url('/v2/images?member_status=pending&visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(1, len(images))
self.assertEqual(images[0]['name'], 'tenant1-private')
# Image list should contain 0 image for TENANT3 with status rejected
# and visibility shared
path = self._url('/v2/images?member_status=rejected&visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image list should contain 0 image for TENANT3 with status accepted
# and visibility shared
path = self._url('/v2/images?member_status=accepted&visibility=shared')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image list should contain 0 image for TENANT3 with status accepted
# and visibility private
path = self._url('/v2/images?visibility=private')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Image tenant2-private's image members list should contain no members
path = self._url('/v2/images/%s/members' % image_fixture[3]['id'])
response = requests.get(path, headers=get_header('tenant2'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(0, len(body['members']))
# Tenant 1, who is the owner cannot change status of image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'accepted'})
response = requests.put(path, headers=get_header('tenant1'), data=body)
self.assertEqual(403, response.status_code)
# Tenant 1, who is the owner can get status of its own image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(body['status'], 'pending')
self.assertEqual(body['image_id'], image_fixture[1]['id'])
self.assertEqual(body['member_id'], TENANT3)
# Tenant 3, who is the member can get status of its own status
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(body['status'], 'pending')
self.assertEqual(body['image_id'], image_fixture[1]['id'])
self.assertEqual(body['member_id'], TENANT3)
# Tenant 2, who not the owner cannot get status of image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.get(path, headers=get_header('tenant2'))
self.assertEqual(404, response.status_code)
# Tenant 3 can change status of image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'accepted'})
response = requests.put(path, headers=get_header(TENANT3), data=body)
self.assertEqual(200, response.status_code)
image_member = jsonutils.loads(response.text)
self.assertEqual(image_fixture[1]['id'], image_member['image_id'])
self.assertEqual(TENANT3, image_member['member_id'])
self.assertEqual('accepted', image_member['status'])
# Image list should contain 4 images for TENANT3 because status is
# accepted
path = self._url('/v2/images')
response = requests.get(path, headers=get_header(TENANT3))
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(4, len(images))
# Tenant 3 invalid status change
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'invalid-status'})
response = requests.put(path, headers=get_header(TENANT3), data=body)
self.assertEqual(400, response.status_code)
# Owner cannot change status of image
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
body = jsonutils.dumps({'status': 'accepted'})
response = requests.put(path, headers=get_header('tenant1'), data=body)
self.assertEqual(403, response.status_code)
# Add Image member for tenant2-private image
path = self._url('/v2/images/%s/members' % image_fixture[3]['id'])
body = jsonutils.dumps({'member': TENANT4})
response = requests.post(path, headers=get_header('tenant2'),
data=body)
self.assertEqual(200, response.status_code)
image_member = jsonutils.loads(response.text)
self.assertEqual(image_fixture[3]['id'], image_member['image_id'])
self.assertEqual(TENANT4, image_member['member_id'])
self.assertTrue('created_at' in image_member)
self.assertTrue('updated_at' in image_member)
# Add Image member to public image
path = self._url('/v2/images/%s/members' % image_fixture[0]['id'])
body = jsonutils.dumps({'member': TENANT2})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(403, response.status_code)
# Image tenant1-private's members list should contain 1 member
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(1, len(body['members']))
# Admin can see any members
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
response = requests.get(path, headers=get_header('tenant1', 'admin'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(1, len(body['members']))
# Image members not found for private image not owned by TENANT 1
path = self._url('/v2/images/%s/members' % image_fixture[3]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(404, response.status_code)
# Image members forbidden for public image
path = self._url('/v2/images/%s/members' % image_fixture[0]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(403, response.status_code)
# Image Member Cannot delete Image membership
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.delete(path, headers=get_header(TENANT3))
self.assertEqual(403, response.status_code)
# Delete Image member
path = self._url('/v2/images/%s/members/%s' % (image_fixture[1]['id'],
TENANT3))
response = requests.delete(path, headers=get_header('tenant1'))
self.assertEqual(204, response.status_code)
# Now the image has only no members
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(200, response.status_code)
body = jsonutils.loads(response.text)
self.assertEqual(0, len(body['members']))
# Adding 11 image members should fail since configured limit is 10
path = self._url('/v2/images/%s/members' % image_fixture[1]['id'])
for i in range(10):
body = jsonutils.dumps({'member': str(uuid.uuid4())})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(200, response.status_code)
body = jsonutils.dumps({'member': str(uuid.uuid4())})
response = requests.post(path, headers=get_header('tenant1'),
data=body)
self.assertEqual(413, response.status_code)
# Delete Image members not found for public image
path = self._url('/v2/images/%s/members/%s' % (image_fixture[0]['id'],
TENANT3))
response = requests.get(path, headers=get_header('tenant1'))
self.assertEqual(404, response.status_code)
self.stop_servers()
**** CubicPower OpenStack Study ****
class TestQuotas(functional.FunctionalTest):
**** CubicPower OpenStack Study ****
def setUp(self):
super(TestQuotas, self).setUp()
self.cleanup()
self.api_server.deployment_flavor = 'noauth'
self.user_storage_quota = 100
self.start_servers(**self.__dict__.copy())
**** CubicPower OpenStack Study ****
def _url(self, path):
return 'http://127.0.0.1:%d%s' % (self.api_port, path)
**** CubicPower OpenStack Study ****
def _headers(self, custom_headers=None):
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': TENANT1,
'X-Roles': 'member',
}
base_headers.update(custom_headers or {})
return base_headers
**** CubicPower OpenStack Study ****
def _upload_image_test(self, data_src, expected_status):
# Image list should be empty
path = self._url('/v2/images')
response = requests.get(path, headers=self._headers())
self.assertEqual(200, response.status_code)
images = jsonutils.loads(response.text)['images']
self.assertEqual(0, len(images))
# Create an image (with a deployer-defined property)
path = self._url('/v2/images')
headers = self._headers({'content-type': 'application/json'})
data = jsonutils.dumps({'name': 'testimg',
'type': 'kernel',
'foo': 'bar',
'disk_format': 'aki',
'container_format': 'aki'})
response = requests.post(path, headers=headers, data=data)
self.assertEqual(201, response.status_code)
image = jsonutils.loads(response.text)
image_id = image['id']
# upload data
path = self._url('/v2/images/%s/file' % image_id)
headers = self._headers({'Content-Type': 'application/octet-stream'})
response = requests.put(path, headers=headers, data=data_src)
self.assertEqual(expected_status, response.status_code)
# Deletion should work
path = self._url('/v2/images/%s' % image_id)
response = requests.delete(path, headers=self._headers())
self.assertEqual(204, response.status_code)
**** CubicPower OpenStack Study ****
def test_image_upload_under_quota(self):
data = 'x' * (self.user_storage_quota - 1)
self._upload_image_test(data, 204)
**** CubicPower OpenStack Study ****
def test_image_upload_exceed_quota(self):
data = 'x' * (self.user_storage_quota + 1)
self._upload_image_test(data, 413)
**** CubicPower OpenStack Study ****
def test_chunked_image_upload_under_quota(self):
def data_gen():
yield 'x' * (self.user_storage_quota - 1)
self._upload_image_test(data_gen(), 204)
**** CubicPower OpenStack Study ****
def data_gen():
yield 'x' * (self.user_storage_quota - 1)
self._upload_image_test(data_gen(), 204)
**** CubicPower OpenStack Study ****
def test_chunked_image_upload_exceed_quota(self):
def data_gen():
yield 'x' * (self.user_storage_quota + 1)
self._upload_image_test(data_gen(), 413)
**** CubicPower OpenStack Study ****
def data_gen():
yield 'x' * (self.user_storage_quota + 1)
self._upload_image_test(data_gen(), 413)