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

This commit is contained in:
Jenkins 2016-03-18 12:43:12 +00:00 committed by Gerrit Code Review
commit ec67db1a7f
6 changed files with 95 additions and 12 deletions

View File

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

View File

@ -362,6 +362,21 @@
} }
); );
var snapshotWatcher = $scope.$watchCollection(
function getSnapshots() {
return $scope.model.volumeSnapshots;
},
function onSnapshotsChange() {
$scope.initPromise.then(function onInit() {
$scope.$applyAsync(function setDefaultSnapshot() {
if ($scope.launchContext.snapshotId) {
setSourceSnapshotWithId($scope.launchContext.snapshotId);
}
});
});
}
);
// Explicitly remove watchers on desruction of this controller // Explicitly remove watchers on desruction of this controller
$scope.$on('$destroy', function() { $scope.$on('$destroy', function() {
newSpecWatcher(); newSpecWatcher();
@ -369,6 +384,7 @@
bootSourceWatcher(); bootSourceWatcher();
imagesWatcher(); imagesWatcher();
volumeWatcher(); volumeWatcher();
snapshotWatcher();
}); });
// Initialize // Initialize
@ -505,5 +521,14 @@
ctrl.currentBootSource = ctrl.bootSourcesOptions[2].type; ctrl.currentBootSource = ctrl.bootSourcesOptions[2].type;
} }
} }
function setSourceSnapshotWithId(id) {
var pre = findSourceById($scope.model.volumeSnapshots, id);
if (pre) {
changeBootSource(bootSourceTypes.VOLUME_SNAPSHOT, [pre]);
$scope.model.newInstanceSpec.source_type = ctrl.bootSourcesOptions[3];
ctrl.currentBootSource = ctrl.bootSourcesOptions[3].type;
}
}
} }
})(); })();

View File

@ -51,7 +51,7 @@
images: [ { id: 'image-1' }, { id: 'image-2' } ], images: [ { id: 'image-1' }, { id: 'image-2' } ],
imageSnapshots: [], imageSnapshots: [],
volumes: [ { id: 'volume-1' }, { id: 'volume-2' } ], volumes: [ { id: 'volume-1' }, { id: 'volume-2' } ],
volumeSnapshots: [], volumeSnapshots: [ {id: 'snapshot-2'} ],
novaLimits: { novaLimits: {
maxTotalInstances: 10, maxTotalInstances: 10,
totalInstancesUsed: 0 totalInstancesUsed: 0
@ -200,15 +200,26 @@
expect(ctrl.currentBootSource).toBe('volume'); expect(ctrl.currentBootSource).toBe('volume');
}); });
it('defaults source to snapshot-2 if launchContext.snapshotId = snapshot-2', function() {
scope.launchContext = { snapshotId: 'snapshot-2' };
deferred.resolve();
$browser.defer.flush();
expect(ctrl.tableData.allocated[0]).toEqual({ id: 'snapshot-2' });
expect(scope.model.newInstanceSpec.source_type.type).toBe('volume_snapshot');
expect(ctrl.currentBootSource).toBe('volume_snapshot');
});
describe('Scope Functions', function() { describe('Scope Functions', function() {
describe('watchers', function () { describe('watchers', function () {
it('establishes five watches', function() { it('establishes five watches', function() {
expect(scope.$watch.calls.count()).toBe(5); expect(scope.$watch.calls.count()).toBe(6);
}); });
it("establishes two watch collections", function () { it("establishes two watch collections", function () {
expect(scope.$watchCollection.calls.count()).toBe(2); expect(scope.$watchCollection.calls.count()).toBe(3);
}); });
}); });

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils import html from django.utils import html
from django.utils.http import urlencode from django.utils.http import urlencode
@ -48,6 +49,29 @@ class LaunchSnapshot(volume_tables.LaunchVolume):
return False return False
class LaunchSnapshotNG(LaunchSnapshot):
name = "launch_snapshot_ng"
verbose_name = _("Launch as Instance")
url = "horizon:project:volumes:snapshots_tab"
classes = ("btn-launch", )
ajax = False
def __init__(self, attrs=None, **kwargs):
kwargs['preempt'] = True
super(LaunchSnapshot, self).__init__(attrs, **kwargs)
def get_link_url(self, datum):
url = reverse(self.url)
vol_id = self.table.get_object_id(datum)
ngclick = "modal.openLaunchInstanceWizard(" \
"{successUrl: '%s', snapshotId: '%s'})" % (url, vol_id)
self.attrs.update({
"ng-controller": "LaunchInstanceModalController as modal",
"ng-click": ngclick
})
return "javascript:void(0);"
class DeleteVolumeSnapshot(policy.PolicyTargetMixin, tables.DeleteAction): class DeleteVolumeSnapshot(policy.PolicyTargetMixin, tables.DeleteAction):
@staticmethod @staticmethod
def action_present(count): def action_present(count):
@ -156,8 +180,15 @@ class VolumeSnapshotsTable(volume_tables.VolumesTableBase):
pagination_param = 'snapshot_marker' pagination_param = 'snapshot_marker'
prev_pagination_param = 'prev_snapshot_marker' prev_pagination_param = 'prev_snapshot_marker'
table_actions = (VolumeSnapshotsFilterAction, DeleteVolumeSnapshot,) table_actions = (VolumeSnapshotsFilterAction, DeleteVolumeSnapshot,)
row_actions = (CreateVolumeFromSnapshot, LaunchSnapshot,
EditVolumeSnapshot, DeleteVolumeSnapshot) launch_actions = ()
if getattr(settings, 'LAUNCH_INSTANCE_LEGACY_ENABLED', False):
launch_actions = (LaunchSnapshot,) + launch_actions
if getattr(settings, 'LAUNCH_INSTANCE_NG_ENABLED', True):
launch_actions = (LaunchSnapshotNG,) + launch_actions
row_actions = ((CreateVolumeFromSnapshot,) + launch_actions +
(EditVolumeSnapshot, DeleteVolumeSnapshot))
row_class = UpdateRow row_class = UpdateRow
status_columns = ("status",) status_columns = ("status",)
permissions = [( permissions = [(

View File

@ -30,7 +30,14 @@ class VolumesnapshotsTable(tables.TableRegion):
"name", "description", "snapshot_source", "type", "size") "name", "description", "snapshot_source", "type", "size")
@tables.bind_table_action('delete') @tables.bind_table_action('delete')
def delete_volume_snapshot(self, delete_button): def delete_volume_snapshots(self, delete_button):
"""Batch Delete table action."""
delete_button.click()
return forms.BaseFormRegion(self.driver, self.conf)
@tables.bind_row_action('delete')
def delete_volume_snapshot(self, delete_button, row):
"""Per-entity delete row action."""
delete_button.click() delete_button.click()
return forms.BaseFormRegion(self.driver, self.conf) return forms.BaseFormRegion(self.driver, self.conf)
@ -77,8 +84,14 @@ class VolumesnapshotsPage(basepage.BaseNavigationPage):
def delete_volume_snapshot(self, name): def delete_volume_snapshot(self, name):
row = self._get_row_with_volume_snapshot_name(name) row = self._get_row_with_volume_snapshot_name(name)
row.mark() confirm_form = self.volumesnapshots_table.delete_volume_snapshot(row)
confirm_form = self.volumesnapshots_table.delete_volume_snapshot() confirm_form.submit()
def delete_volume_snapshots(self, names):
for name in names:
row = self._get_row_with_volume_snapshot_name(name)
row.mark()
confirm_form = self.volumesnapshots_table.delete_volume_snapshots()
confirm_form.submit() confirm_form.submit()
def is_volume_snapshot_deleted(self, name): def is_volume_snapshot_deleted(self, name):

View File

@ -104,13 +104,14 @@ class TestVolumeSnapshotsBasic(helpers.TestCase):
items_per_page = 1 items_per_page = 1
snapshot_names = ["{0}_{1}".format(self.VOLUME_SNAPSHOT_NAME, i) for i snapshot_names = ["{0}_{1}".format(self.VOLUME_SNAPSHOT_NAME, i) for i
in range(count)] in range(count)]
for name in snapshot_names: for i, name in enumerate(snapshot_names):
volumes_snapshot_page = volumes_page.create_volume_snapshot( volumes_snapshot_page = volumes_page.create_volume_snapshot(
self.VOLUME_NAME, name) self.VOLUME_NAME, name)
volumes_page.find_message_and_dismiss(messages.INFO) volumes_page.find_message_and_dismiss(messages.INFO)
self.assertTrue( self.assertTrue(
volumes_snapshot_page.is_volume_snapshot_available(name)) volumes_snapshot_page.is_volume_snapshot_available(name))
volumes_snapshot_page.switch_to_volumes_tab() if i < count - 1:
volumes_snapshot_page.switch_to_volumes_tab()
first_page_definition = {'Next': True, 'Prev': False, first_page_definition = {'Next': True, 'Prev': False,
'Count': items_per_page, 'Count': items_per_page,
@ -151,9 +152,10 @@ class TestVolumeSnapshotsBasic(helpers.TestCase):
settings_page.find_message_and_dismiss(messages.SUCCESS) settings_page.find_message_and_dismiss(messages.SUCCESS)
volumes_snapshot_page = self.volumes_snapshot_page volumes_snapshot_page = self.volumes_snapshot_page
volumes_snapshot_page.delete_volume_snapshots(snapshot_names)
volumes_snapshot_page.find_message_and_dismiss(messages.SUCCESS)
for name in snapshot_names: for name in snapshot_names:
volumes_snapshot_page.delete_volume_snapshot(name) volumes_snapshot_page.is_volume_snapshot_deleted(name)
volumes_snapshot_page.find_message_and_dismiss(messages.SUCCESS)
def tearDown(self): def tearDown(self):
"""Clean up: delete volume""" """Clean up: delete volume"""