Browse Source

Merge "Allow Launch Instance (Angular) from Volume Snapshots"

Jenkins 3 years ago
parent
commit
ec67db1a7f

+ 1
- 0
horizon/static/horizon/js/horizon.tabs.js View File

@@ -10,6 +10,7 @@ horizon.tabs.initTabLoad = function (tab) {
10 10
   $(horizon.tabs._init_load_functions).each(function (index, f) {
11 11
     f(tab);
12 12
   });
13
+  recompileAngularContent();
13 14
 };
14 15
 
15 16
 horizon.tabs.load_tab = function () {

+ 25
- 0
openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.js View File

@@ -362,6 +362,21 @@
362 362
       }
363 363
     );
364 364
 
365
+    var snapshotWatcher = $scope.$watchCollection(
366
+      function getSnapshots() {
367
+        return $scope.model.volumeSnapshots;
368
+      },
369
+      function onSnapshotsChange() {
370
+        $scope.initPromise.then(function onInit() {
371
+          $scope.$applyAsync(function setDefaultSnapshot() {
372
+            if ($scope.launchContext.snapshotId) {
373
+              setSourceSnapshotWithId($scope.launchContext.snapshotId);
374
+            }
375
+          });
376
+        });
377
+      }
378
+    );
379
+
365 380
     // Explicitly remove watchers on desruction of this controller
366 381
     $scope.$on('$destroy', function() {
367 382
       newSpecWatcher();
@@ -369,6 +384,7 @@
369 384
       bootSourceWatcher();
370 385
       imagesWatcher();
371 386
       volumeWatcher();
387
+      snapshotWatcher();
372 388
     });
373 389
 
374 390
     // Initialize
@@ -505,5 +521,14 @@
505 521
         ctrl.currentBootSource = ctrl.bootSourcesOptions[2].type;
506 522
       }
507 523
     }
524
+
525
+    function setSourceSnapshotWithId(id) {
526
+      var pre = findSourceById($scope.model.volumeSnapshots, id);
527
+      if (pre) {
528
+        changeBootSource(bootSourceTypes.VOLUME_SNAPSHOT, [pre]);
529
+        $scope.model.newInstanceSpec.source_type = ctrl.bootSourcesOptions[3];
530
+        ctrl.currentBootSource = ctrl.bootSourcesOptions[3].type;
531
+      }
532
+    }
508 533
   }
509 534
 })();

+ 14
- 3
openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.spec.js View File

@@ -51,7 +51,7 @@
51 51
           images: [ { id: 'image-1' }, { id: 'image-2' } ],
52 52
           imageSnapshots: [],
53 53
           volumes: [ { id: 'volume-1' }, { id: 'volume-2' } ],
54
-          volumeSnapshots: [],
54
+          volumeSnapshots: [ {id: 'snapshot-2'} ],
55 55
           novaLimits: {
56 56
             maxTotalInstances: 10,
57 57
             totalInstancesUsed: 0
@@ -200,15 +200,26 @@
200 200
         expect(ctrl.currentBootSource).toBe('volume');
201 201
       });
202 202
 
203
+      it('defaults source to snapshot-2 if launchContext.snapshotId = snapshot-2', function() {
204
+        scope.launchContext = { snapshotId: 'snapshot-2' };
205
+        deferred.resolve();
206
+
207
+        $browser.defer.flush();
208
+
209
+        expect(ctrl.tableData.allocated[0]).toEqual({ id: 'snapshot-2' });
210
+        expect(scope.model.newInstanceSpec.source_type.type).toBe('volume_snapshot');
211
+        expect(ctrl.currentBootSource).toBe('volume_snapshot');
212
+      });
213
+
203 214
       describe('Scope Functions', function() {
204 215
 
205 216
         describe('watchers', function () {
206 217
           it('establishes five watches', function() {
207
-            expect(scope.$watch.calls.count()).toBe(5);
218
+            expect(scope.$watch.calls.count()).toBe(6);
208 219
           });
209 220
 
210 221
           it("establishes two watch collections", function () {
211
-            expect(scope.$watchCollection.calls.count()).toBe(2);
222
+            expect(scope.$watchCollection.calls.count()).toBe(3);
212 223
           });
213 224
         });
214 225
 

+ 33
- 2
openstack_dashboard/dashboards/project/volumes/snapshots/tables.py View File

@@ -12,6 +12,7 @@
12 12
 #    License for the specific language governing permissions and limitations
13 13
 #    under the License.
14 14
 
15
+from django.conf import settings
15 16
 from django.core.urlresolvers import reverse
16 17
 from django.utils import html
17 18
 from django.utils.http import urlencode
@@ -48,6 +49,29 @@ class LaunchSnapshot(volume_tables.LaunchVolume):
48 49
         return False
49 50
 
50 51
 
52
+class LaunchSnapshotNG(LaunchSnapshot):
53
+    name = "launch_snapshot_ng"
54
+    verbose_name = _("Launch as Instance")
55
+    url = "horizon:project:volumes:snapshots_tab"
56
+    classes = ("btn-launch", )
57
+    ajax = False
58
+
59
+    def __init__(self, attrs=None, **kwargs):
60
+        kwargs['preempt'] = True
61
+        super(LaunchSnapshot, self).__init__(attrs, **kwargs)
62
+
63
+    def get_link_url(self, datum):
64
+        url = reverse(self.url)
65
+        vol_id = self.table.get_object_id(datum)
66
+        ngclick = "modal.openLaunchInstanceWizard(" \
67
+            "{successUrl: '%s', snapshotId: '%s'})" % (url, vol_id)
68
+        self.attrs.update({
69
+            "ng-controller": "LaunchInstanceModalController as modal",
70
+            "ng-click": ngclick
71
+        })
72
+        return "javascript:void(0);"
73
+
74
+
51 75
 class DeleteVolumeSnapshot(policy.PolicyTargetMixin, tables.DeleteAction):
52 76
     @staticmethod
53 77
     def action_present(count):
@@ -156,8 +180,15 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
156 180
         pagination_param = 'snapshot_marker'
157 181
         prev_pagination_param = 'prev_snapshot_marker'
158 182
         table_actions = (VolumeSnapshotsFilterAction, DeleteVolumeSnapshot,)
159
-        row_actions = (CreateVolumeFromSnapshot, LaunchSnapshot,
160
-                       EditVolumeSnapshot, DeleteVolumeSnapshot)
183
+
184
+        launch_actions = ()
185
+        if getattr(settings, 'LAUNCH_INSTANCE_LEGACY_ENABLED', False):
186
+            launch_actions = (LaunchSnapshot,) + launch_actions
187
+        if getattr(settings, 'LAUNCH_INSTANCE_NG_ENABLED', True):
188
+            launch_actions = (LaunchSnapshotNG,) + launch_actions
189
+
190
+        row_actions = ((CreateVolumeFromSnapshot,) + launch_actions +
191
+                       (EditVolumeSnapshot, DeleteVolumeSnapshot))
161 192
         row_class = UpdateRow
162 193
         status_columns = ("status",)
163 194
         permissions = [(

+ 16
- 3
openstack_dashboard/test/integration_tests/pages/project/compute/volumes/volumesnapshotspage.py View File

@@ -30,7 +30,14 @@ class VolumesnapshotsTable(tables.TableRegion):
30 30
         "name", "description", "snapshot_source", "type", "size")
31 31
 
32 32
     @tables.bind_table_action('delete')
33
-    def delete_volume_snapshot(self, delete_button):
33
+    def delete_volume_snapshots(self, delete_button):
34
+        """Batch Delete table action."""
35
+        delete_button.click()
36
+        return forms.BaseFormRegion(self.driver, self.conf)
37
+
38
+    @tables.bind_row_action('delete')
39
+    def delete_volume_snapshot(self, delete_button, row):
40
+        """Per-entity delete row action."""
34 41
         delete_button.click()
35 42
         return forms.BaseFormRegion(self.driver, self.conf)
36 43
 
@@ -77,8 +84,14 @@ class VolumesnapshotsPage(basepage.BaseNavigationPage):
77 84
 
78 85
     def delete_volume_snapshot(self, name):
79 86
         row = self._get_row_with_volume_snapshot_name(name)
80
-        row.mark()
81
-        confirm_form = self.volumesnapshots_table.delete_volume_snapshot()
87
+        confirm_form = self.volumesnapshots_table.delete_volume_snapshot(row)
88
+        confirm_form.submit()
89
+
90
+    def delete_volume_snapshots(self, names):
91
+        for name in names:
92
+            row = self._get_row_with_volume_snapshot_name(name)
93
+            row.mark()
94
+        confirm_form = self.volumesnapshots_table.delete_volume_snapshots()
82 95
         confirm_form.submit()
83 96
 
84 97
     def is_volume_snapshot_deleted(self, name):

+ 6
- 4
openstack_dashboard/test/integration_tests/tests/test_volume_snapshots.py View File

@@ -104,13 +104,14 @@ class TestVolumeSnapshotsBasic(helpers.TestCase):
104 104
         items_per_page = 1
105 105
         snapshot_names = ["{0}_{1}".format(self.VOLUME_SNAPSHOT_NAME, i) for i
106 106
                           in range(count)]
107
-        for name in snapshot_names:
107
+        for i, name in enumerate(snapshot_names):
108 108
             volumes_snapshot_page = volumes_page.create_volume_snapshot(
109 109
                 self.VOLUME_NAME, name)
110 110
             volumes_page.find_message_and_dismiss(messages.INFO)
111 111
             self.assertTrue(
112 112
                 volumes_snapshot_page.is_volume_snapshot_available(name))
113
-            volumes_snapshot_page.switch_to_volumes_tab()
113
+            if i < count - 1:
114
+                volumes_snapshot_page.switch_to_volumes_tab()
114 115
 
115 116
         first_page_definition = {'Next': True, 'Prev': False,
116 117
                                  'Count': items_per_page,
@@ -151,9 +152,10 @@ class TestVolumeSnapshotsBasic(helpers.TestCase):
151 152
         settings_page.find_message_and_dismiss(messages.SUCCESS)
152 153
 
153 154
         volumes_snapshot_page = self.volumes_snapshot_page
155
+        volumes_snapshot_page.delete_volume_snapshots(snapshot_names)
156
+        volumes_snapshot_page.find_message_and_dismiss(messages.SUCCESS)
154 157
         for name in snapshot_names:
155
-            volumes_snapshot_page.delete_volume_snapshot(name)
156
-            volumes_snapshot_page.find_message_and_dismiss(messages.SUCCESS)
158
+            volumes_snapshot_page.is_volume_snapshot_deleted(name)
157 159
 
158 160
     def tearDown(self):
159 161
         """Clean up: delete volume"""

Loading…
Cancel
Save