Allows row status to be determined as the aggregate of multiple columns.

Instances table handles both status columns correctly now. Fixes bug 948419.
Syspanel instances table no longer has "launch instance" action. Fixes bug 952609.
Allows ajax-updating for volumes table. Fixes bug 948396.

Change-Id: I8657c79d0ab7ec5972cc7c4886d7f009a1710876
This commit is contained in:
Gabriel Hurley 2012-03-11 16:11:38 -07:00
parent 24f6bc59b2
commit 44f670e104
6 changed files with 101 additions and 30 deletions

View File

@ -201,13 +201,20 @@ class InstancesTable(tables.DataTable):
(None, True), (None, True),
("none", True) ("none", True)
) )
STATUS_CHOICES = (
("active", True),
("error", False),
)
name = tables.Column("name", link="horizon:nova:instances_and_volumes:" \ name = tables.Column("name", link="horizon:nova:instances_and_volumes:" \
"instances:detail", "instances:detail",
verbose_name=_("Instance Name")) verbose_name=_("Instance Name"))
ip = tables.Column(get_ips, verbose_name=_("IP Address")) ip = tables.Column(get_ips, verbose_name=_("IP Address"))
size = tables.Column(get_size, verbose_name=_("Size")) size = tables.Column(get_size, verbose_name=_("Size"))
status = tables.Column("status", filters=(title,), status = tables.Column("status",
verbose_name=_("Status")) filters=(title,),
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES)
task = tables.Column("OS-EXT-STS:task_state", task = tables.Column("OS-EXT-STS:task_state",
verbose_name=_("Task"), verbose_name=_("Task"),
filters=(title,), filters=(title,),
@ -220,7 +227,7 @@ class InstancesTable(tables.DataTable):
class Meta: class Meta:
name = "instances" name = "instances"
verbose_name = _("Instances") verbose_name = _("Instances")
status_column = "task" status_columns = ["status", "task"]
table_actions = (LaunchLink, TerminateInstance) table_actions = (LaunchLink, TerminateInstance)
row_actions = (EditInstance, ConsoleLink, LogLink, SnapshotLink, row_actions = (EditInstance, ConsoleLink, LogLink, SnapshotLink,
TogglePause, ToggleSuspend, RebootInstance, TogglePause, ToggleSuspend, RebootInstance,

View File

@ -74,6 +74,12 @@ class CreateSnapshot(tables.LinkAction):
return volume.status == "available" return volume.status == "available"
class UpdateRow(tables.UpdateAction):
def get_data(self, request, volume_id):
volume = api.volume_get(request, volume_id)
return volume
def get_size(volume): def get_size(volume):
return _("%s GB") % volume.size return _("%s GB") % volume.size
@ -95,12 +101,21 @@ def get_attachment(volume):
class VolumesTableBase(tables.DataTable): class VolumesTableBase(tables.DataTable):
STATUS_CHOICES = (
("in-use", True),
("available", True),
("creating", None),
("error", False),
)
name = tables.Column("displayName", verbose_name=_("Name")) name = tables.Column("displayName", verbose_name=_("Name"))
description = tables.Column("displayDescription", description = tables.Column("displayDescription",
verbose_name=_("Description")) verbose_name=_("Description"))
size = tables.Column(get_size, verbose_name=_("Size")) size = tables.Column(get_size, verbose_name=_("Size"))
status = tables.Column("status", filters=(title,), status = tables.Column("status",
verbose_name=_("Status")) filters=(title,),
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES)
def get_object_display(self, obj): def get_object_display(self, obj):
return obj.displayName return obj.displayName
@ -113,8 +128,10 @@ class VolumesTable(VolumesTableBase):
class Meta: class Meta:
name = "volumes" name = "volumes"
verbose_name = _("Volumes") verbose_name = _("Volumes")
status_columns = ["status"]
table_actions = (CreateVolume, DeleteVolume,) table_actions = (CreateVolume, DeleteVolume,)
row_actions = (EditAttachments, CreateSnapshot, DeleteVolume) row_actions = (EditAttachments, CreateSnapshot,
DeleteVolume, UpdateRow)
class DetachVolume(tables.BatchAction): class DetachVolume(tables.BatchAction):

View File

@ -38,6 +38,10 @@ class SyspanelInstancesTable(tables.DataTable):
(None, True), (None, True),
("none", True) ("none", True)
) )
STATUS_CHOICES = (
("active", True),
("error", False),
)
tenant = tables.Column("tenant_name", verbose_name=_("Tenant")) tenant = tables.Column("tenant_name", verbose_name=_("Tenant"))
user = tables.Column("user_id", verbose_name=_("User")) user = tables.Column("user_id", verbose_name=_("User"))
internal_id = tables.Column("internal_identifier", internal_id = tables.Column("internal_identifier",
@ -48,8 +52,11 @@ class SyspanelInstancesTable(tables.DataTable):
verbose_name=_("Instance Name")) verbose_name=_("Instance Name"))
ip = tables.Column(get_ips, verbose_name=_("IP Address")) ip = tables.Column(get_ips, verbose_name=_("IP Address"))
size = tables.Column(get_size, verbose_name=_("Size")) size = tables.Column(get_size, verbose_name=_("Size"))
status = tables.Column("status", filters=(title,), status = tables.Column("status",
verbose_name=_("Status")) filters=(title,),
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES)
task = tables.Column("OS-EXT-STS:task_state", task = tables.Column("OS-EXT-STS:task_state",
verbose_name=_("Task"), verbose_name=_("Task"),
filters=(title,), filters=(title,),
@ -62,8 +69,8 @@ class SyspanelInstancesTable(tables.DataTable):
class Meta: class Meta:
name = "instances" name = "instances"
verbose_name = _("Instances") verbose_name = _("Instances")
status_column = "task" status_columns = ["status", "task"]
table_actions = (LaunchLink, TerminateInstance) table_actions = (TerminateInstance,)
row_actions = (EditInstance, ConsoleLink, LogLink, SnapshotLink, row_actions = (EditInstance, ConsoleLink, LogLink, SnapshotLink,
TogglePause, ToggleSuspend, RebootInstance, TogglePause, ToggleSuspend, RebootInstance,
TerminateInstance, UpdateRow) TerminateInstance, UpdateRow)

View File

@ -55,4 +55,4 @@ class ServicesTable(tables.DataTable):
verbose_name = _("Services") verbose_name = _("Services")
table_actions = (ServiceFilterAction,) table_actions = (ServiceFilterAction,)
multi_select = False multi_select = False
status_column = "enabled" status_columns = ["enabled"]

View File

@ -264,8 +264,8 @@ class Row(object):
.. attribute:: status .. attribute:: status
Boolean value representing the status of this row according Boolean value representing the status of this row calculated from
to the value of the table's ``status_column`` value if it is set. the values of the table's ``status_columns`` if they are set.
.. attribute:: status_class .. attribute:: status_class
@ -299,15 +299,17 @@ class Row(object):
@property @property
def status(self): def status(self):
column_name = self.table._meta.status_column column_names = self.table._meta.status_columns
if column_name: if column_names:
return self.cells[column_name].status statuses = dict([(column_name, self.cells[column_name].status) for
column_name in column_names])
return self.table.calculate_row_status(statuses)
@property @property
def status_class(self): def status_class(self):
column_name = self.table._meta.status_column column_names = self.table._meta.status_columns
if column_name: if column_names:
return self.cells[column_name].get_status_class(self.status) return self.table.get_row_status_class(self.status)
else: else:
return '' return ''
@ -371,7 +373,7 @@ class Cell(object):
return self._status return self._status
if self.column.status or \ if self.column.status or \
self.column.table._meta.status_column == self.column.name: self.column.name in self.column.table._meta.status_columns:
#returns the first matching status found #returns the first matching status found
data_value_lower = unicode(self.data).lower() data_value_lower = unicode(self.data).lower()
for status_name, status_value in self.column.status_choices: for status_name, status_value in self.column.status_choices:
@ -453,20 +455,18 @@ class DataTableOptions(object):
The name of the context variable which will contain the table when The name of the context variable which will contain the table when
it is rendered. Defaults to ``"table"``. it is rendered. Defaults to ``"table"``.
.. attribute:: status_column .. attribute:: status_columns
The name of a column on this table which represents the "state" A list or tuple of column names which represents the "state"
of the data object being represented. The collumn must already be of the data object being represented.
designated as a status column by passing the ``status=True``
parameter to the column.
If ``status_column`` is set, when the rows are rendered the value If ``status_columns`` is set, when the rows are rendered the value
of this column will be used to add an extra class to the row in of this column will be used to add an extra class to the row in
the form of ``"status_up"`` or ``"status_down"`` for that row's the form of ``"status_up"`` or ``"status_down"`` for that row's
data. data.
This is useful for displaying the enabled/disabled status of a The row status is used by other Horizon components to trigger tasks
service, for example. such as dynamic AJAX updating.
.. attribute:: row_class .. attribute:: row_class
@ -484,7 +484,7 @@ class DataTableOptions(object):
or self.name.title() or self.name.title()
self.verbose_name = unicode(verbose_name) self.verbose_name = unicode(verbose_name)
self.columns = getattr(options, 'columns', None) self.columns = getattr(options, 'columns', None)
self.status_column = getattr(options, 'status_column', None) self.status_columns = getattr(options, 'status_columns', [])
self.table_actions = getattr(options, 'table_actions', []) self.table_actions = getattr(options, 'table_actions', [])
self.row_actions = getattr(options, 'row_actions', []) self.row_actions = getattr(options, 'row_actions', [])
self.row_class = getattr(options, 'row_class', Row) self.row_class = getattr(options, 'row_class', Row)
@ -893,6 +893,46 @@ class DataTable(object):
""" """
return http.urlquote_plus(self.get_object_id(self.data[-1])) return http.urlquote_plus(self.get_object_id(self.data[-1]))
def calculate_row_status(self, statuses):
"""
Returns a boolean value determining the overall row status
based on the dictionary of column name to status mappings passed in.
By default, it uses the following logic:
#. If any statuses are ``False``, return ``False``.
#. If no statuses are ``False`` but any or ``None``, return ``None``.
#. If all statuses are ``True``, return ``True``.
This provides the greatest protection against false positives without
weighting any particular columns.
The ``statuses`` parameter is passed in as a dictionary mapping
column names to their statuses in order to allow this function to
be overridden in such a way as to weight one column's status over
another should that behavior be desired.
"""
values = statuses.values()
if any([status is False for status in values]):
return False
elif any([status is None for status in values]):
return None
else:
return True
def get_row_status_class(self, status):
"""
Returns a css class name determined by the status value. This class
name is used to indicate the status of the rows in the table if
any ``status_columns`` have been specified.
"""
if status is True:
return "status_up"
elif status is False:
return "status_down"
else:
return "status_unknown"
def get_columns(self): def get_columns(self):
""" Returns this table's columns including auto-generated ones.""" """ Returns this table's columns including auto-generated ones."""
return self.columns.values() return self.columns.values()

View File

@ -147,7 +147,7 @@ class MyTable(tables.DataTable):
class Meta: class Meta:
name = "my_table" name = "my_table"
verbose_name = "My Table" verbose_name = "My Table"
status_column = "status" status_columns = ["status"]
columns = ('id', 'name', 'value', 'optional', 'status') columns = ('id', 'name', 'value', 'optional', 'status')
table_actions = (MyFilterAction, MyAction, MyBatchAction) table_actions = (MyFilterAction, MyAction, MyBatchAction)
row_actions = (MyAction, MyLinkAction, MyUpdateAction, row_actions = (MyAction, MyLinkAction, MyUpdateAction,