diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 4e24753900..2d199416c2 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -52,6 +52,7 @@ SEC_GROUP_ROLE_PREFIX = \ workflows.update_instance.INSTANCE_SEC_GROUP_SLUG + "_role_" AVAILABLE = api.cinder.VOLUME_STATE_AVAILABLE VOLUME_SEARCH_OPTS = dict(status=AVAILABLE, bootable=True) +VOLUME_BOOTABLE_SEARCH_OPTS = dict(bootable=True) SNAPSHOT_SEARCH_OPTS = dict(status=AVAILABLE) @@ -2058,9 +2059,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ConfigDrive': 1, 'ServerGroups': 1, }) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -2224,9 +2228,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ConfigDrive': 1, 'ServerGroups': 1, }) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -2332,9 +2339,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, }) self.mock_server_group_list.assert_called_once_with( helpers.IsHttpRequest()) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -2473,9 +2483,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ConfigDrive': 1, 'ServerGroups': 1, }) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -2594,9 +2607,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, }) self.mock_server_group_list.assert_called_once_with( helpers.IsHttpRequest()) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -2692,9 +2708,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, self._check_glance_image_list_detailed(count=5) self._check_neutron_network_and_port_list() self._check_nova_lists(flavor_count=3) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -2761,9 +2780,11 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ServerGroups': True, }) self.mock_server_group_list.return_value = [] + volumes = [v for v in self.cinder_volumes.list() + if (getattr(v, 'bootable', 'false') == 'true')] snapshots = [v for v in self.cinder_volume_snapshots.list() if (v.status == AVAILABLE)] - self.mock_volume_list.return_value = [] + self.mock_volume_list.return_value = volumes self.mock_volume_snapshot_list.return_value = snapshots self.mock_server_create.return_value = None self.mock_tenant_quota_usages.return_value = quota_usages @@ -2803,9 +2824,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, }) self.mock_server_group_list.assert_called_once_with( helpers.IsHttpRequest()) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -2970,9 +2994,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ConfigDrive': 1, 'ServerGroups': 1, }) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -3077,9 +3104,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ConfigDrive': 1, 'ServerGroups': 1, }) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -3190,9 +3220,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ConfigDrive': 1, 'ServerGroups': 1, }) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -3298,9 +3331,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, }) self.mock_server_group_list.assert_called_once_with( helpers.IsHttpRequest()) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -3393,9 +3429,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, 'ConfigDrive': 1, 'ServerGroups': 1, }) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -3582,9 +3621,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, network_id=net.id, tenant_id=self.tenant.id) for net in self.networks.list()]) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -3716,9 +3758,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, self._check_neutron_network_and_port_list() self.mock_server_group_list.assert_called_once_with( helpers.IsHttpRequest()) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -3960,9 +4005,12 @@ class InstanceLaunchInstanceTests(InstanceTestBase, self.mock_server_group_list.assert_called_once_with( helpers.IsHttpRequest()) - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), - search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) @@ -4084,8 +4132,12 @@ class InstanceTests2(InstanceTestBase, InstanceTableTestMixin): html=True, msg_prefix="The default key pair was not selected.") - self.mock_volume_list.assert_called_once_with( - helpers.IsHttpRequest(), search_opts=VOLUME_SEARCH_OPTS) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_SEARCH_OPTS), + mock.call(helpers.IsHttpRequest(), + search_opts=VOLUME_BOOTABLE_SEARCH_OPTS), + ]) self.mock_volume_snapshot_list.assert_called_once_with( helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS) self._check_glance_image_list_detailed(count=5) diff --git a/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py b/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py index bdb1cab576..91a920156e 100644 --- a/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py +++ b/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py @@ -489,9 +489,12 @@ class SetInstanceDetailsAction(workflows.Action): try: if cinder.is_volume_service_enabled(request): available = api.cinder.VOLUME_STATE_AVAILABLE + volumes = [v.id for v in cinder.volume_list( + self.request, search_opts=dict(bootable=True))] snapshots = [self._get_volume_display_name(s) for s in cinder.volume_snapshot_list( - self.request, search_opts=dict(status=available))] + self.request, search_opts=dict(status=available)) + if s.volume_id in volumes] except Exception: exceptions.handle(self.request, _('Unable to retrieve list of volume ' diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js index 20d6856268..e311d639a7 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js @@ -678,8 +678,20 @@ } function onGetVolumeSnapshots(data) { + cinderAPI.getVolumes({bootable: 1}).then(function (volumes) { + onGetBootableVolumeSnapshots(volumes.data.items, data.data.items); + }); + } + + function onGetBootableVolumeSnapshots(bootableVolumes, volumeSnapshots) { + var bootableVolumeIds = []; + bootableVolumes.forEach(function(volume) { + bootableVolumeIds.push(volume.id); + }); model.volumeSnapshots.length = 0; - push.apply(model.volumeSnapshots, data.data.items); + push.apply(model.volumeSnapshots, volumeSnapshots.filter(function (volumeSnapshot) { + return bootableVolumeIds.indexOf(volumeSnapshot.volume_id) !== -1; + })); addAllowedBootSource( model.volumeSnapshots, bootSourceTypes.VOLUME_SNAPSHOT, diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js index 715575ce82..e663b107b0 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js @@ -229,7 +229,9 @@ return deferred.promise; }, getVolumeSnapshots: function() { - var snapshots = [ { id: 'snap-1' }, { id: 'snap-2' } ]; + var snapshots = [ { id: 'snap-1', volume_id: 'vol-1' }, + { id: 'snap-2', volume_id: 'vol-2' }, + { id: 'snap-3', volume_id: 'vol-3' } ]; var deferred = $q.defer(); deferred.resolve({ data: { items: snapshots } }); @@ -682,7 +684,8 @@ expect(model.volumes.length).toBe(0); expect(model.volumes).toEqual([]); expect(model.volumeSnapshots.length).toBe(2); - expect(model.volumeSnapshots).toEqual([{ id: 'snap-1' }, { id: 'snap-2' }]); + expect(model.volumeSnapshots).toEqual([{ id: 'snap-1', volume_id: 'vol-1' }, + { id: 'snap-2', volume_id: 'vol-2' }]); expect(model.allowedBootSources).toBeDefined(); expect(model.allowedBootSources.length).toBe(3); expect(model.allowedBootSources).toContain(IMAGE);