diff options
Diffstat (limited to 'openstack_dashboard/dashboards')
7 files changed, 379 insertions, 96 deletions
diff --git a/openstack_dashboard/dashboards/project/images/images/forms.py b/openstack_dashboard/dashboards/project/images/images/forms.py index 0105a116f..1c3a44999 100644 --- a/openstack_dashboard/dashboards/project/images/images/forms.py +++ b/openstack_dashboard/dashboards/project/images/images/forms.py @@ -65,25 +65,34 @@ def create_image_metadata(data): else: container_format = 'bare' - # The Create form uses 'is_public' but the Update form uses 'public'. Just - # being tolerant here so we don't break anything else. - meta = {'is_public': data.get('is_public', data.get('public', False)), - 'protected': data['protected'], + meta = {'protected': data['protected'], 'disk_format': disk_format, 'container_format': container_format, 'min_disk': (data['minimum_disk'] or 0), 'min_ram': (data['minimum_ram'] or 0), - 'name': data['name'], - 'properties': {}} - - if 'description' in data: - meta['properties']['description'] = data['description'] + 'name': data['name']} + + is_public = data.get('is_public', data.get('public', False)) + properties = {} + # NOTE(tsufiev): in V2 the way how empty non-base attributes (AKA metadata) + # are handled has changed: in V2 empty metadata is kept in image + # properties, while in V1 they were omitted. Skip empty description (which + # is metadata) to keep the same behavior between V1 and V2 + if data.get('description'): + properties['description'] = data['description'] if data.get('kernel'): - meta['properties']['kernel_id'] = data['kernel'] + properties['kernel_id'] = data['kernel'] if data.get('ramdisk'): - meta['properties']['ramdisk_id'] = data['ramdisk'] + properties['ramdisk_id'] = data['ramdisk'] if data.get('architecture'): - meta['properties']['architecture'] = data['architecture'] + properties['architecture'] = data['architecture'] + + if api.glance.VERSIONS.active < 2: + meta.update({'is_public': is_public, 'properties': properties}) + else: + meta['visibility'] = 'public' if is_public else 'private' + meta.update(properties) + return meta @@ -195,6 +204,24 @@ class CreateImageForm(CreateParent): self._hide_file_source_type() if not policy.check((("image", "set_image_location"),), request): self._hide_url_source_type() + + # GlanceV2 feature removals + if api.glance.VERSIONS.active >= 2: + # NOTE: GlanceV2 doesn't support copy-from feature, sorry! + self._hide_is_copying() + if not getattr(settings, 'IMAGES_ALLOW_LOCATION', False): + self._hide_url_source_type() + if (api.glance.get_image_upload_mode() == 'off' or not + policy.check((("image", "upload_image"),), request)): + # Neither setting a location nor uploading image data is + # allowed, so throw an error. + msg = _('The current Horizon settings indicate no valid ' + 'image creation methods are available. Providing ' + 'an image location and/or uploading from the ' + 'local file system must be allowed to support ' + 'image creation.') + messages.error(request, msg) + raise ValidationError(msg) if not policy.check((("image", "publicize_image"),), request): self._hide_is_public() @@ -252,6 +279,10 @@ class CreateImageForm(CreateParent): self.fields['is_public'].widget = HiddenInput() self.fields['is_public'].initial = False + def _hide_is_copying(self): + self.fields['is_copying'].widget = HiddenInput() + self.fields['is_copying'].initial = False + def clean(self): data = super(CreateImageForm, self).clean() @@ -278,7 +309,7 @@ class CreateImageForm(CreateParent): policy.check((("image", "upload_image"),), request) and data.get('image_file', None)): meta['data'] = data['image_file'] - elif data['is_copying']: + elif data.get('is_copying'): meta['copy_from'] = data['image_url'] else: meta['location'] = data['image_url'] @@ -369,9 +400,6 @@ class UpdateImageForm(forms.SelfHandlingForm): image_id = data['image_id'] error_updating = _('Unable to update image "%s".') meta = create_image_metadata(data) - # Ensure we do not delete properties that have already been - # set on an image. - meta['purge_props'] = False try: image = api.glance.image_update(request, image_id, **meta) diff --git a/openstack_dashboard/dashboards/project/images/images/tables.py b/openstack_dashboard/dashboards/project/images/images/tables.py index df18973fd..e5e10b4fd 100644 --- a/openstack_dashboard/dashboards/project/images/images/tables.py +++ b/openstack_dashboard/dashboards/project/images/images/tables.py @@ -206,8 +206,14 @@ class OwnerFilter(tables.FixedFilterAction): new_dict = button_dict.copy() new_dict['value'] = new_dict['tenant'] buttons.append(new_dict) - buttons.append(make_dict(_('Shared with Project'), 'shared', - 'fa-share-square-o')) + # FIXME(bpokorny): Remove this check once admins can list images with + # GlanceV2 without getting all images in the whole cloud. + if api.glance.VERSIONS.active >= 2: + buttons.append(make_dict(_('Non-Public from Other Projects'), + 'other', 'fa-group')) + else: + buttons.append(make_dict(_('Shared with Project'), 'shared', + 'fa-share-square-o')) buttons.append(make_dict(_('Public'), 'public', 'fa-group')) return buttons @@ -223,14 +229,15 @@ class OwnerFilter(tables.FixedFilterAction): def get_image_categories(im, user_tenant_id): categories = [] - if api.glance.is_image_public(im): + if im.is_public: categories.append('public') if im.owner == user_tenant_id: categories.append('project') elif im.owner in filter_tenant_ids(): categories.append(im.owner) - elif not api.glance.is_image_public(im): + elif not im.is_public: categories.append('shared') + categories.append('other') return categories diff --git a/openstack_dashboard/dashboards/project/images/images/tests.py b/openstack_dashboard/dashboards/project/images/images/tests.py index ef1f336d5..df0e21699 100644 --- a/openstack_dashboard/dashboards/project/images/images/tests.py +++ b/openstack_dashboard/dashboards/project/images/images/tests.py @@ -38,7 +38,7 @@ from openstack_dashboard.dashboards.project.images.images import tables IMAGES_INDEX_URL = reverse('horizon:project:images:index') -class CreateImageFormTests(test.TestCase): +class CreateImageFormTests(test.ResetImageAPIVersionMixin, test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',)}) def test_no_location_or_file(self): filters = {'disk_format': 'aki'} @@ -64,6 +64,7 @@ class CreateImageFormTests(test.TestCase): self.assertFalse(form.is_valid()) @override_settings(HORIZON_IMAGES_ALLOW_UPLOAD=False) + @override_settings(IMAGES_ALLOW_LOCATION=True) @test.create_stubs({api.glance: ('image_list_detailed',)}) def test_image_upload_disabled(self): filters = {'disk_format': 'aki'} @@ -81,7 +82,8 @@ class CreateImageFormTests(test.TestCase): source_type_dict = dict(form.fields['source_type'].choices) self.assertNotIn('file', source_type_dict) - def test_create_image_metadata_docker(self): + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_create_image_metadata_docker_v1(self): form_data = { 'name': u'Docker image', 'description': u'Docker image test', @@ -106,8 +108,29 @@ class CreateImageFormTests(test.TestCase): self.assertEqual(meta['properties']['architecture'], form_data['architecture']) + def test_create_image_metadata_docker_v2(self): + form_data = { + 'name': u'Docker image', + 'description': u'Docker image test', + 'source_type': u'url', + 'image_url': u'/', + 'disk_format': u'docker', + 'architecture': u'x86-64', + 'minimum_disk': 15, + 'minimum_ram': 512, + 'is_public': False, + 'protected': False, + 'is_copying': False + } + meta = forms.create_image_metadata(form_data) + self.assertEqual(meta['disk_format'], 'raw') + self.assertEqual(meta['container_format'], 'docker') + self.assertNotIn('properties', meta) + self.assertEqual(meta['description'], form_data['description']) + self.assertEqual(meta['architecture'], form_data['architecture']) + -class UpdateImageFormTests(test.TestCase): +class UpdateImageFormTests(test.ResetImageAPIVersionMixin, test.TestCase): def test_is_format_field_editable(self): form = forms.UpdateImageForm({}) disk_format = form.fields['disk_format'] @@ -127,8 +150,9 @@ class UpdateImageFormTests(test.TestCase): self.assertEqual(res.context['image'].disk_format, image.disk_format) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) @test.create_stubs({api.glance: ('image_update', 'image_get')}) - def test_image_update_post(self): + def test_image_update_post_v1(self): image = self.images.first() data = { 'name': u'Ubuntu 11.10', @@ -156,10 +180,10 @@ class UpdateImageFormTests(test.TestCase): name=data['name'], min_ram=data['minimum_ram'], min_disk=data['minimum_disk'], - properties={'description': data['description'], - 'architecture': - data['architecture']}, - purge_props=False).AndReturn(image) + properties={ + 'description': data['description'], + 'architecture': + data['architecture']}).AndReturn(image) self.mox.ReplayAll() url = reverse('horizon:project:images:images:update', args=[image.id]) @@ -167,8 +191,47 @@ class UpdateImageFormTests(test.TestCase): self.assertNoFormErrors(res) self.assertEqual(res.status_code, 302) + @test.create_stubs({api.glance: ('image_update', 'image_get')}) + def test_image_update_post_v2(self): + image = self.images.first() + data = { + 'name': u'Ubuntu 11.10', + 'image_id': str(image.id), + 'description': u'Login with admin/admin', + 'source_type': u'url', + 'image_url': u'http://cloud-images.ubuntu.com/releases/' + u'oneiric/release/ubuntu-11.10-server-cloudimg' + u'-amd64-disk1.img', + 'disk_format': u'qcow2', + 'architecture': u'x86-64', + 'minimum_disk': 15, + 'minimum_ram': 512, + 'is_public': False, + 'protected': False, + 'method': 'UpdateImageForm'} + api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \ + .AndReturn(image) + api.glance.image_update(IsA(http.HttpRequest), + image.id, + visibility='private', + protected=data['protected'], + disk_format=data['disk_format'], + container_format="bare", + name=data['name'], + min_ram=data['minimum_ram'], + min_disk=data['minimum_disk'], + description=data['description'], + architecture=data['architecture']).\ + AndReturn(image) + self.mox.ReplayAll() + url = reverse('horizon:project:images:images:update', + args=[image.id]) + res = self.client.post(url, data) + self.assertNoFormErrors(res) + self.assertEqual(res.status_code, 302) -class ImageViewTests(test.TestCase): + +class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',)}) def test_image_create_get(self): filters = {'disk_format': 'aki'} @@ -186,8 +249,9 @@ class ImageViewTests(test.TestCase): self.assertTemplateUsed(res, 'project/images/images/create.html') + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) @test.create_stubs({api.glance: ('image_create',)}) - def test_image_create_post_copy_from(self): + def test_image_create_post_copy_from_v1(self): data = { 'source_type': u'url', 'image_url': u'http://cloud-images.ubuntu.com/releases/' @@ -198,8 +262,9 @@ class ImageViewTests(test.TestCase): api_data = {'copy_from': data['image_url']} self._test_image_create(data, api_data) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) @test.create_stubs({api.glance: ('image_create',)}) - def test_image_create_post_location(self): + def test_image_create_post_location_v1(self): data = { 'source_type': u'url', 'image_url': u'http://cloud-images.ubuntu.com/releases/' @@ -210,8 +275,34 @@ class ImageViewTests(test.TestCase): api_data = {'location': data['image_url']} self._test_image_create(data, api_data) + @override_settings(IMAGES_ALLOW_LOCATION=True) + @test.create_stubs({api.glance: ('image_create',)}) + def test_image_create_post_location_v2(self): + data = { + 'source_type': u'url', + 'image_url': u'http://cloud-images.ubuntu.com/releases/' + u'oneiric/release/ubuntu-11.10-server-cloudimg' + u'-amd64-disk1.img'} + + api_data = {'location': data['image_url']} + self._test_image_create(data, api_data) + + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + @test.create_stubs({api.glance: ('image_create',)}) + def test_image_create_post_upload_v1(self): + temp_file = tempfile.NamedTemporaryFile() + temp_file.write(b'123') + temp_file.flush() + temp_file.seek(0) + + data = {'source_type': u'file', + 'image_file': temp_file} + + api_data = {'data': IsA(InMemoryUploadedFile)} + self._test_image_create(data, api_data) + @test.create_stubs({api.glance: ('image_create',)}) - def test_image_create_post_upload(self): + def test_image_create_post_upload_v2(self): temp_file = tempfile.NamedTemporaryFile() temp_file.write(b'123') temp_file.flush() @@ -223,8 +314,26 @@ class ImageViewTests(test.TestCase): api_data = {'data': IsA(InMemoryUploadedFile)} self._test_image_create(data, api_data) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) @test.create_stubs({api.glance: ('image_create',)}) - def test_image_create_post_with_kernel_ramdisk(self): + def test_image_create_post_with_kernel_ramdisk_v1(self): + temp_file = tempfile.NamedTemporaryFile() + temp_file.write(b'123') + temp_file.flush() + temp_file.seek(0) + + data = { + 'source_type': u'file', + 'image_file': temp_file, + 'kernel_id': '007e7d55-fe1e-4c5c-bf08-44b4a496482e', + 'ramdisk_id': '007e7d55-fe1e-4c5c-bf08-44b4a496482a' + } + + api_data = {'data': IsA(InMemoryUploadedFile)} + self._test_image_create(data, api_data) + + @test.create_stubs({api.glance: ('image_create',)}) + def test_image_create_post_with_kernel_ramdisk_v2(self): temp_file = tempfile.NamedTemporaryFile() temp_file.write(b'123') temp_file.flush() @@ -256,14 +365,22 @@ class ImageViewTests(test.TestCase): api_data = {'container_format': 'bare', 'disk_format': data['disk_format'], - 'is_public': True, 'protected': False, 'min_disk': data['minimum_disk'], 'min_ram': data['minimum_ram'], - 'properties': { - 'description': data['description'], - 'architecture': data['architecture']}, 'name': data['name']} + if api.glance.VERSIONS.active < 2: + api_data.update({'is_public': True, + 'properties': { + 'description': data['description'], + 'architecture': data['architecture']} + }) + else: + api_data.update({'visibility': 'public', + 'description': data['description'], + 'architecture': data['architecture'] + }) + api_data.update(extra_api_data) filters = {'disk_format': 'aki'} @@ -286,12 +403,9 @@ class ImageViewTests(test.TestCase): self.assertNoFormErrors(res) self.assertEqual(res.status_code, 302) - @test.create_stubs({api.glance: ('image_get',)}) - def test_image_detail_get(self): - image = self.images.first() - + def _test_image_detail_get(self, image): api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \ - .AndReturn(self.images.first()) + .AndReturn(image) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:images:images:detail', @@ -302,10 +416,20 @@ class ImageViewTests(test.TestCase): self.assertEqual(res.context['image'].name, image.name) self.assertEqual(res.context['image'].protected, image.protected) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) @test.create_stubs({api.glance: ('image_get',)}) - def test_image_detail_custom_props_get(self): - image = self.images.list()[8] + def test_image_detail_get_v1(self): + image = self.images.first() + self._test_image_detail_get(image) + + @test.create_stubs({api.glance: ('image_get',)}) + def test_image_detail_get_v2(self): + image = self.imagesV2.first() + + self._test_image_detail_get(image) + + def _test_image_detail_custom_props_get(self, image): api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \ .AndReturn(image) self.mox.ReplayAll() @@ -320,8 +444,8 @@ class ImageViewTests(test.TestCase): self.assertNotIn(('description'), image_keys) # Test custom properties are sorted - self.assertEqual(image_props[0], ('bar', 'bar', 'bar val')) - self.assertEqual(image_props[1], ('foo', 'foo', 'foo val')) + self.assertLess(image_props.index(('bar', 'bar', 'bar val')), + image_props.index(('foo', 'foo', 'foo val'))) # Test all custom properties appear in template self.assertContains(res, '<dt title="bar">bar</dt>') @@ -329,10 +453,20 @@ class ImageViewTests(test.TestCase): self.assertContains(res, '<dt title="foo">foo</dt>') self.assertContains(res, '<dd>foo val</dd>') + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) @test.create_stubs({api.glance: ('image_get',)}) - def test_protected_image_detail_get(self): - image = self.images.list()[2] + def test_image_detail_custom_props_get_v1(self): + image = self.images.list()[8] + + self._test_image_detail_custom_props_get(image) + + @test.create_stubs({api.glance: ('image_get',)}) + def test_image_detail_custom_props_get_v2(self): + image = self.imagesV2.list()[2] + self._test_image_detail_custom_props_get(image) + + def _test_protected_image_detail_get(self, image): api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \ .AndReturn(image) self.mox.ReplayAll() @@ -344,6 +478,19 @@ class ImageViewTests(test.TestCase): 'horizon/common/_detail.html') self.assertEqual(res.context['image'].protected, image.protected) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + @test.create_stubs({api.glance: ('image_get',)}) + def test_protected_image_detail_get_v1(self): + image = self.images.list()[2] + + self._test_protected_image_detail_get(image) + + @test.create_stubs({api.glance: ('image_get',)}) + def test_protected_image_detail_get_v2(self): + image = self.imagesV2.list()[1] + + self._test_protected_image_detail_get(image) + @test.create_stubs({api.glance: ('image_get',)}) def test_image_detail_get_with_exception(self): image = self.images.first() @@ -359,9 +506,8 @@ class ImageViewTests(test.TestCase): @test.create_stubs({api.glance: ('image_get',)}) def test_image_update_get(self): - image = self.images.first() - image.disk_format = "ami" - image.is_public = True + image = self.images.filter(is_public=True)[0] + api.glance.image_get(IsA(http.HttpRequest), str(image.id)) \ .AndReturn(image) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/images/images/views.py b/openstack_dashboard/dashboards/project/images/images/views.py index 853a6ab7d..1fafc3871 100644 --- a/openstack_dashboard/dashboards/project/images/images/views.py +++ b/openstack_dashboard/dashboards/project/images/images/views.py @@ -19,6 +19,7 @@ """ Views for managing images. """ +from django.conf import settings from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ @@ -71,6 +72,9 @@ class CreateView(forms.ModalFormView): context = super(CreateView, self).get_context_data(**kwargs) upload_mode = api.glance.get_image_upload_mode() context['image_upload_enabled'] = upload_mode != 'off' + context['images_allow_location'] = getattr(settings, + 'IMAGES_ALLOW_LOCATION', + False) return context diff --git a/openstack_dashboard/dashboards/project/images/templates/images/images/_create.html b/openstack_dashboard/dashboards/project/images/templates/images/images/_create.html index e3c471a93..d9c425404 100644 --- a/openstack_dashboard/dashboards/project/images/templates/images/images/_create.html +++ b/openstack_dashboard/dashboards/project/images/templates/images/images/_create.html @@ -8,18 +8,22 @@ {% block modal-body-right %} <h3>{% trans "Description:" %}</h3> <p> - {% if image_upload_enabled %} + {% if image_upload_enabled and images_allow_location %} {% trans "Images can be provided via an HTTP/HTTPS URL or be uploaded from your local file system." %} + {% elif image_upload_enabled %} + {% trans "Currently only images uploaded from your local file system are supported." %} {% else %} {% trans "Currently only images available via an HTTP/HTTPS URL are supported. The image location must be accessible to the Image Service." %} {% endif %} </p> <p> - <strong>{% trans "Please note: " %}</strong> - {% if image_upload_enabled %} - {% trans "If you select an image via an HTTP/HTTPS URL, the Image Location field MUST be a valid and direct URL to the image binary; it must also be accessible to the Image Service. URLs that redirect or serve error pages will result in unusable images." %} - {% else %} - {% trans "The Image Location field MUST be a valid and direct URL to the image binary. URLs that redirect or serve error pages will result in unusable images." %} + {% if images_allow_location %} + <strong>{% trans "Please note: " %}</strong> + {% if image_upload_enabled %} + {% trans "If you select an image via an HTTP/HTTPS URL, the Image Location field MUST be a valid and direct URL to the image binary; it must also be accessible to the Image Service. URLs that redirect or serve error pages will result in unusable images." %} + {% else %} + {% trans "The Image Location field MUST be a valid and direct URL to the image binary. URLs that redirect or serve error pages will result in unusable images." %} + {% endif %} {% endif %} </p> {% endblock %} diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 6283f9059..8794499a5 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -28,6 +28,7 @@ from django.core.urlresolvers import reverse from django.forms import widgets from django import http import django.test +from django.test.utils import override_settings from django.utils.http import urlencode from mox3.mox import IgnoreArg # noqa from mox3.mox import IsA # noqa @@ -54,7 +55,14 @@ VOLUME_SEARCH_OPTS = dict(status=AVAILABLE, bootable=True) SNAPSHOT_SEARCH_OPTS = dict(status=AVAILABLE) -class InstanceTests(helpers.TestCase): +class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase): + def setUp(self): + super(InstanceTests, self).setUp() + if api.glance.VERSIONS.active < 2: + self.versioned_images = self.images + else: + self.versioned_images = self.imagesV2 + @helpers.create_stubs({ api.nova: ( 'flavor_list', @@ -1511,7 +1519,7 @@ class InstanceTests(helpers.TestCase): config_drive=True, config_drive_default=False, test_with_profile=False): - image = self.images.first() + image = self.versioned_images.first() api.nova.extension_supported('BlockDeviceMappingV2Boot', IsA(http.HttpRequest)) \ @@ -1525,7 +1533,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -1674,6 +1682,10 @@ class InstanceTests(helpers.TestCase): self.assertEqual(step.action.initial['config_drive'], config_drive_default) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_instance_get_glance_v1(self): + self.test_launch_instance_get() + @django.test.utils.override_settings( OPENSTACK_HYPERVISOR_FEATURES={'can_set_password': False}) def test_launch_instance_get_without_password(self): @@ -1777,7 +1789,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -1853,6 +1865,10 @@ class InstanceTests(helpers.TestCase): for volume in bootable_volumes: self.assertIn(volume, volume_sources_ids) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_instance_get_bootable_volumes_glance_v1(self): + self.test_launch_instance_get_bootable_volumes() + @helpers.update_settings( OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'}) def test_launch_instance_get_bootable_volumes_with_profile(self): @@ -1879,7 +1895,7 @@ class InstanceTests(helpers.TestCase): test_with_profile=False, test_with_multi_nics=False): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() keypair = self.keypairs.first() server = self.servers.first() sec_group = self.security_groups.first() @@ -1903,7 +1919,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -2024,6 +2040,10 @@ class InstanceTests(helpers.TestCase): self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_instance_post_glance_v1(self): + self.test_launch_instance_post() + def test_launch_instance_post_no_disk_config_supported(self): self.test_launch_instance_post(disk_config=False) @@ -2046,7 +2066,7 @@ class InstanceTests(helpers.TestCase): test_with_multi_nics=False, ): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() keypair = self.keypairs.first() server = self.servers.first() sec_group = self.security_groups.first() @@ -2068,7 +2088,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -2181,6 +2201,10 @@ class InstanceTests(helpers.TestCase): def test_launch_instance_post_with_profile_and_port_error(self): self._test_launch_instance_post_with_profile_and_port_error() + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_instance_post_with_profile_and_port_error_glance_v1(self): + self.test_launch_instance_post_with_profile_and_port_error() + @helpers.update_settings( OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'}) @helpers.create_stubs({api.glance: ('image_list_detailed',), @@ -2263,7 +2287,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -2367,6 +2391,10 @@ class InstanceTests(helpers.TestCase): self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_instance_post_boot_from_volume_glance_v1(self): + self.test_launch_instance_post_boot_from_volume() + def test_launch_instance_post_boot_from_volume_with_bdmv2(self): self.test_launch_instance_post_boot_from_volume(test_with_bdmv2=True) @@ -2422,7 +2450,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -2527,6 +2555,10 @@ class InstanceTests(helpers.TestCase): self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_lnch_inst_post_no_images_avail_boot_from_volume_glance_v1(self): + self.test_launch_instance_post_no_images_available_boot_from_volume() + @helpers.update_settings( OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'}) def test_lnch_inst_post_no_images_avail_boot_from_vol_with_profile(self): @@ -2712,7 +2744,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -2817,6 +2849,10 @@ class InstanceTests(helpers.TestCase): self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_instance_post_boot_from_snapshot_glance_v1(self): + self.test_launch_instance_post_boot_from_snapshot() + def test_launch_instance_post_boot_from_snapshot_with_bdmv2(self): self.test_launch_instance_post_boot_from_snapshot(test_with_bdmv2=True) @@ -2945,7 +2981,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -3000,6 +3036,10 @@ class InstanceTests(helpers.TestCase): self.assertTemplateUsed(res, views.WorkflowView.template_name) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_flavorlist_error_glance_v1(self): + self.test_launch_flavorlist_error() + @helpers.update_settings( OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'}) def test_launch_flavorlist_error_with_profile(self): @@ -3024,7 +3064,7 @@ class InstanceTests(helpers.TestCase): def test_launch_form_keystone_exception(self, test_with_profile=False): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() keypair = self.keypairs.first() server = self.servers.first() sec_group = self.security_groups.first() @@ -3055,7 +3095,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -3151,6 +3191,10 @@ class InstanceTests(helpers.TestCase): self.assertRedirectsNoFollow(res, INDEX_URL) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_form_keystone_exception_with_profile_glance_v1(self): + self.test_launch_form_keystone_exception() + @helpers.update_settings( OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'}) def test_launch_form_keystone_exception_with_profile(self): @@ -3172,7 +3216,7 @@ class InstanceTests(helpers.TestCase): def test_launch_form_instance_count_error(self, test_with_profile=False): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() keypair = self.keypairs.first() server = self.servers.first() volume = self.volumes.first() @@ -3197,7 +3241,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -3273,6 +3317,10 @@ class InstanceTests(helpers.TestCase): self.assertContains(res, "greater than or equal to 1") + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_form_instance_count_error_glance_v1(self): + self.test_launch_form_instance_count_error() + @helpers.create_stubs({api.glance: ('image_list_detailed',), api.neutron: ('network_list', 'profile_list', @@ -3290,7 +3338,7 @@ class InstanceTests(helpers.TestCase): def _test_launch_form_count_error(self, resource, avail, test_with_profile=False): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() keypair = self.keypairs.first() server = self.servers.first() volume = self.volumes.first() @@ -3320,7 +3368,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -3407,7 +3455,11 @@ class InstanceTests(helpers.TestCase): "512, Requested: 1024)" % {'avail': avail}) self.assertContains(res, msg) - def test_launch_form_cores_count_error(self): + def test_launch_form_cores_count_error_glance_v2(self): + self._test_launch_form_count_error('cores', 1, test_with_profile=False) + + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_form_cores_count_error_glance_v1(self): self._test_launch_form_count_error('cores', 1, test_with_profile=False) def test_launch_form_ram_count_error(self): @@ -3461,7 +3513,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -3547,18 +3599,22 @@ class InstanceTests(helpers.TestCase): test_with_profile=False, ): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() image.min_ram = flavor.ram image.min_disk = flavor.disk + 1 self._test_launch_form_instance_requirement_error(image, flavor, test_with_profile) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_form_instance_requirement_error_disk_glance_v1(self): + self.test_launch_form_instance_requirement_error_disk() + def test_launch_form_instance_requirement_error_ram( self, test_with_profile=False, ): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() image.min_ram = flavor.ram + 1 image.min_disk = flavor.disk self._test_launch_form_instance_requirement_error(image, flavor, @@ -3593,7 +3649,7 @@ class InstanceTests(helpers.TestCase): widget_class, widget_attrs): flavor = self.flavors.first() - image = self.images.first() + image = self.versioned_images.first() keypair = self.keypairs.first() server = self.servers.first() volume = self.volumes.first() @@ -3617,7 +3673,7 @@ class InstanceTests(helpers.TestCase): IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}).AndReturn( - [self.images.list(), False, False]) + [self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -3692,8 +3748,8 @@ class InstanceTests(helpers.TestCase): for widget_part in widget_content.split(): self.assertContains(res, widget_part) - @django.test.utils.override_settings( - OPENSTACK_HYPERVISOR_FEATURES={'can_set_mount_point': True}) + @override_settings( + OPENSTACK_HYPERVISOR_FEATURES={'can_set_mount_point': True},) def test_launch_form_instance_device_name_showed(self): self._test_launch_form_instance_show_device_name( u'vda', widgets.TextInput, { @@ -3701,6 +3757,17 @@ class InstanceTests(helpers.TestCase): 'attrs': {'id': 'id_device_name'}} ) + @override_settings( + OPENSTACK_HYPERVISOR_FEATURES={'can_set_mount_point': True}, + OPENSTACK_API_VERSIONS={'image': 1} + ) + def test_launch_form_instance_device_name_showed_glance_v1(self): + self._test_launch_form_instance_show_device_name( + u'vda', widgets.TextInput, { + 'name': 'device_name', 'value': 'vda', + 'attrs': {'id': 'id_device_name'}} + ) + @django.test.utils.override_settings( OPENSTACK_HYPERVISOR_FEATURES={'can_set_mount_point': False}) def test_launch_form_instance_device_name_hidden(self): @@ -3754,7 +3821,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -3834,22 +3901,26 @@ class InstanceTests(helpers.TestCase): def test_launch_form_instance_volume_size_error(self, test_with_profile=False): - image = self.images.get(name='protected_images') + image = self.versioned_images.get(name='protected_images') volume_size = image.min_disk // 2 msg = ("The Volume size is too small for the '%s' image" % image.name) self._test_launch_form_instance_volume_size(image, volume_size, msg, test_with_profile) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_form_instance_volume_size_error_glance_v1(self): + self.test_launch_form_instance_volume_size_error() + def test_launch_form_instance_non_int_volume_size(self, test_with_profile=False): - image = self.images.get(name='protected_images') + image = self.versioned_images.get(name='protected_images') msg = "Enter a whole number." self._test_launch_form_instance_volume_size(image, 1.5, msg, test_with_profile) def test_launch_form_instance_volume_exceed_quota(self): - image = self.images.get(name='protected_images') + image = self.versioned_images.get(name='protected_images') msg = "Requested volume exceeds quota: Available: 0, Requested: 1" self._test_launch_form_instance_volume_size(image, image.min_disk, msg, False, 0) @@ -3975,7 +4046,7 @@ class InstanceTests(helpers.TestCase): quotas: ('tenant_quota_usages',)}) def test_launch_with_empty_device_name_allowed(self): flavor = self.flavors.get(name='m1.massive') - image = self.images.first() + image = self.versioned_images.first() keypair = self.keypairs.first() server = self.servers.first() sec_group = self.security_groups.first() @@ -4007,7 +4078,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -4094,6 +4165,10 @@ class InstanceTests(helpers.TestCase): res = self.client.post(url, form_data) self.assertNoFormErrors(res) + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_launch_with_empty_device_name_allowed_glance_v1(self): + self.test_launch_with_empty_device_name_allowed() + @helpers.create_stubs({ api.nova: ('flavor_list', 'server_list', 'tenant_absolute_limits', 'extension_supported',), @@ -4157,7 +4232,7 @@ class InstanceTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([self.versioned_images.list(), False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -4218,6 +4293,10 @@ class InstanceTests(helpers.TestCase): html=True, msg_prefix="The default key pair was not selected.") + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_select_default_keypair_if_only_one_glance_v1(self): + self.test_select_default_keypair_if_only_one() + @helpers.update_settings( OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'}) def test_select_default_keypair_if_only_one_with_profile(self): @@ -4945,7 +5024,7 @@ class InstanceAjaxTests(helpers.TestCase): self.assertContains(res, "Not available") -class ConsoleManagerTests(helpers.TestCase): +class ConsoleManagerTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase): def setup_consoles(self): # Need to refresh with mocks or will fail since mox do not detect @@ -5266,9 +5345,8 @@ class ConsoleManagerTests(helpers.TestCase): cinder: ('volume_list', 'volume_snapshot_list',), quotas: ('tenant_quota_usages',)}) - def test_port_cleanup_called_on_failed_vm_launch(self): + def _test_port_cleanup_called_on_failed_vm_launch(self, image, images): flavor = self.flavors.first() - image = self.images.first() keypair = self.keypairs.first() server = self.servers.first() sec_group = self.security_groups.first() @@ -5298,7 +5376,7 @@ class ConsoleManagerTests(helpers.TestCase): api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ - .AndReturn([self.images.list(), False, False]) + .AndReturn([images, False, False]) api.glance.image_list_detailed( IsA(http.HttpRequest), filters={'property-owner_id': self.tenant.id, @@ -5390,3 +5468,14 @@ class ConsoleManagerTests(helpers.TestCase): res = self.client.post(url, form_data) self.assertRedirectsNoFollow(res, INDEX_URL) + + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_port_cleanup_called_on_failed_vm_launch_v1(self): + image = self.images.first() + images = self.images.list() + self._test_port_cleanup_called_on_failed_vm_launch(image, images) + + def test_port_cleanup_called_on_failed_vm_launch_v2(self): + image = self.imagesV2.first() + images = self.imagesV2.list() + self._test_port_cleanup_called_on_failed_vm_launch(image, images) diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py index 778e88ba0..41b2ac060 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py @@ -40,7 +40,7 @@ VOLUME_VOLUMES_TAB_URL = urlunquote(reverse( SEARCH_OPTS = dict(status=api.cinder.VOLUME_STATE_AVAILABLE) -class VolumeViewTests(test.TestCase): +class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): @test.create_stubs({cinder: ('volume_create', 'volume_snapshot_list', 'volume_type_list', @@ -752,12 +752,17 @@ class VolumeViewTests(test.TestCase): image.min_disk = 30 self._test_create_volume_from_image_under_image_min_disk_size(image) - def test_create_volume_from_image_under_image_property_min_disk_size(self): + @override_settings(OPENSTACK_API_VERSIONS={'image': 1}) + def test_create_volume_from_image_under_image_prop_min_disk_size_v1(self): image = self.images.get(name="protected_images") image.min_disk = 0 image.properties['min_disk'] = 30 self._test_create_volume_from_image_under_image_min_disk_size(image) + def test_create_volume_from_image_under_image_prop_min_disk_size_v2(self): + image = self.imagesV2.get(name="protected_images") + self._test_create_volume_from_image_under_image_min_disk_size(image) + @test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list', 'volume_type_default', |