Merge "Allow Launch Instance (Angular) from Volume Snapshots"
This commit is contained in:
commit
ec67db1a7f
@ -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 () {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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 = [(
|
||||||
|
@ -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):
|
||||||
|
@ -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"""
|
||||||
|
Loading…
Reference in New Issue
Block a user