Better support for community images
This commit updates several places where image information is processed and extends support for the 'community' image visibility value. Some support did exist already, which was mostly just the main Images tab of the dashboard, but this commit also includes support for: - image name in the instances list/details - 'Community' visibility label in the Images tab - Listing of community images in launch instance wizard Closes-Bug: #1779250 Change-Id: Iedea0b7d20313837a72a2759511251a7bb324869
This commit is contained in:
parent
2eea0f15b3
commit
d641e6d105
openstack_dashboard
api
dashboards/project
images
instances
static/dashboard/project/workflow/launch-instance
static/app/core/images/filters
test
@ -493,7 +493,8 @@ def create_image_metadata(data):
|
||||
_handle_unknown_properties(data, properties)
|
||||
|
||||
if ('visibility' in data and
|
||||
data['visibility'] not in ['public', 'private', 'shared']):
|
||||
data['visibility'] not in ['public', 'private', 'community',
|
||||
'shared']):
|
||||
raise KeyError('invalid visibility option: %s' % data['visibility'])
|
||||
_normalize_is_public_filter(data)
|
||||
|
||||
|
@ -67,7 +67,8 @@ class Image(generic.View):
|
||||
:param min_disk: (optional) the minimum disk size
|
||||
for the image to boot with
|
||||
:param min_ram: (optional) the minimum ram for the image to boot with
|
||||
:param visibility: (required) takes 'public', 'shared', and 'private'
|
||||
:param visibility: (required) takes 'public', 'shared', 'private' and
|
||||
'community'
|
||||
:param protected: (required) true if the image is protected
|
||||
|
||||
Any parameters not listed above will be assigned as custom properties
|
||||
@ -202,7 +203,8 @@ class Images(generic.View):
|
||||
:param min_disk: (optional) the minimum disk size
|
||||
for the image to boot with
|
||||
:param min_ram: (optional) the minimum ram for the image to boot with
|
||||
:param visibility: (required) takes 'public', 'private', and 'shared'
|
||||
:param visibility: (required) takes 'public', 'private', 'shared', and
|
||||
'community'
|
||||
:param protected: (required) true if the image is protected
|
||||
:param import_data: (optional) true to copy the image data
|
||||
to the image service or use it from the current location
|
||||
|
@ -399,7 +399,7 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
|
||||
|
||||
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
|
||||
def test_image_detail_custom_props_get_v1(self):
|
||||
image = self.images.list()[8]
|
||||
image = self.images.list()[9]
|
||||
|
||||
self._test_image_detail_custom_props_get(image)
|
||||
|
||||
|
@ -59,7 +59,7 @@ class ImagesAndSnapshotsTests(BaseImagesTestCase):
|
||||
images_table = res.context['images_table']
|
||||
images = images_table.data
|
||||
|
||||
self.assertEqual(len(images), 9)
|
||||
self.assertEqual(len(images), 10)
|
||||
row_actions = images_table.get_row_actions(images[0])
|
||||
self.assertEqual(len(row_actions), 5)
|
||||
row_actions = images_table.get_row_actions(images[1])
|
||||
@ -155,9 +155,13 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
shared_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'shared')]
|
||||
community_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'community')]
|
||||
self.mock_image_list.side_effect = [
|
||||
[public_images, False, False],
|
||||
[private_images, False, False],
|
||||
[community_images, False, False],
|
||||
[shared_images, False, False]
|
||||
]
|
||||
|
||||
@ -167,6 +171,8 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'property-owner_id': self.tenant.id,
|
||||
'status': 'active'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'visibility': 'community', 'status': 'active'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'visibility': 'shared', 'status': 'active'})
|
||||
]
|
||||
@ -186,6 +192,9 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
private_images = [image for image in self.images.list()
|
||||
if (image.status == 'active' and
|
||||
not image.is_public)]
|
||||
community_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'community')]
|
||||
shared_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'shared')]
|
||||
@ -193,6 +202,7 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
self.mock_image_list.side_effect = [
|
||||
[public_images, False, False],
|
||||
[private_images, False, False],
|
||||
[community_images, False, False],
|
||||
[shared_images, False, False],
|
||||
[private_images, False, False]
|
||||
]
|
||||
@ -203,6 +213,8 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'property-owner_id': self.tenant.id,
|
||||
'status': 'active'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'visibility': 'community', 'status': 'active'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'visibility': 'shared', 'status': 'active'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
@ -225,6 +237,9 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
self.assertEqual(
|
||||
len(private_images),
|
||||
len(images_cache['images_by_project'][self.tenant.id]))
|
||||
self.assertEqual(
|
||||
len(community_images),
|
||||
len(images_cache['community_images']))
|
||||
self.assertEqual(
|
||||
len(shared_images),
|
||||
len(images_cache['shared_images']))
|
||||
@ -252,6 +267,9 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
private_images = [image for image in self.images.list()
|
||||
if (image.status == 'active' and
|
||||
not image.is_public)]
|
||||
community_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'community')]
|
||||
shared_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'shared')]
|
||||
@ -259,7 +277,8 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
self.mock_image_list.side_effect = [
|
||||
self.exceptions.glance,
|
||||
[private_images, False, False],
|
||||
[shared_images, False, False],
|
||||
[community_images, False, False],
|
||||
[shared_images, False, False]
|
||||
]
|
||||
|
||||
images_cache = {}
|
||||
@ -270,6 +289,8 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
filters={'is_public': True, 'status': 'active'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'status': 'active', 'property-owner_id': '1'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'visibility': 'community', 'status': 'active'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'visibility': 'shared', 'status': 'active'})
|
||||
]
|
||||
@ -286,6 +307,9 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
self.assertEqual(
|
||||
len(private_images),
|
||||
len(images_cache['images_by_project'][self.tenant.id]))
|
||||
self.assertEqual(
|
||||
len(community_images),
|
||||
len(images_cache['community_images']))
|
||||
self.assertEqual(
|
||||
len(shared_images),
|
||||
len(images_cache['shared_images']))
|
||||
@ -297,6 +321,9 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
private_images = [image for image in self.images.list()
|
||||
if (image.status == 'active' and
|
||||
not image.is_public)]
|
||||
community_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'community')]
|
||||
shared_images = [image for image in self.imagesV2.list()
|
||||
if (image.status == 'active' and
|
||||
image.visibility == 'shared')]
|
||||
@ -304,6 +331,7 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
self.mock_image_list.side_effect = [
|
||||
[public_images, False, False],
|
||||
self.exceptions.glance,
|
||||
[community_images, False, False],
|
||||
[shared_images, False, False],
|
||||
[private_images, False, False]
|
||||
]
|
||||
@ -317,6 +345,9 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
len(public_images),
|
||||
len(images_cache['public_images']))
|
||||
self.assertFalse(len(images_cache['images_by_project']))
|
||||
self.assertEqual(
|
||||
len(community_images),
|
||||
len(images_cache['community_images']))
|
||||
self.assertEqual(
|
||||
len(shared_images),
|
||||
len(images_cache['shared_images']))
|
||||
@ -334,6 +365,9 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
self.assertEqual(
|
||||
len(private_images),
|
||||
len(images_cache['images_by_project'][self.tenant.id]))
|
||||
self.assertEqual(
|
||||
len(community_images),
|
||||
len(images_cache['community_images']))
|
||||
self.assertEqual(
|
||||
len(shared_images),
|
||||
len(images_cache['shared_images']))
|
||||
@ -343,6 +377,8 @@ class ImagesAndSnapshotsUtilsTests(BaseImagesTestCase):
|
||||
filters={'status': 'active', 'is_public': True}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'status': 'active', 'property-owner_id': '1'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'status': 'active', 'visibility': 'community'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
filters={'status': 'active', 'visibility': 'shared'}),
|
||||
mock.call(test.IsHttpRequest(),
|
||||
|
@ -21,9 +21,9 @@ from openstack_dashboard.api import glance
|
||||
def get_available_images(request, project_id=None, images_cache=None):
|
||||
"""Returns a list of available images
|
||||
|
||||
Returns a list of images that are public, shared or owned by the given
|
||||
project_id. If project_id is not specified, only public images are
|
||||
returned.
|
||||
Returns a list of images that are public, shared, community or owned by
|
||||
the given project_id. If project_id is not specified, only public and
|
||||
community images are returned.
|
||||
|
||||
:param images_cache: An optional dict-like object in which to
|
||||
cache public and per-project id image metadata.
|
||||
@ -32,6 +32,7 @@ def get_available_images(request, project_id=None, images_cache=None):
|
||||
if images_cache is None:
|
||||
images_cache = {}
|
||||
public_images = images_cache.get('public_images', [])
|
||||
community_images = images_cache.get('community_images', [])
|
||||
images_by_project = images_cache.get('images_by_project', {})
|
||||
shared_images = images_cache.get('shared_images', [])
|
||||
if 'public_images' not in images_cache:
|
||||
@ -65,6 +66,18 @@ def get_available_images(request, project_id=None, images_cache=None):
|
||||
else:
|
||||
owned_images = images_by_project[project_id]
|
||||
|
||||
if 'community_images' not in images_cache:
|
||||
community = {"visibility": "community",
|
||||
"status": "active"}
|
||||
try:
|
||||
images, _more, _prev = glance.image_list_detailed(
|
||||
request, filters=community)
|
||||
[community_images.append(image) for image in images]
|
||||
images_cache['community_images'] = community_images
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Unable to retrieve community images."))
|
||||
|
||||
if 'shared_images' not in images_cache:
|
||||
shared = {"visibility": "shared",
|
||||
"status": "active"}
|
||||
@ -79,7 +92,7 @@ def get_available_images(request, project_id=None, images_cache=None):
|
||||
if 'images_by_project' not in images_cache:
|
||||
images_cache['images_by_project'] = images_by_project
|
||||
|
||||
images = owned_images + public_images + shared_images
|
||||
images = owned_images + public_images + community_images + shared_images
|
||||
|
||||
image_ids = []
|
||||
final_images = []
|
||||
|
@ -2171,7 +2171,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
helpers.IsHttpRequest(),
|
||||
search_opts=SNAPSHOT_SEARCH_OPTS)
|
||||
|
||||
self._check_glance_image_list_detailed(count=5)
|
||||
self._check_glance_image_list_detailed(count=8)
|
||||
|
||||
self.mock_network_list.assert_has_calls([
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
@ -2349,7 +2349,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.mock_volume_snapshot_list.assert_called_once_with(
|
||||
helpers.IsHttpRequest(),
|
||||
search_opts=SNAPSHOT_SEARCH_OPTS)
|
||||
self._check_glance_image_list_detailed(count=5)
|
||||
self._check_glance_image_list_detailed(count=8)
|
||||
self.mock_network_list.assert_has_calls([
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
tenant_id=self.tenant.id, shared=False),
|
||||
@ -2445,7 +2445,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.mock_volume_snapshot_list.assert_called_once_with(
|
||||
helpers.IsHttpRequest(),
|
||||
search_opts=SNAPSHOT_SEARCH_OPTS)
|
||||
self._check_glance_image_list_detailed(count=5)
|
||||
self._check_glance_image_list_detailed(count=8)
|
||||
self.mock_network_list.assert_has_calls([
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
tenant_id=self.tenant.id, shared=False),
|
||||
@ -2539,7 +2539,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
self._check_nova_glance_neutron_lists(flavor_count=2, image_count=5)
|
||||
self._check_nova_glance_neutron_lists(flavor_count=2, image_count=8)
|
||||
self._check_extension_supported({
|
||||
'BlockDeviceMappingV2Boot': 1,
|
||||
'DiskConfig': 1,
|
||||
@ -2685,7 +2685,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
self._check_nova_glance_neutron_lists(flavor_count=2, image_count=4)
|
||||
self._check_nova_glance_neutron_lists(flavor_count=2, image_count=6)
|
||||
self._check_extension_supported({
|
||||
'BlockDeviceMappingV2Boot': 2,
|
||||
'DiskConfig': 1,
|
||||
@ -2804,7 +2804,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
self._check_nova_glance_neutron_lists(flavor_count=2,
|
||||
image_count=4)
|
||||
image_count=6)
|
||||
self.assert_mock_multiple_calls_with_same_arguments(
|
||||
self.mock_flavor_list, 2,
|
||||
mock.call(helpers.IsHttpRequest()))
|
||||
@ -2920,7 +2920,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
|
||||
])
|
||||
self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
|
||||
self._check_glance_image_list_detailed(count=5)
|
||||
self._check_glance_image_list_detailed(count=8)
|
||||
self._check_neutron_network_and_port_list()
|
||||
self._check_nova_lists(flavor_count=3)
|
||||
self.mock_volume_list.assert_has_calls([
|
||||
@ -3137,7 +3137,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
|
||||
self.assertFormErrors(res, 3, "You must select a snapshot.")
|
||||
|
||||
self.assertEqual(3, self.mock_image_list_detailed.call_count)
|
||||
self.assertEqual(4, self.mock_image_list_detailed.call_count)
|
||||
self.mock_image_list_detailed.assert_has_calls([
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
filters={'is_public': True,
|
||||
@ -3145,6 +3145,8 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
filters={'property-owner_id': self.tenant.id,
|
||||
'status': 'active'}),
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
filters={'status': 'active', 'visibility': 'community'}),
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
filters={'status': 'active', 'visibility': 'shared'}),
|
||||
])
|
||||
@ -3220,7 +3222,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
helpers.IsHttpRequest(),
|
||||
search_opts=SNAPSHOT_SEARCH_OPTS)
|
||||
|
||||
self._check_glance_image_list_detailed(count=5)
|
||||
self._check_glance_image_list_detailed(count=8)
|
||||
self._check_neutron_network_and_port_list()
|
||||
|
||||
self.mock_tenant_quota_usages.assert_called_once_with(
|
||||
@ -3339,7 +3341,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.mock_availability_zone_list.assert_called_once_with(
|
||||
helpers.IsHttpRequest())
|
||||
|
||||
self._check_glance_image_list_detailed(count=5)
|
||||
self._check_glance_image_list_detailed(count=8)
|
||||
self._check_neutron_network_and_port_list()
|
||||
|
||||
self.mock_server_create.assert_called_once_with(
|
||||
@ -3428,7 +3430,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.assertContains(res, "greater than or equal to 1")
|
||||
|
||||
self._check_nova_glance_neutron_lists(flavor_count=3,
|
||||
image_count=6)
|
||||
image_count=10)
|
||||
self._check_extension_supported({
|
||||
'BlockDeviceMappingV2Boot': 1,
|
||||
'DiskConfig': 1,
|
||||
@ -3539,7 +3541,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.assertContains(res, msg)
|
||||
|
||||
self._check_nova_glance_neutron_lists(flavor_count=3,
|
||||
image_count=6)
|
||||
image_count=10)
|
||||
self._check_extension_supported({
|
||||
'BlockDeviceMappingV2Boot': 1,
|
||||
'DiskConfig': 1,
|
||||
@ -3641,7 +3643,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
res = self.client.post(url, form_data)
|
||||
|
||||
self._check_nova_glance_neutron_lists(flavor_count=3,
|
||||
image_count=6)
|
||||
image_count=10)
|
||||
self._check_extension_supported({
|
||||
'BlockDeviceMappingV2Boot': 1,
|
||||
'DiskConfig': 1,
|
||||
@ -3802,7 +3804,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
helpers.IsHttpRequest())
|
||||
self.mock_availability_zone_list.assert_called_once_with(
|
||||
helpers.IsHttpRequest())
|
||||
self.assertEqual(6, self.mock_image_list_detailed.call_count)
|
||||
self.assertEqual(10, self.mock_image_list_detailed.call_count)
|
||||
self.mock_image_list_detailed.assert_has_calls(
|
||||
[
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
@ -3813,6 +3815,9 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
'status': 'active'})
|
||||
] +
|
||||
[
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
filters={'status': 'active',
|
||||
'visibility': 'community'}),
|
||||
mock.call(helpers.IsHttpRequest(),
|
||||
filters={'status': 'active',
|
||||
'visibility': 'shared'})
|
||||
@ -3974,9 +3979,9 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.mock_availability_zone_list.assert_called_once_with(
|
||||
helpers.IsHttpRequest())
|
||||
if avail_volumes is None:
|
||||
image_list_count = 6
|
||||
image_list_count = 10
|
||||
else:
|
||||
image_list_count = 5
|
||||
image_list_count = 8
|
||||
self._check_glance_image_list_detailed(count=image_list_count)
|
||||
self._check_neutron_network_and_port_list()
|
||||
self.mock_server_group_list.assert_called_once_with(
|
||||
@ -4221,7 +4226,7 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
|
||||
self.assertNoFormErrors(res)
|
||||
|
||||
self._check_nova_glance_neutron_lists(flavor_count=2,
|
||||
image_count=5)
|
||||
image_count=8)
|
||||
self._check_extension_supported({
|
||||
'BlockDeviceMappingV2Boot': 1,
|
||||
'DiskConfig': 1,
|
||||
@ -4366,7 +4371,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
])
|
||||
self.mock_volume_snapshot_list.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS)
|
||||
self._check_glance_image_list_detailed(count=5)
|
||||
self._check_glance_image_list_detailed(count=8)
|
||||
self._check_neutron_network_and_port_list()
|
||||
self.mock_tenant_quota_usages.assert_called_once_with(
|
||||
helpers.IsHttpRequest(),
|
||||
@ -4620,7 +4625,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
|
||||
self.mock_server_get.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id)
|
||||
self._check_glance_image_list_detailed(count=3)
|
||||
self._check_glance_image_list_detailed(count=4)
|
||||
self.mock_extension_supported.assert_called_once_with(
|
||||
'DiskConfig', helpers.IsHttpRequest())
|
||||
self.mock_is_feature_available.assert_called_once_with(
|
||||
@ -4674,7 +4679,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
|
||||
self.mock_server_get.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id)
|
||||
self._check_glance_image_list_detailed(count=3)
|
||||
self._check_glance_image_list_detailed(count=4)
|
||||
self.mock_extension_supported.assert_called_once_with(
|
||||
'DiskConfig', helpers.IsHttpRequest())
|
||||
self.mock_server_rebuild.assert_called_once_with(
|
||||
@ -4703,7 +4708,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
|
||||
self.mock_server_get.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id)
|
||||
self._check_glance_image_list_detailed(count=3)
|
||||
self._check_glance_image_list_detailed(count=4)
|
||||
self.mock_extension_supported.assert_called_once_with(
|
||||
'DiskConfig', helpers.IsHttpRequest())
|
||||
self.mock_server_rebuild.assert_called_once_with(
|
||||
@ -4734,10 +4739,10 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
["Passwords do not match."])
|
||||
|
||||
if django.VERSION >= (1, 9):
|
||||
image_list_count = 6
|
||||
image_list_count = 8
|
||||
ext_count = 2
|
||||
else:
|
||||
image_list_count = 3
|
||||
image_list_count = 5
|
||||
ext_count = 1
|
||||
self.mock_server_get.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id)
|
||||
@ -4769,7 +4774,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
|
||||
self.mock_server_get.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id)
|
||||
self._check_glance_image_list_detailed(count=3)
|
||||
self._check_glance_image_list_detailed(count=4)
|
||||
self.mock_extension_supported.assert_called_once_with(
|
||||
'DiskConfig', helpers.IsHttpRequest())
|
||||
self.mock_server_rebuild.assert_called_once_with(
|
||||
@ -4802,7 +4807,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
|
||||
self.mock_server_get.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id)
|
||||
self._check_glance_image_list_detailed(count=3)
|
||||
self._check_glance_image_list_detailed(count=4)
|
||||
self.mock_extension_supported.assert_called_once_with(
|
||||
'DiskConfig', helpers.IsHttpRequest())
|
||||
self.mock_server_rebuild.assert_called_once_with(
|
||||
@ -4832,7 +4837,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
|
||||
self.mock_server_get.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id)
|
||||
self._check_glance_image_list_detailed(count=3)
|
||||
self._check_glance_image_list_detailed(count=4)
|
||||
self.mock_extension_supported.assert_called_once_with(
|
||||
'DiskConfig', helpers.IsHttpRequest())
|
||||
self.mock_server_rebuild.assert_called_once_with(
|
||||
@ -5045,7 +5050,7 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
|
||||
password=password)
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
self._check_glance_image_list_detailed(count=3)
|
||||
self._check_glance_image_list_detailed(count=4)
|
||||
self.mock_server_rescue.assert_called_once_with(
|
||||
helpers.IsHttpRequest(), server.id, image=image.id,
|
||||
password=password)
|
||||
|
@ -563,7 +563,14 @@
|
||||
|
||||
if (enabledImage || enabledSnapshot) {
|
||||
var filter = {status: 'active', sort_key: 'name', sort_dir: 'asc'};
|
||||
return glanceAPI.getImages(filter).then(function getEnabledImages(data) {
|
||||
var filterCommunity = angular.merge({}, filter, {visibility: 'community'});
|
||||
|
||||
var imagePromises = [
|
||||
glanceAPI.getImages(filter),
|
||||
glanceAPI.getImages(filterCommunity)
|
||||
];
|
||||
|
||||
$q.all(imagePromises).then(function getEnabledImages(data) {
|
||||
if (enabledImage) {
|
||||
onGetImages(data);
|
||||
}
|
||||
@ -666,18 +673,22 @@
|
||||
|
||||
function onGetImages(data) {
|
||||
model.images.length = 0;
|
||||
push.apply(model.images, data.data.items.filter(function (image) {
|
||||
return isBootableImageType(image) && getImageType(image) !== 'snapshot';
|
||||
}));
|
||||
angular.forEach(data, function addData(data) {
|
||||
push.apply(model.images, data.data.items.filter(function (image) {
|
||||
return isBootableImageType(image) &&
|
||||
(!image.properties || image.properties.image_type !== 'snapshot');
|
||||
}));
|
||||
});
|
||||
addAllowedBootSource(model.images, bootSourceTypes.IMAGE, gettext('Image'));
|
||||
}
|
||||
|
||||
function onGetSnapshots(data) {
|
||||
model.imageSnapshots.length = 0;
|
||||
push.apply(model.imageSnapshots, data.data.items.filter(function (image) {
|
||||
return isBootableImageType(image) && getImageType(image) === 'snapshot';
|
||||
}));
|
||||
|
||||
angular.forEach(data, function addData(data) {
|
||||
push.apply(model.imageSnapshots, data.data.items.filter(function (image) {
|
||||
return isBootableImageType(image) && getImageType(image) === 'snapshot';
|
||||
}));
|
||||
});
|
||||
addAllowedBootSource(
|
||||
model.imageSnapshots,
|
||||
bootSourceTypes.INSTANCE_SNAPSHOT,
|
||||
|
@ -430,8 +430,8 @@
|
||||
expect(model.initialized).toBe(true);
|
||||
expect(model.newInstanceSpec).toBeDefined();
|
||||
|
||||
expect(model.images.length).toBe(4);
|
||||
expect(model.imageSnapshots.length).toBe(2);
|
||||
expect(model.images.length).toBe(12);
|
||||
expect(model.imageSnapshots.length).toBe(4);
|
||||
expect(model.availabilityZones.length).toBe(3); // 2 + 1 for 'nova pick'
|
||||
expect(model.flavors.length).toBe(2);
|
||||
expect(model.keypairs.length).toBe(2);
|
||||
|
@ -162,7 +162,11 @@
|
||||
};
|
||||
|
||||
// Map Visibility data so we can decode true/false to Public/Private
|
||||
var _visibilitymap = { true: gettext('Public'), false: gettext('Private') };
|
||||
var _visibilitymap = { 'public': gettext('Public'),
|
||||
'private': gettext('Private'),
|
||||
'shared': gettext('Shared'),
|
||||
'community': gettext('Community')
|
||||
};
|
||||
|
||||
// Mapping for dynamic table data
|
||||
var tableBodyCellsMap = {
|
||||
@ -171,14 +175,14 @@
|
||||
{ key: 'updated_at', filter: dateFilter, filterArg: 'short' },
|
||||
{ key: 'size', filter: bytesFilter, classList: ['number'] },
|
||||
{ key: 'disk_format', filter: diskFormatFilter, filterRawData: true },
|
||||
{ key: 'is_public', filter: decodeFilter, filterArg: _visibilitymap }
|
||||
{ key: 'visibility', filter: decodeFilter, filterArg: _visibilitymap }
|
||||
],
|
||||
snapshot: [
|
||||
{ key: 'name', classList: ['hi-light', 'word-break'] },
|
||||
{ key: 'updated_at', filter: dateFilter, filterArg: 'short' },
|
||||
{ key: 'size', filter: bytesFilter, classList: ['number'] },
|
||||
{ key: 'disk_format', filter: diskFormatFilter, filterRawData: true },
|
||||
{ key: 'is_public', filter: decodeFilter, filterArg: _visibilitymap }
|
||||
{ key: 'visibility', filter: decodeFilter, filterArg: _visibilitymap }
|
||||
],
|
||||
volume: [
|
||||
{ key: 'name', classList: ['hi-light', 'word-break'] },
|
||||
@ -273,11 +277,13 @@
|
||||
},
|
||||
visibility: {
|
||||
label: gettext('Visibility'),
|
||||
name: 'is_public',
|
||||
name: 'visibility',
|
||||
singleton: true,
|
||||
options: [
|
||||
{ label: gettext('Public'), key: 'true' },
|
||||
{ label: gettext('Private'), key: 'false' }
|
||||
{ label: gettext('Public'), key: 'public' },
|
||||
{ label: gettext('Private'), key: 'private' },
|
||||
{ label: gettext('Shared With Project'), key: 'shared' },
|
||||
{ label: gettext('Community'), key: 'community' }
|
||||
]
|
||||
},
|
||||
volumeType: {
|
||||
|
@ -154,7 +154,7 @@
|
||||
expect(ctrl.sourceFacets[1].name).toEqual('updated_at');
|
||||
expect(ctrl.sourceFacets[2].name).toEqual('size');
|
||||
expect(ctrl.sourceFacets[3].name).toEqual('disk_format');
|
||||
expect(ctrl.sourceFacets[4].name).toEqual('is_public');
|
||||
expect(ctrl.sourceFacets[4].name).toEqual('visibility');
|
||||
});
|
||||
|
||||
it('should broadcast event when source type is changed', function() {
|
||||
@ -174,7 +174,7 @@
|
||||
expect(ctrl.sourceFacets[1].name).toEqual('updated_at');
|
||||
expect(ctrl.sourceFacets[2].name).toEqual('size');
|
||||
expect(ctrl.sourceFacets[3].name).toEqual('disk_format');
|
||||
expect(ctrl.sourceFacets[4].name).toEqual('is_public');
|
||||
expect(ctrl.sourceFacets[4].name).toEqual('visibility');
|
||||
});
|
||||
|
||||
it('should change facets for volume source type', function() {
|
||||
|
@ -119,6 +119,8 @@
|
||||
function deriveSharingStatus(image, currentProjectId, translatedVisibility) {
|
||||
if (angular.equals(translatedVisibility, imageVisibility.public)) {
|
||||
return translatedVisibility;
|
||||
} else if (angular.equals(translatedVisibility, imageVisibility.community)) {
|
||||
return translatedVisibility;
|
||||
} else if (angular.isDefined(currentProjectId) &&
|
||||
!angular.equals(image.owner, currentProjectId)) {
|
||||
return imageVisibility.other;
|
||||
|
@ -186,6 +186,22 @@ def data(TEST):
|
||||
'min_ram': 0}
|
||||
private_image3 = images.Image(images.ImageManager(None), image_dict)
|
||||
|
||||
# A community image. Not public and not local tenant, but visibility
|
||||
# is set as 'community'
|
||||
image_dict = {'id': '13682b34-fb85-4fe5-bd65-1e16305b45c9',
|
||||
'name': 'community_image 1',
|
||||
'status': "active",
|
||||
'size': 10 * 1024 ** 3,
|
||||
'virtual_size': None,
|
||||
'min_disk': 0,
|
||||
'owner': 'someothertenant',
|
||||
'container_format': 'aki',
|
||||
'is_public': False,
|
||||
'protected': False,
|
||||
'min_ram': 0,
|
||||
'visibility': 'community'}
|
||||
community_image = images.Image(images.ImageManager(None), image_dict)
|
||||
|
||||
# A shared image. Not public and not local tenant.
|
||||
image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849',
|
||||
'name': 'shared_image 1',
|
||||
@ -244,7 +260,8 @@ def data(TEST):
|
||||
|
||||
TEST.images_api.add(public_image, private_image, protected_image,
|
||||
public_image2, private_image2, private_image3,
|
||||
shared_image1, official_image1, multi_prop_image)
|
||||
community_image, shared_image1, official_image1,
|
||||
multi_prop_image)
|
||||
|
||||
TEST.images.add(api.glance.Image(public_image),
|
||||
api.glance.Image(private_image),
|
||||
@ -252,6 +269,7 @@ def data(TEST):
|
||||
api.glance.Image(public_image2),
|
||||
api.glance.Image(private_image2),
|
||||
api.glance.Image(private_image3),
|
||||
api.glance.Image(community_image),
|
||||
api.glance.Image(shared_image1),
|
||||
api.glance.Image(official_image1),
|
||||
api.glance.Image(multi_prop_image))
|
||||
|
@ -191,7 +191,7 @@ class GlanceApiTests(test.APIMockTestCase):
|
||||
self.assertFalse(has_more)
|
||||
self.assertFalse(has_prev)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=9)
|
||||
@override_settings(API_RESULT_PAGE_SIZE=10)
|
||||
@mock.patch.object(api.glance, 'glanceclient')
|
||||
def test_image_list_detailed_pagination_equal_page_size(self,
|
||||
mock_glanceclient):
|
||||
|
Loading…
x
Reference in New Issue
Block a user