[Launch Instance Fix] Launch Instance from Various Screens

Using the 'launch' options from Image Table/Detail or Network Topology
fires off the Launch Instance workflow.  Each firing-off point describes
where the user should be relocated to when successfully done with the
wizard (when a launch request has been successfully submitted).

Co-Authored-By: Shaoquan Chen <sean.chen2@hp.com>

Closes-Bug: #1433113
Change-Id: Id7bfad80781195c6311ef37e3ef32077947d8e62
This commit is contained in:
Matt Borland 2015-03-17 09:36:13 -06:00
parent 224e992237
commit 688b2d57d4
7 changed files with 100 additions and 21 deletions

View File

@ -152,6 +152,7 @@
// this is a simple workaround.
function always() {
$scope.ready = true;
$scope.initTask.resolve();
switchToFirstReadyStep();
}

View File

@ -57,6 +57,28 @@ class LaunchImage(tables.LinkAction):
return False
class LaunchImageNG(LaunchImage):
name = "launch_image_ng"
verbose_name = _("Launch")
classes = ("btn-launch")
ajax = False
def __init__(self,
attrs={
"ng-controller": "LaunchInstanceModalCtrl"
},
**kwargs):
kwargs['preempt'] = True
super(LaunchImage, self).__init__(attrs, **kwargs)
def get_link_url(self, datum):
imageId = self.table.get_object_id(datum)
clickValue = "openLaunchInstanceWizard({successUrl: " +\
"'/project/images/', imageId: '%s'})" % (imageId)
self.attrs['ng-click'] = clickValue
return "javascript:void(0);"
class DeleteImage(tables.DeleteAction):
# NOTE: The bp/add-batchactions-help-text
# will add appropriate help text to some batch/delete actions.
@ -268,6 +290,11 @@ class ImagesTable(tables.DataTable):
status_columns = ["status"]
verbose_name = _("Images")
table_actions = (OwnerFilter, CreateImage, DeleteImage,)
row_actions = (LaunchImage, CreateVolumeFromImage,
EditImage, DeleteImage,)
launch_actions = ()
if getattr(settings, 'LAUNCH_INSTANCE_LEGACY_ENABLED', True):
launch_actions = (LaunchImage,) + launch_actions
if getattr(settings, 'LAUNCH_INSTANCE_NG_ENABLED', False):
launch_actions = (LaunchImageNG,) + launch_actions
row_actions = launch_actions + (CreateVolumeFromImage,
EditImage, DeleteImage,)
pagination_param = "image_marker"

View File

@ -28,6 +28,9 @@
</div>
<div class="launchButtons">
{% if launch_instance_allowed %}
{% if show_ng_launch %}
<a ng-controller="LaunchInstanceModalCtrl" ng-click="openLaunchInstanceWizard({successUrl: '/project/network_topology/', dismissUrl: '/project/network_topology/'})" id="instances__action_launch" class="btn btn-default btn-sm btn-launch ajax-modal {% if instance_quota_exceeded %}disabled{% endif %}"><span class="fa fa-cloud-upload"></span> {% if instance_quota_exceeded %}{% trans "Launch Instance (Quota exceeded)"%}{% else %}{% trans "Launch Instance"%}{% endif %}</a>
{% endif %}
<a href="{% url 'horizon:project:network_topology:launchinstance' %}" id="instances__action_launch" class="btn btn-default btn-sm btn-launch ajax-modal {% if instance_quota_exceeded %}disabled{% endif %}"><span class="fa fa-cloud-upload"></span> {% if instance_quota_exceeded %}{% trans "Launch Instance (Quota exceeded)"%}{% else %}{% trans "Launch Instance"%}{% endif %}</a>
{% endif %}
{% if create_network_allowed %}

View File

@ -130,6 +130,8 @@ class NetworkTopologyView(views.HorizonTemplateView):
context['router_quota_exceeded'] = self._quota_exceeded('routers')
context['console_type'] = getattr(
settings, 'CONSOLE_TYPE', 'AUTO')
context['show_ng_launch'] = getattr(
settings, 'LAUNCH_INSTANCE_NG_ENABLED', False)
return context

View File

@ -95,7 +95,8 @@
function LaunchInstanceModalCtrl($scope, $modal, $window, modalSpec) {
$scope.openLaunchInstanceWizard = function (launchContext) {
var localSpec = {
var localSpec = {
resolve: {
launchContext: function() { return launchContext; }
}
@ -105,11 +106,16 @@
var launchInstanceModal = $modal.open(localSpec);
launchInstanceModal.result.then(function () {
if (launchContext && launchContext.successUrl) {
$window.location.href = launchContext.successUrl;
}
});
var handleModalClose = function(redirectPropertyName) {
return function() {
if (launchContext && launchContext[redirectPropertyName]) {
$window.location.href = launchContext[redirectPropertyName];
}
};
};
launchInstanceModal.result.then(handleModalClose('successUrl'),
handleModalClose('dismissUrl'));
};
}

View File

@ -83,7 +83,7 @@
popover-trigger="hover"></span>
<select name="boot-source-type" class="form-control input-sm"
ng-options="src.label for src in bootSourcesOptions track by src.type"
ng-change="updateBootSourceSelection(model.newInstanceSpec.source_type)"
ng-change="updateBootSourceSelection(model.newInstanceSpec.source_type.type)"
ng-model="model.newInstanceSpec.source_type">
</select>
</div>
@ -199,7 +199,7 @@
</td>
</tr>
<tr ng-repeat-start="row in tableData.displayAllocated track by row.id">
<tr ng-repeat-start="row in selection">
<td class="expander">
<span class="fa fa-chevron-right"
hz-expand-detail

View File

@ -1,6 +1,9 @@
(function () {
'use strict';
var push = [].push,
forEach = angular.forEach;
/**
* @ngdoc overview
* @name hz.dashboard.launch-instance
@ -103,17 +106,17 @@
//
$scope.bootSourcesOptions = [
{ type: 'image', label: gettext('Image') },
{ type: 'snapshot', label: gettext('Instance Snapshot') },
{ type: 'volume', label: gettext('Volume') },
{ type: 'volume_snapshot', label: gettext('Volume Snapshot') }
{ type: bootSourceTypes.IMAGE, label: gettext('Image') },
{ type: bootSourceTypes.INSTANCE_SNAPSHOT, label: gettext('Instance Snapshot') },
{ type: bootSourceTypes.VOLUME, label: gettext('Volume') },
{ type: bootSourceTypes.VOLUME_SNAPSHOT, label: gettext('Volume Snapshot') }
];
$scope.updateBootSourceSelection = function (selectedSource) {
$scope.currentBootSource = selectedSource.type;
$scope.currentBootSource = selectedSource;
$scope.model.newInstanceSpec.vol_create = false;
$scope.model.newInstanceSpec.vol_delete_on_terminate = false;
changeBootSource(selectedSource.type);
changeBootSource(selectedSource);
validateBootSourceType();
};
@ -129,7 +132,7 @@
$scope.sourceDetails =
'/static/dashboard/launch-instance/source/source-details.html';
var selection = $scope.model.newInstanceSpec.source;
var selection = $scope.selection = $scope.model.newInstanceSpec.source;
var bootSources = {
image: {
@ -229,8 +232,8 @@
};
// dynamically update page based on boot source selection
function changeBootSource(key) {
updateDataSource(key);
function changeBootSource(key, preSelection) {
updateDataSource(key, preSelection);
updateHelpText(key);
updateTableHeadCells(key);
updateTableBodyCells(key);
@ -238,9 +241,12 @@
updateMaxInstanceCount();
}
function updateDataSource(key) {
angular.extend($scope.tableData, bootSources[key]);
function updateDataSource(key, preSelection) {
selection.length = 0;
if (preSelection) {
push.apply(selection, preSelection);
}
angular.extend($scope.tableData, bootSources[key]);
}
function updateHelpText(key) {
@ -418,6 +424,40 @@
.$setValidity('bootSourceType', isValid);
}
function findSourceById(sources, id) {
var i = 0, len = sources.length, source;
for (; i < len; i++) {
source = sources[i];
if (source.id === id) {
return source;
}
}
}
function setSourceImageWithId(id) {
var pre = findSourceById($scope.model.images, id);
if (pre) {
changeBootSource(bootSourceTypes.IMAGE, [pre]);
$scope.model.newInstanceSpec.source_type = $scope.bootSourcesOptions[0];
$scope.currentBootSource = $scope.bootSourcesOptions[0].type;
}
}
$scope.$watchCollection(
function () {
return $scope.model.images;
},
function (newValue, oldValue) {
$scope.initPromise.then(function () {
$scope.$applyAsync(function () {
if ($scope.launchContext.imageId) {
setSourceImageWithId($scope.launchContext.imageId);
}
});
});
}
);
//
// initialize
//