Merge "Replace "Terminate Instance" with "Delete Instance""

This commit is contained in:
Jenkins 2015-12-02 23:27:46 +00:00 committed by Gerrit Code Review
commit f56476c2e1
21 changed files with 104 additions and 101 deletions

View File

@ -182,13 +182,13 @@ horizon.addInitFunction(horizon.instances.init = function () {
elements_list = "#id_instance_snapshot_id";
break;
case "volume_id":
elements_list = "#id_volume_id, #id_device_name, #id_delete_on_terminate";
elements_list = "#id_volume_id, #id_device_name, #id_vol_delete_on_instance_delete";
break;
case "volume_image_id":
elements_list = "#id_image_id, #id_volume_size, #id_device_name, #id_delete_on_terminate";
elements_list = "#id_image_id, #id_volume_size, #id_device_name, #id_vol_delete_on_instance_delete";
break;
case "volume_snapshot_id":
elements_list = "#id_volume_snapshot_id, #id_device_name, #id_delete_on_terminate";
elements_list = "#id_volume_snapshot_id, #id_device_name, #id_vol_delete_on_instance_delete";
break;
}
var elements_list_group = $(elements_list).closest(".form-group");

View File

@ -986,7 +986,7 @@ horizon.network_topology = {
table2:portTmpl
});
} else if (d instanceof Server) {
htmlData.delete_label = gettext('Terminate Instance');
htmlData.delete_label = gettext('Delete Instance');
htmlData.view_details_label = gettext('View Instance Details');
htmlData.console_id = d.id;
htmlData.ips = d.ip_addresses;

View File

@ -29,25 +29,25 @@ from openstack_dashboard.contrib.trove.content.databases import db_capability
ACTIVE_STATES = ("ACTIVE",)
class TerminateCluster(tables.BatchAction):
name = "terminate"
class DeleteCluster(tables.BatchAction):
name = "delete"
icon = "remove"
classes = ('btn-danger',)
help_text = _("Terminated cluster is not recoverable.")
help_text = _("Deleted cluster is not recoverable.")
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Terminate Cluster",
u"Terminate Clusters",
u"Delete Cluster",
u"Delete Clusters",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Scheduled termination of Cluster",
u"Scheduled termination of Clusters",
u"Scheduled deletion of Cluster",
u"Scheduled deletion of Clusters",
count
)
@ -159,8 +159,8 @@ class ClustersTable(tables.DataTable):
verbose_name = _("Clusters")
status_columns = ["task"]
row_class = UpdateRow
table_actions = (LaunchLink, TerminateCluster)
row_actions = (AddShard, ResetPassword, TerminateCluster)
table_actions = (LaunchLink, DeleteCluster)
row_actions = (AddShard, ResetPassword, DeleteCluster)
def get_instance_size(instance):

View File

@ -31,26 +31,26 @@ from openstack_dashboard.contrib.trove.content.database_backups \
ACTIVE_STATES = ("ACTIVE",)
class TerminateInstance(tables.BatchAction):
help_text = _("Terminated instances are not recoverable.")
class DeleteInstance(tables.BatchAction):
help_text = _("Deleted instances are not recoverable.")
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Terminate Instance",
u"Terminate Instances",
u"Delete Instance",
u"Delete Instances",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Scheduled termination of Instance",
u"Scheduled termination of Instances",
u"Scheduled deletion of Instance",
u"Scheduled deletion of Instances",
count
)
name = "terminate"
name = "delete"
classes = ("btn-danger", )
icon = "remove"
@ -340,13 +340,13 @@ class InstancesTable(tables.DataTable):
verbose_name = _("Instances")
status_columns = ["status"]
row_class = UpdateRow
table_actions = (LaunchLink, TerminateInstance)
table_actions = (LaunchLink, DeleteInstance)
row_actions = (CreateBackup,
ResizeVolume,
ResizeInstance,
RestartInstance,
DetachReplica,
TerminateInstance)
DeleteInstance)
class UsersTable(tables.DataTable):

View File

@ -171,7 +171,7 @@ class AdminInstancesTable(tables.DataTable):
name = "instances"
verbose_name = _("Instances")
status_columns = ["status", "task"]
table_actions = (project_tables.TerminateInstance,
table_actions = (project_tables.DeleteInstance,
AdminInstanceFilterAction)
row_class = AdminUpdateRow
row_actions = (project_tables.ConfirmResize,
@ -187,4 +187,4 @@ class AdminInstancesTable(tables.DataTable):
LiveMigrateInstance,
project_tables.SoftRebootInstance,
project_tables.RebootInstance,
project_tables.TerminateInstance)
project_tables.DeleteInstance)

View File

@ -81,31 +81,31 @@ def is_deleting(instance):
return task_state.lower() == "deleting"
class TerminateInstance(policy.PolicyTargetMixin, tables.BatchAction):
name = "terminate"
class DeleteInstance(policy.PolicyTargetMixin, tables.BatchAction):
name = "delete"
classes = ("btn-danger",)
icon = "remove"
policy_rules = (("compute", "compute:delete"),)
help_text = _("Terminated instances are not recoverable.")
help_text = _("Deleted instances are not recoverable.")
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Terminate Instance",
u"Terminate Instances",
u"Delete Instance",
u"Delete Instances",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Scheduled termination of Instance",
u"Scheduled termination of Instances",
u"Scheduled deletion of Instance",
u"Scheduled deletion of Instances",
count
)
def allowed(self, request, instance=None):
"""Allow terminate action if instance not currently being deleted."""
"""Allow delete action if instance not currently being deleted."""
return not is_deleting(instance)
def action(self, request, obj_id):
@ -1172,7 +1172,7 @@ class InstancesTable(tables.DataTable):
launch_actions = (LaunchLink,) + launch_actions
if getattr(settings, 'LAUNCH_INSTANCE_NG_ENABLED', False):
launch_actions = (LaunchLinkNG,) + launch_actions
table_actions = launch_actions + (TerminateInstance,
table_actions = launch_actions + (DeleteInstance,
InstancesFilterAction)
row_actions = (StartInstance, ConfirmResize, RevertResize,
CreateSnapshot, SimpleAssociateIP, AssociateIP,
@ -1182,4 +1182,4 @@ class InstancesTable(tables.DataTable):
ConsoleLink, LogLink, TogglePause, ToggleSuspend,
ToggleShelve, ResizeLink, LockInstance, UnlockInstance,
SoftRebootInstance, RebootInstance,
StopInstance, RebuildInstance, TerminateInstance)
StopInstance, RebuildInstance, DeleteInstance)

View File

@ -10,7 +10,7 @@
<script type="text/javascript" charset="utf-8">
$(function () {
var $flavor = $("#flavor_details_{{ id }}");
// NOTE(tsufiev): check this in case this template is used in network topology -> terminate instance
// NOTE(tsufiev): check this in case this template is used in network topology -> delete instance
if ( $flavor.popover ) {
$flavor.popover({html:true});
}

View File

@ -242,7 +242,7 @@ class InstanceTests(helpers.TestCase):
'server_delete',),
api.glance: ('image_list_detailed',),
api.network: ('servers_update_addresses',)})
def test_terminate_instance(self):
def test_delete_instance(self):
servers = self.servers.list()
server = servers[0]
@ -256,7 +256,7 @@ class InstanceTests(helpers.TestCase):
api.nova.server_delete(IsA(http.HttpRequest), server.id)
self.mox.ReplayAll()
formData = {'action': 'instances__terminate__%s' % server.id}
formData = {'action': 'instances__delete__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
@ -266,7 +266,7 @@ class InstanceTests(helpers.TestCase):
'server_delete',),
api.glance: ('image_list_detailed',),
api.network: ('servers_update_addresses',)})
def test_terminate_instance_exception(self):
def test_delete_instance_exception(self):
servers = self.servers.list()
server = servers[0]
@ -282,7 +282,7 @@ class InstanceTests(helpers.TestCase):
self.mox.ReplayAll()
formData = {'action': 'instances__terminate__%s' % server.id}
formData = {'action': 'instances__delete__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
@ -4384,7 +4384,7 @@ class InstanceTests(helpers.TestCase):
'server_delete',),
api.glance: ('image_list_detailed',),
api.network: ('servers_update_addresses',)})
def test_terminate_instance_with_pagination(self):
def test_delete_instance_with_pagination(self):
"""Instance should be deleted from
the next page.
"""
@ -4408,7 +4408,7 @@ class InstanceTests(helpers.TestCase):
servers[page_size - 1].id])
next_page_url = "?".join([reverse('horizon:project:instances:index'),
params])
formData = {'action': 'instances__terminate__%s' % server.id}
formData = {'action': 'instances__delete__%s' % server.id}
res = self.client.post(next_page_url, formData)
self.assertRedirectsNoFollow(res, next_page_url)

View File

@ -128,11 +128,11 @@ class SetInstanceDetailsAction(workflows.Action):
"system choose a device name "
"for you."))
delete_on_terminate = forms.BooleanField(label=_("Delete on Terminate"),
initial=False,
required=False,
help_text=_("Delete volume on "
"instance terminate"))
vol_delete_on_instance_delete = forms.BooleanField(
label=_("Delete Volume on Instance Delete"),
initial=False,
required=False,
help_text=_("Delete volume when the instance is deleted"))
class Meta(object):
name = _("Details")
@ -507,7 +507,7 @@ class SetInstanceDetails(workflows.Step):
contributes = ("source_type", "source_id",
"availability_zone", "name", "count", "flavor",
"device_name", # Can be None for an image.
"delete_on_terminate")
"vol_delete_on_instance_delete")
def prepare_action_context(self, request, context):
if 'source_type' in context and 'source_id' in context:
@ -884,17 +884,18 @@ class LaunchInstance(workflows.Workflow):
'source_type': dev_source_type_mapping[source_type],
'destination_type': 'volume',
'delete_on_termination':
bool(context['delete_on_terminate']),
bool(context['vol_delete_on_instance_delete']),
'uuid': volume_source_id,
'boot_index': '0',
'volume_size': context['volume_size']
}
]
else:
dev_mapping_1 = {context['device_name']: '%s::%s' %
(context['source_id'],
bool(context['delete_on_terminate']))
}
dev_mapping_1 = {
context['device_name']: '%s::%s' %
(context['source_id'],
bool(context['vol_delete_on_instance_delete']))
}
except Exception:
msg = _('Unable to retrieve extensions information')
exceptions.handle(request, msg)
@ -906,7 +907,7 @@ class LaunchInstance(workflows.Workflow):
'source_type': 'image',
'destination_type': 'volume',
'delete_on_termination':
bool(context['delete_on_terminate']),
bool(context['vol_delete_on_instance_delete']),
'uuid': context['source_id'],
'boot_index': '0',
'volume_size': context['volume_size']

View File

@ -22,5 +22,5 @@ class InstancesTable(tables.InstancesTable):
name = "instances"
verbose_name = _("Instances")
row_actions = (
tables.TerminateInstance,
tables.DeleteInstance,
)

View File

@ -160,7 +160,7 @@
vol_create: false,
// May be null
vol_device_name: 'vda',
vol_delete_on_terminate: false,
vol_delete_on_instance_delete: false,
vol_size: 1
};
}
@ -472,7 +472,7 @@
delete finalSpec.source_type;
delete finalSpec.vol_create;
delete finalSpec.vol_device_name;
delete finalSpec.vol_delete_on_terminate;
delete finalSpec.vol_delete_on_instance_delete;
delete finalSpec.vol_size;
}
@ -486,7 +486,7 @@
'device_name': deviceName,
'source_type': SOURCE_TYPE_IMAGE,
'destination_type': SOURCE_TYPE_VOLUME,
'delete_on_termination': finalSpec.vol_delete_on_terminate,
'delete_on_termination': finalSpec.vol_delete_on_instance_delete,
'uuid': finalSpec.source_id,
'boot_index': '0',
'volume_size': finalSpec.vol_size
@ -503,7 +503,7 @@
':',
sourceType,
'::',
finalSpec.vol_delete_on_terminate
finalSpec.vol_delete_on_instance_delete
].join('');
// Source ID must be empty for API

View File

@ -373,7 +373,7 @@
it('sets volume options appropriately', function() {
expect(model.newInstanceSpec.vol_create).toBe(false);
expect(model.newInstanceSpec.vol_device_name).toBe('vda');
expect(model.newInstanceSpec.vol_delete_on_terminate).toBe(false);
expect(model.newInstanceSpec.vol_delete_on_instance_delete).toBe(false);
expect(model.newInstanceSpec.vol_size).toBe(1);
});
@ -391,7 +391,7 @@
model.newInstanceSpec.security_groups = [ { id: 'adminId', name: 'admin' },
{ id: 'demoId', name: 'demo' } ];
model.newInstanceSpec.vol_create = true;
model.newInstanceSpec.vol_delete_on_terminate = true;
model.newInstanceSpec.vol_delete_on_instance_delete = true;
model.newInstanceSpec.vol_device_name = "volTestName";
model.newInstanceSpec.vol_size = 10;
});
@ -442,7 +442,7 @@
it('should handle source type of "volume"', function() {
model.newInstanceSpec.source_type.type = 'volume';
model.newInstanceSpec.source[0].id = 'imAnID';
model.newInstanceSpec.vol_delete_on_terminate = 'yep';
model.newInstanceSpec.vol_delete_on_instance_delete = 'yep';
var finalSpec = model.createInstance();
expect(finalSpec.block_device_mapping.volTestName)
@ -461,7 +461,7 @@
it('should handle source type of "volume_snapshot"', function() {
model.newInstanceSpec.source_type.type = 'volume_snapshot';
model.newInstanceSpec.source[0].id = 'imAnID';
model.newInstanceSpec.vol_delete_on_terminate = 'yep';
model.newInstanceSpec.vol_delete_on_instance_delete = 'yep';
var finalSpec = model.createInstance();
expect(finalSpec.block_device_mapping.volTestName)

View File

@ -240,7 +240,7 @@
function updateBootSourceSelection(selectedSource) {
ctrl.currentBootSource = selectedSource;
$scope.model.newInstanceSpec.vol_create = false;
$scope.model.newInstanceSpec.vol_delete_on_terminate = false;
$scope.model.newInstanceSpec.vol_delete_on_instance_delete = false;
changeBootSource(selectedSource);
validateBootSourceType();
}

View File

@ -137,7 +137,7 @@
expect(ctrl.currentBootSource).toEqual('image');
expect(scope.model.newInstanceSpec.vol_create).toBe(false);
expect(scope.model.newInstanceSpec.vol_delete_on_terminate).toBe(false);
expect(scope.model.newInstanceSpec.vol_delete_on_instance_delete).toBe(false);
// check table data
expect(ctrl.tableData).toBeDefined();

View File

@ -6,8 +6,8 @@
<p translate><li><b>Image</b>: This option uses an image to boot the instance.</li></p>
<p translate><li><b>Instance Snapshot</b>: This option uses an instance snapshot to boot the instance.</li></p>
<p translate>If you want to create an instance that uses persistent storage, meaning the instance data is saved when the instance is deleted, then select one of the following boot options:</p>
<p translate><li><b>Image (with Create New Volume checked)</b>: This options uses an image to boot the instance, and creates a new volume to persist instance data. You can specify volume size and whether to delete the volume on termination of the instance.</li></p>
<p translate><li><b>Volume</b>: This option uses a volume that already exists. It does not create a new volume. You can choose to delete the volume on termination of the instance. <em>Note: when selecting Volume, you can only launch one instance.</em></li></p>
<p translate><li><b>Volume Snapshot</b>: This option uses a volume snapshot to boot the instance, and creates a new volume to persist instance data. You can choose to delete the volume on termination of the instance.</li></p>
<p translate><li><b>Image (with Create New Volume checked)</b>: This options uses an image to boot the instance, and creates a new volume to persist instance data. You can specify volume size and whether to delete the volume on deletion of the instance.</li></p>
<p translate><li><b>Volume</b>: This option uses a volume that already exists. It does not create a new volume. You can choose to delete the volume on deletion of the instance. <em>Note: when selecting Volume, you can only launch one instance.</em></li></p>
<p translate><li><b>Volume Snapshot</b>: This option uses a volume snapshot to boot the instance, and creates a new volume to persist instance data. You can choose to delete the volume on deletion of the instance.</li></p>
</div>

View File

@ -77,12 +77,12 @@
<div class="col-xs-12 col-sm-4" ng-if="model.newInstanceSpec.vol_create == true">
<div class="form-group delete-volume">
<label translate class="on-top">Delete Volume on Terminate</label>
<label translate class="on-top">Delete Volume on Instance Delete</label>
<div class="form-field">
<div class="btn-group">
<label class="btn btn-toggle"
ng-repeat="option in ctrl.toggleButtonOptions"
ng-model="model.newInstanceSpec.vol_delete_on_terminate"
ng-model="model.newInstanceSpec.vol_delete_on_instance_delete"
btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>
@ -96,12 +96,12 @@
<div class="col-xs-12 col-sm-6">
<div class="form-group delete-volume">
<label translate class="on-top">Delete Volume on Terminate</label>
<label translate class="on-top">Delete Volume on Instance Delete</label>
<div class="form-field">
<div class="btn-group">
<label class="btn btn-toggle"
ng-repeat="option in ctrl.toggleButtonOptions"
ng-model="model.newInstanceSpec.vol_delete_on_terminate"
ng-model="model.newInstanceSpec.vol_delete_on_instance_delete"
btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>

View File

@ -545,7 +545,7 @@ div.input input[type="checkbox"] {
width: 25px;
}
tr.terminated {
tr.deleted {
color: #999999;
}

View File

@ -25,13 +25,13 @@ class InstancesPage(basepage.BaseNavigationPage):
DEFAULT_VOLUME_NAME = None
DEFAULT_SNAPSHOT_NAME = None
DEFAULT_VOLUME_SNAPSHOT_NAME = None
DEFAULT_DELETE_ON_TERMINATE = False
DEFAULT_VOL_DELETE_ON_INSTANCE_DELETE = False
DEFAULT_SECURITY_GROUP = True
_instances_table_locator = (by.By.CSS_SELECTOR, 'table#instances')
INSTANCES_TABLE_NAME = "instances"
INSTANCES_TABLE_ACTIONS = ("launch_ng", "launch", "terminate",
INSTANCES_TABLE_ACTIONS = ("launch_ng", "launch", "delete",
('start', 'stop', "reboot"))
INSTANCES_TABLE_NAME_COLUMN_INDEX = 0
INSTANCES_TABLE_STATUS_COLUMN_INDEX = 5
@ -41,14 +41,14 @@ class InstancesPage(basepage.BaseNavigationPage):
"associate_floating_ip", "disassociate_floating_ip",
"edit_instance", "edit_security_groups", "console",
"view_log", "pause", "suspend", "resize", "lock", "unlock",
"soft_reboot", "hard_reboot", "shutoff", "rebuild", "terminate")
"soft_reboot", "hard_reboot", "shutoff", "rebuild", "delete")
}
CREATE_INSTANCE_FORM_FIELDS = ((
"availability_zone", "name", "flavor",
"count", "source_type", "instance_snapshot_id",
"volume_id", "volume_snapshot_id", "image_id", "volume_size",
"delete_on_terminate"),
"vol_delete_on_instance_delete"),
("keypair", "groups"),
("script_source", "script_upload", "script_data"),
("disk_config", "config_drive")
@ -88,14 +88,16 @@ class InstancesPage(basepage.BaseNavigationPage):
def is_instance_present(self, name):
return bool(self._get_row_with_instance_name(name))
def create_instance(self, instance_name,
available_zone=None,
instance_count=DEFAULT_COUNT,
flavor=DEFAULT_FLAVOR,
boot_source=DEFAULT_BOOT_SOURCE,
source_name=None,
device_size=None,
delete_on_terminate=DEFAULT_DELETE_ON_TERMINATE):
def create_instance(
self, instance_name,
available_zone=None,
instance_count=DEFAULT_COUNT,
flavor=DEFAULT_FLAVOR,
boot_source=DEFAULT_BOOT_SOURCE,
source_name=None,
device_size=None,
vol_delete_on_instance_delete=DEFAULT_VOL_DELETE_ON_INSTANCE_DELETE
):
if not available_zone:
available_zone = self.conf.launch_instances.available_zone
self.instances_table.launch.click()
@ -112,19 +114,19 @@ class InstancesPage(basepage.BaseNavigationPage):
boot_source[0].text = source_name
if device_size:
instance.volume_size.value = device_size
if delete_on_terminate:
instance.delete_on_terminate.mark()
if vol_delete_on_instance_delete:
instance.vol_delete_on_instance_delete.mark()
instance.submit.click()
self.wait_till_popups_disappear()
def terminate_instance(self, name):
def delete_instance(self, name):
row = self._get_row_with_instance_name(name)
row.mark()
self.instances_table.terminate.click()
self.instances_table.delete.click()
self.confirm_delete_instances_form.submit.click()
self.wait_till_popups_disappear()
def is_instance_terminated(self, name):
def is_instance_deleted(self, name):
try:
row = self._get_row_with_instance_name(name)
self._wait_till_element_disappears(row)

View File

@ -17,12 +17,12 @@ INSTANCES_NAME = helpers.gen_random_resource_name('instance',
class TestInstances(helpers.AdminTestCase):
"""This is a basic scenario to test:
* Create Instance and Terminate Instance
* Create Instance and Delete Instance
"""
def test_create_terminate_instance(self):
def test_create_delete_instance(self):
instances_page = self.home_pg.go_to_compute_instancespage()
instances_page.create_instance(INSTANCES_NAME)
self.assertTrue(instances_page.is_instance_active(INSTANCES_NAME))
instances_page.terminate_instance(INSTANCES_NAME)
self.assertTrue(instances_page.is_instance_terminated(INSTANCES_NAME))
instances_page.delete_instance(INSTANCES_NAME)
self.assertTrue(instances_page.is_instance_deleted(INSTANCES_NAME))

View File

@ -26,7 +26,7 @@ from openstack_dashboard.usage import quotas
class BaseUsage(object):
show_terminated = False
show_deleted = False
def __init__(self, request, project_id=None):
self.project_id = project_id or request.user.tenant_id
@ -248,7 +248,7 @@ class BaseUsage(object):
class GlobalUsage(BaseUsage):
show_terminated = True
show_deleted = True
def get_usage_list(self, start, end):
return api.nova.usage_list(self.request, start, end)
@ -259,10 +259,10 @@ class ProjectUsage(BaseUsage):
'hours', 'local_gb')
def get_usage_list(self, start, end):
show_terminated = self.request.GET.get('show_terminated',
self.show_terminated)
show_deleted = self.request.GET.get('show_deleted',
self.show_deleted)
instances = []
terminated_instances = []
deleted_instances = []
usage = api.nova.usage_get(self.request, self.project_id, start, end)
# Attribute may not exist if there are no instances
if hasattr(usage, 'server_usages'):
@ -273,8 +273,8 @@ class ProjectUsage(BaseUsage):
server_uptime = server_usage['uptime']
total_uptime = now - datetime.timedelta(seconds=server_uptime)
server_usage['uptime_at'] = total_uptime
if server_usage['ended_at'] and not show_terminated:
terminated_instances.append(server_usage)
if server_usage['ended_at'] and not show_deleted:
deleted_instances.append(server_usage)
else:
instances.append(server_usage)
usage.server_usages = instances

View File

@ -20,7 +20,7 @@ from openstack_dashboard.usage import base
class UsageView(tables.DataTableView):
usage_class = None
show_terminated = True
show_deleted = True
csv_template_name = None
page_title = _("Overview")