Added efficacy-related fields to the dashboard

In this changeset, I added all the information related to the efficacy:

- The efficacy specification alongside its related goal
- The global efficacy for all action plans
- The related efficacy indicators for each action plan

Partially Implements: blueprint efficacy-indicator

Change-Id: I2ac6380b236783736166df998afe589433faa4d6
This commit is contained in:
Vincent Francoise 2016-06-03 11:15:08 +02:00
parent 387e51ee96
commit dc58cddd27
21 changed files with 466 additions and 171 deletions

View File

@ -20,6 +20,8 @@ whitelist_externals = /bin/bash
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -delete
python manage.py test --settings=watcher_dashboard.test.settings \
--exclude-dir=watcher_dashboard/test/integration_tests \
watcher_dashboard

View File

@ -79,20 +79,19 @@ class Audit(base.APIDictWrapper):
deadline=deadline)
@classmethod
def list(cls, request, audit_template_filter):
def list(cls, request, **filters):
"""Return a list of audits in Watcher
:param request: request object
:type request: django.http.HttpRequest
:param audit_template_filter: audit_template filter, name or uuid
:type audit_template_filter: string
:param filters: key/value kwargs used as filters
:type filters: dict
:return: list of audits, or an empty list if there are none
:rtype: list of :py:class:`~.Audit`
"""
return watcherclient(request).audit.list(
audit_template=audit_template_filter)
return watcherclient(request).audit.list(detail=True, **filters)
@classmethod
@errors_utils.handle_errors(_("Unable to retrieve audit"))
@ -129,16 +128,16 @@ class Audit(base.APIDictWrapper):
class AuditTemplate(base.APIDictWrapper):
_attrs = ('uuid', 'created_at', 'updated_at', 'deleted_at',
'description', 'host_aggregate', 'name',
'extra', 'goal_uuid' 'strategy_uuid')
_attrs = ('uuid', 'description', 'host_aggregate', 'name', 'extra',
'goal_uuid', 'goal_name', 'strategy_uuid', 'strategy_name',
'created_at', 'updated_at', 'deleted_at')
def __init__(self, apiresource, request=None):
super(AuditTemplate, self).__init__(apiresource)
self._request = request
@classmethod
def create(cls, request, name, goal_uuid, strategy_uuid,
def create(cls, request, name, goal, strategy,
description, host_aggregate):
"""Create an audit template in Watcher
@ -148,11 +147,12 @@ class AuditTemplate(base.APIDictWrapper):
:param name: Name for this audit template
:type name: string
:param goal_uuid: Goal UUID associated to this audit template
:type goal_uuid: string
:param goal: Goal UUID or name associated to this audit template
:type goal: string
:param strategy_uuid: Strategy UUID associated to this audit template
:type strategy_uuid: string
:param strategy: Strategy UUID or name associated to this audit
template
:type strategy: string
:param description: Descrition of the audit template
:type description: string
@ -169,8 +169,8 @@ class AuditTemplate(base.APIDictWrapper):
"""
audit_template = watcherclient(request).audit_template.create(
name=name,
goal_uuid=goal_uuid,
strategy_uuid=strategy_uuid,
goal=goal,
strategy=strategy,
description=description,
host_aggregate=host_aggregate
)
@ -209,11 +209,13 @@ class AuditTemplate(base.APIDictWrapper):
:type request: django.http.HttpRequest
:param filters: key/value kwargs used as filters
:type filters: dict
:return: list of audit templates, or an empty list if there are none
:rtype: list of :py:class:`~.AuditTemplate`
"""
return watcherclient(request).audit_template.list(**filters)
return watcherclient(request).audit_template.list(
detail=True, **filters)
@classmethod
@errors_utils.handle_errors(_("Unable to retrieve audit template"))
@ -260,20 +262,19 @@ class ActionPlan(base.APIDictWrapper):
self._request = request
@classmethod
def list(cls, request, audit_filter):
def list(cls, request, **filters):
"""Return a list of action plans in Watcher
:param request: request object
:type request: django.http.HttpRequest
:param audit_filter: audit id filter
:type audit_filter: string
:param filters: key/value kwargs used as filters
:type filters: dict
:return: list of action plans, or an empty list if there are none
:rtype: list of :py:class:`~.ActionPlan`
"""
return watcherclient(request).action_plan.list(
audit=audit_filter)
return watcherclient(request).action_plan.list(detail=True, **filters)
@classmethod
@errors_utils.handle_errors(_("Unable to retrieve action plan"))
@ -316,9 +317,7 @@ class ActionPlan(base.APIDictWrapper):
:param action_plan_id: audit id
:type action_plan_id: int
"""
patch = []
patch.append({'op': 'replace', 'path': '/state', 'value': 'PENDING'})
watcherclient(request).action_plan.update(action_plan_id, patch)
watcherclient(request).action_plan.start(action_plan_id)
@property
def id(self):
@ -335,21 +334,19 @@ class Action(base.APIDictWrapper):
self._request = request
@classmethod
def list(cls, request, action_plan_filter):
def list(cls, request, **filters):
"""Return a list of actions in Watcher
:param request: request object
:type request: django.http.HttpRequest
:param action_plan_filter: action_plan id filter
:type action_plan_filter: string
:param filters: key/value kwargs used as filters
:type filters: dict
:return: list of actions, or an empty list if there are none
:rtype: list of :py:class:`~.Action`
"""
return watcherclient(request).action.list(
action_plan=action_plan_filter, detail=True)
return watcherclient(request).action.list(detail=True, **filters)
@classmethod
@errors_utils.handle_errors(_("Unable to retrieve action"))
@ -417,6 +414,9 @@ class Goal(base.APIDictWrapper):
:param request: request object
:type request: django.http.HttpRequest
:param filters: key/value kwargs used as filters
:type filters: dict
:return: list of goals, or an empty list if there are none
:rtype: list of :py:class:`~.Goal` instance
"""
@ -424,19 +424,19 @@ class Goal(base.APIDictWrapper):
@classmethod
@errors_utils.handle_errors(_("Unable to retrieve goal"))
def get(cls, request, goal_uuid):
def get(cls, request, goal):
"""Return the goal that matches the ID
:param request: request object
:type request: django.http.HttpRequest
:param goal_uuid: uuid of goal to be retrieved
:type goal_uuid: int
:param goal: uuid of goal to be retrieved
:type goal: int
:return: matching goal, or None if no goal matches the UUID
:rtype: :py:class:`~.Goal` instance
"""
return watcherclient(request).goal.get(goal_uuid)
return watcherclient(request).goal.get(goal)
@property
def id(self):
@ -446,8 +446,8 @@ class Goal(base.APIDictWrapper):
class Strategy(base.APIDictWrapper):
"""Strategy resource."""
_attrs = ('uuid', 'name', 'display_name', 'goal_uuid', 'created_at',
'updated_at', 'deleted_at')
_attrs = ('uuid', 'name', 'display_name', 'goal_uuid', 'goal_name',
'created_at', 'updated_at', 'deleted_at')
def __init__(self, apiresource, request=None):
super(Strategy, self).__init__(apiresource)
@ -460,32 +460,40 @@ class Strategy(base.APIDictWrapper):
:param request: request object
:type request: django.http.HttpRequest
:param goal_uuid: goal uuid filter
:type goal_uuid: string
:param filters: key/value kwargs used as filters
:type filters: dict
:return: list of strategies, or an empty list if there are none
:rtype: list of :py:class:`~.Strategy` instances
"""
goal_uuid = filters.get('goal_uuid', None)
return watcherclient(request).strategy.list(
goal_uuid=goal_uuid, detail=True)
return watcherclient(request).strategy.list(detail=True, **filters)
@classmethod
@errors_utils.handle_errors(_("Unable to retrieve strategy"))
def get(cls, request, strategy_uuid):
def get(cls, request, strategy):
"""Return the strategy that matches the UUID
:param request: request object
:type request: django.http.HttpRequest
:param strategy_uuid: uuid of strategy to be retrieved
:type strategy_uuid: str
:param strategy: uuid of strategy to be retrieved
:type strategy: str
:return: matching strategy, or None if no strategy matches the UUID
:rtype: :py:class:`~.Strategy` instance
"""
return watcherclient(request).strategy.get(strategy_uuid)
return watcherclient(request).strategy.get(strategy)
@property
def id(self):
return self.uuid
class EfficacyIndicatorSpec(base.APIDictWrapper):
attrs = ('name', 'description', 'unit', 'schema')
class EfficacyIndicator(base.APIDictWrapper):
attrs = ('name', 'description', 'unit', 'value')

View File

@ -44,7 +44,7 @@ class ActionPlansFilterAction(horizon.tables.FilterAction):
# server = choices query = text
filter_type = "server"
filter_choices = (
('audit_filter', _("Audit ="), True),
('audit', _("Audit ="), True),
)
@ -117,22 +117,35 @@ class UpdateRow(horizon.tables.Row):
try:
action_plan = watcher.Action.get(request, action_plan_id)
except Exception:
msg = _('Failed to get the action_plan.')
msg = _('Failed to get the action plan.')
LOG.info(msg)
horizon.messages.warning(request, msg)
return action_plan
def format_global_efficacy(action_plan):
formatted_global_efficacy = None
global_efficacy = watcher.EfficacyIndicator(action_plan.global_efficacy)
if global_efficacy.value is not None and global_efficacy.unit:
formatted_global_efficacy = "%(value)s %(unit)s" % dict(
unit=global_efficacy.unit,
value=global_efficacy.value)
elif global_efficacy.value is not None:
formatted_global_efficacy = global_efficacy.value
return formatted_global_efficacy
class ActionPlansTable(horizon.tables.DataTable):
name = horizon.tables.Column(
'uuid',
verbose_name=_("UUID"),
link="horizon:admin:action_plans:detail")
audit = horizon.tables.Column(
'audit_uuid',
verbose_name=_('Audit'),
filters=(title, filters.replace_underscores))
verbose_name=_('Audit'))
updated_at = horizon.tables.Column(
'updated_at',
filters=(filters.parse_isotime,
@ -143,13 +156,16 @@ class ActionPlansTable(horizon.tables.DataTable):
verbose_name=_('State'),
status=True,
status_choices=ACTION_PLAN_STATE_DISPLAY_CHOICES)
efficacy = horizon.tables.Column(
transform=format_global_efficacy,
verbose_name=_('Efficacy'))
def get_object_id(self, datum):
return datum.uuid
class Meta(object):
name = "action_plans"
verbose_name = _("ActionPlans")
verbose_name = _("Action Plans")
table_actions = (
# CancelActionPlan,
ActionPlansFilterAction,
@ -162,3 +178,62 @@ class ActionPlansTable(horizon.tables.DataTable):
# DeleteActionPlans,
)
row_class = UpdateRow
class RelatedActionPlansTable(horizon.tables.DataTable):
name = horizon.tables.Column(
'uuid',
verbose_name=_("UUID"),
link="horizon:admin:action_plans:detail")
audit = horizon.tables.Column(
'audit_uuid',
verbose_name=_('Audit'))
updated_at = horizon.tables.Column(
'updated_at',
filters=(filters.parse_isotime,
filters.timesince_sortable),
verbose_name=_("Updated At"))
status = horizon.tables.Column(
'state',
verbose_name=_('State'),
status=True,
status_choices=ACTION_PLAN_STATE_DISPLAY_CHOICES)
efficacy = horizon.tables.Column(
transform=format_global_efficacy,
verbose_name=_('Efficacy'))
def get_object_id(self, datum):
return datum.uuid
class Meta(object):
name = "related_action_plans"
verbose_name = _("Related Action Plans")
hidden_title = False
class RelatedEfficacyIndicatorsTable(horizon.tables.DataTable):
name = horizon.tables.Column(
'name',
verbose_name=_("Name"))
description = horizon.tables.Column(
'description',
verbose_name=_("Description"))
unit = horizon.tables.Column(
'unit',
verbose_name=_("Unit"))
value = horizon.tables.Column(
'value',
verbose_name=_("Value"))
def get_object_id(self, datum):
return datum.name
class Meta(object):
name = "related_efficacy_indicators"
verbose_name = _("Related Efficacy Indicators")
hidden_title = False

View File

@ -46,9 +46,9 @@ class IndexView(horizon.tables.DataTableView):
search_opts = self.get_filters()
try:
action_plans = watcher.ActionPlan.list(
self.request,
audit_filter=search_opts)
except Exception:
self.request, **search_opts)
except Exception as exc:
LOG.exception(exc)
horizon.exceptions.handle(
self.request,
_("Unable to retrieve action_plan information."))
@ -58,15 +58,15 @@ class IndexView(horizon.tables.DataTableView):
return len(self.get_data())
def get_filters(self):
filter = None
filters = {}
filter_action = self.table._meta._filter_action
if filter_action:
filter_field = self.table.get_filter_field()
if filter_action.is_api_filter(filter_field):
filter_string = self.table.get_filter_string()
if filter_field and filter_string:
filter = filter_string
return filter
filters[filter_field] = filter_string
return filters
class ArchiveView(forms.ModalFormView):
@ -79,7 +79,9 @@ class ArchiveView(forms.ModalFormView):
class DetailView(horizon.tables.MultiTableView):
table_classes = (action_tables.ActionsTable,)
table_classes = (
action_tables.RelatedActionsTable,
tables.RelatedEfficacyIndicatorsTable)
template_name = 'infra_optim/action_plans/details.html'
page_title = _("Action Plan Details: {{ action_plan.uuid }}")
@ -90,7 +92,8 @@ class DetailView(horizon.tables.MultiTableView):
action_plan_uuid = self.kwargs['action_plan_uuid']
action_plan = watcher.ActionPlan.get(
self.request, action_plan_uuid)
except Exception:
except Exception as exc:
LOG.exception(exc)
msg = _('Unable to retrieve details for action_plan "%s".') \
% action_plan_uuid
horizon.exceptions.handle(
@ -98,12 +101,38 @@ class DetailView(horizon.tables.MultiTableView):
redirect=self.redirect_url)
return action_plan
def get_wactions_data(self):
def get_related_wactions_data(self):
try:
action_plan = self._get_data()
actions = watcher.Action.list(self.request,
action_plan_filter=action_plan.id)
except Exception:
action_plan=action_plan.uuid)
except Exception as exc:
LOG.exception(exc)
actions = []
msg = _('Action list can not be retrieved.')
horizon.exceptions.handle(self.request, msg)
return actions
def get_related_efficacy_indicators_data(self):
try:
action_plan = self._get_data()
efficacy_indicators = [
watcher.EfficacyIndicator(indicator)
for indicator in action_plan.efficacy_indicators]
except Exception as exc:
LOG.exception(exc)
msg = _('Failed to get the efficacy indicators.')
LOG.info(msg)
horizon.messages.warning(self.request, msg)
return efficacy_indicators
try:
action_plan = self._get_data()
actions = watcher.Action.list(self.request,
action_plan=action_plan.uuid)
except Exception as exc:
LOG.exception(exc)
actions = []
msg = _('Action list can not be retrieved.')
horizon.exceptions.handle(self.request, msg)
@ -114,22 +143,11 @@ class DetailView(horizon.tables.MultiTableView):
action_plan = self._get_data()
context["action_plan"] = action_plan
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info(action_plan)
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info('*********************************')
LOG.info('*********************************')
return context
def get_tabs(self, request, *args, **kwargs):
action_plan = self._get_data()
# ports = self._get_ports()
return self.tab_group_class(request, action_plan=action_plan,
# ports=ports,
**kwargs)
return self.tab_group_class(
request, action_plan=action_plan, **kwargs)

View File

@ -79,7 +79,6 @@ class ActionsTable(horizon.tables.DataTable):
next_action = horizon.tables.Column(
'next_uuid',
verbose_name=_('Next Action'))
ajax = True
def get_object_id(self, datum):
return datum.uuid
@ -88,5 +87,34 @@ class ActionsTable(horizon.tables.DataTable):
name = "wactions"
verbose_name = _("Actions")
table_actions = (ActionsFilterAction, )
hidden_title = False
row_class = UpdateRow
class RelatedActionsTable(horizon.tables.DataTable):
"""Identical to the index table but with different Meta"""
name = horizon.tables.Column(
'uuid',
verbose_name=_("UUID"))
action_type = horizon.tables.Column(
'action_type',
verbose_name=_('Type'),
filters=(title, filters.replace_underscores))
description = horizon.tables.Column(
'description',
verbose_name=_('Description'))
state = horizon.tables.Column(
'state',
verbose_name=_('State'),
status_choices=ACTION_STATE_DISPLAY_CHOICES)
next_action = horizon.tables.Column(
'next_uuid',
verbose_name=_('Next Action'))
def get_object_id(self, datum):
return datum.uuid
class Meta(object):
name = "related_wactions"
verbose_name = _("Related Actions")
hidden_title = False

View File

@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.tables
@ -25,6 +27,8 @@ from watcher_dashboard.api import watcher
from watcher_dashboard.content.actions import tables
from watcher_dashboard.content.actions import tabs as wtabs
LOG = logging.getLogger(__name__)
class IndexView(horizon.tables.DataTableView):
table_class = tables.ActionsTable
@ -39,8 +43,7 @@ class IndexView(horizon.tables.DataTableView):
actions = []
search_opts = self.get_filters()
try:
actions = watcher.Action.list(self.request,
action_plan_filter=search_opts)
actions = watcher.Action.list(self.request, **search_opts)
except Exception:
horizon.exceptions.handle(
self.request,
@ -51,18 +54,18 @@ class IndexView(horizon.tables.DataTableView):
return len(self.get_data())
def get_filters(self):
filter = None
filters = {}
filter_action = self.table._meta._filter_action
if filter_action:
filter_field = self.table.get_filter_field()
if filter_action.is_api_filter(filter_field):
filter_string = self.table.get_filter_string()
if filter_field and filter_string:
filter = filter_string
return filter
filters[filter_field] = filter_string
return filters
class DetailView(horizon.tabs.TabbedTableView):
class DetailView(horizon.tables.MultiTableView):
tab_group_class = wtabs.ActionDetailTabs
template_name = 'infra_optim/actions/details.html'
redirect_url = 'horizon:admin:actions:index'

View File

@ -34,8 +34,8 @@ class CreateForm(forms.SelfHandlingForm):
name = forms.CharField(max_length=255, label=_("Name"))
description = forms.CharField(max_length=255, label=_("Description"),
required=False)
goal_uuid = forms.ChoiceField(label=_('Goal'), required=True)
strategy_uuid = forms.ChoiceField(label=_('Strategy'), required=False)
goal = forms.ChoiceField(label=_('Goal'), required=True)
strategy = forms.ChoiceField(label=_('Strategy'), required=False)
failure_url = 'horizon:admin:audit_templates:index'
@ -45,14 +45,14 @@ class CreateForm(forms.SelfHandlingForm):
strategies = self._get_strategy_list(request, goals)
if goals:
self.fields['goal_uuid'].choices = goals
self.fields['goal'].choices = goals
else:
del self.fields['goal_uuid']
del self.fields['goal']
if strategies:
self.fields['strategy_uuid'].choices = strategies
self.fields['strategy'].choices = strategies
else:
del self.fields['strategy_uuid']
del self.fields['strategy']
def _get_goal_list(self, request):
try:
@ -89,7 +89,7 @@ class CreateForm(forms.SelfHandlingForm):
choices = [
(strategy.uuid, strategy.display_name +
' (GOAL: ' + _goals[strategy.goal_uuid] + ')')
' (GOAL: ' + _goals[strategy.goal_uuid] + ')')
for strategy in strategies
]
@ -101,8 +101,8 @@ class CreateForm(forms.SelfHandlingForm):
try:
params = {'name': data['name']}
params['description'] = data['description']
params['goal_uuid'] = data['goal_uuid']
params['strategy_uuid'] = data['strategy_uuid'] or None
params['goal'] = data['goal']
params['strategy'] = data['strategy'] or None
params['host_aggregate'] = None
audit_tpl = watcher.AuditTemplate.create(request, **params)
message = _('Audit Template was successfully created.')

View File

@ -35,9 +35,8 @@ class CreateAuditTemplates(horizon.tables.LinkAction):
class AuditTemplatesFilterAction(horizon.tables.FilterAction):
filter_type = "server"
filter_choices = (
('name', _("Name ="), True),
('goal_uuid', _("Goal ="), True),
('strategy_uuid', _("Strategy ="), True),
('goal', _("Goal ="), True),
('strategy', _("Strategy ="), True),
)
@ -100,12 +99,12 @@ class AuditTemplatesTable(horizon.tables.DataTable):
verbose_name=_("Name"),
link="horizon:admin:audit_templates:detail")
goal = horizon.tables.Column(
'goal_uuid',
'goal_name',
verbose_name=_('Goal'),
status=True,
)
strategy = horizon.tables.Column(
'strategy_uuid',
'strategy_name',
verbose_name=_('Strategy'),
status=True,
)

View File

@ -81,10 +81,10 @@ class AuditTemplatesTest(test.BaseAdminViewTests):
@test.create_stubs({api.watcher.AuditTemplate: ('create',)})
def test_create_post(self):
at = self.audit_templates.first()
params = {
form_data = {
'name': at.name,
'goal_uuid': at.goal_uuid,
'strategy_uuid': at.strategy_uuid,
'goal': at.goal_uuid,
'strategy': at.strategy_uuid,
'description': at.description,
'host_aggregate': at.host_aggregate,
}
@ -94,16 +94,9 @@ class AuditTemplatesTest(test.BaseAdminViewTests):
IsA(http.HttpRequest)).AndReturn(self.strategy_list)
api.watcher.AuditTemplate.create(
IsA(http.HttpRequest), **params).AndReturn(at)
IsA(http.HttpRequest), **form_data).AndReturn(at)
self.mox.ReplayAll()
form_data = {
'name': at.name,
'goal_uuid': at.goal_uuid,
'strategy_uuid': at.strategy_uuid,
'description': at.description,
'host_aggregate': at.host_aggregate,
}
res = self.client.post(CREATE_URL, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
@ -21,7 +23,7 @@ from horizon import forms
import horizon.tables
import horizon.tabs
import horizon.workflows
import logging
from watcher_dashboard.api import watcher
from watcher_dashboard.content.audit_templates import forms as wforms
from watcher_dashboard.content.audit_templates import tables
@ -46,7 +48,8 @@ class IndexView(horizon.tables.DataTableView):
try:
audit_templates = watcher.AuditTemplate.list(
self.request, **search_opts)
except Exception:
except Exception as exc:
LOG.exception(exc)
horizon.exceptions.handle(
self.request,
_("Unable to retrieve audit template information."))
@ -91,7 +94,8 @@ class DetailView(horizon.tabs.TabbedTableView):
audit_template_uuid = self.kwargs['audit_template_uuid']
audit_template = watcher.AuditTemplate.get(
self.request, audit_template_uuid)
except Exception:
except Exception as exc:
LOG.exception(exc)
msg = _('Unable to retrieve details for audit template "%s".') \
% audit_template_uuid
horizon.exceptions.handle(
@ -99,6 +103,18 @@ class DetailView(horizon.tabs.TabbedTableView):
redirect=self.redirect_url)
return audit_template
def get_related_audits_data(self):
try:
audit_template = self._get_data()
audits = watcher.Audit.list(
self.request, audit_template=audit_template.uuid)
except Exception as exc:
LOG.exception(exc)
audits = []
msg = _('Audits list cannot be retrieved.')
horizon.exceptions.handle(self.request, msg)
return audits
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
audit_template = self._get_data()
@ -108,6 +124,5 @@ class DetailView(horizon.tabs.TabbedTableView):
def get_tabs(self, request, *args, **kwargs):
audit_template = self._get_data()
# ports = self._get_ports()
return self.tab_group_class(request, audit_template=audit_template,
# ports=ports,
**kwargs)
return self.tab_group_class(
request, audit_template=audit_template, **kwargs)

View File

@ -45,7 +45,7 @@ class AuditsFilterAction(horizon.tables.FilterAction):
# server = choices query = text
filter_type = "server"
filter_choices = (
('audit_template_filter', _("Audit Template ="), True),
('audit_template', _("Audit Template ="), True),
)
@ -63,8 +63,7 @@ class GoToActionPlan(horizon.tables.Action):
url = "horizon:admin:action_plans:detail"
def allowed(self, request, audit):
return ((audit is None) or
(audit.state in ("SUCCESS")))
return audit or audit.state in ("SUCCEEEDED", )
def single(self, table, request, audit_id):
try:
@ -90,8 +89,7 @@ class GoToAuditTemplate(horizon.tables.Action):
# icon = "send"
def allowed(self, request, audit):
return ((audit is None) or
(audit.state in ("SUCCESS")))
return audit or audit.state in ("SUCCEEEDED", )
def single(self, table, request, audit_id):
try:
@ -115,8 +113,7 @@ class AuditsTable(horizon.tables.DataTable):
link="horizon:admin:audits:detail")
audit_template = horizon.tables.Column(
'audit_template_name',
verbose_name=_('Audit Template'),
filters=(title, filters.replace_underscores))
verbose_name=_('Audit Template'))
status = horizon.tables.Column(
'state',
verbose_name=_('State'),
@ -143,3 +140,27 @@ class AuditsTable(horizon.tables.DataTable):
# CreateAudits,
# DeleteAudits,
)
class RelatedAuditsTable(horizon.tables.DataTable):
name = horizon.tables.Column(
'uuid',
verbose_name=_("UUID"),
link="horizon:admin:audits:detail")
audit_template = horizon.tables.Column(
'audit_template_name',
verbose_name=_('Audit Template'),
filters=(title, filters.replace_underscores))
status = horizon.tables.Column(
'state',
verbose_name=_('State'),
status=True,
status_choices=AUDIT_STATE_DISPLAY_CHOICES)
def get_object_id(self, datum):
return datum.uuid
class Meta(object):
name = "audits"
verbose_name = _("Related audits")
hidden_title = False

View File

@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
@ -23,11 +25,15 @@ import horizon.tables
import horizon.tabs
from horizon.utils import memoized
import horizon.workflows
from watcher_dashboard.api import watcher
from watcher_dashboard.content.action_plans import tables as action_plan_tables
from watcher_dashboard.content.audits import forms as wforms
from watcher_dashboard.content.audits import tables
from watcher_dashboard.content.audits import tabs as wtabs
LOG = logging.getLogger(__name__)
class IndexView(horizon.tables.DataTableView):
table_class = tables.AuditsTable
@ -49,8 +55,7 @@ class IndexView(horizon.tables.DataTableView):
audits = []
search_opts = self.get_filters()
try:
audits = watcher.Audit.list(self.request,
audit_template_filter=search_opts)
audits = watcher.Audit.list(self.request, **search_opts)
except Exception:
horizon.exceptions.handle(
self.request,
@ -61,15 +66,15 @@ class IndexView(horizon.tables.DataTableView):
return len(self.get_data())
def get_filters(self):
filter = None
filters = {}
filter_action = self.table._meta._filter_action
if filter_action:
filter_field = self.table.get_filter_field()
if filter_action.is_api_filter(filter_field):
filter_string = self.table.get_filter_string()
if filter_field and filter_string:
filter = filter_string
return filter
filters[filter_field] = filter_string
return filters
class CreateView(forms.ModalFormView):
@ -83,7 +88,8 @@ class CreateView(forms.ModalFormView):
submit_url = reverse_lazy("horizon:admin:audits:create")
class DetailView(horizon.tabs.TabbedTableView):
class DetailView(horizon.tables.MultiTableView):
table_classes = (action_plan_tables.RelatedActionPlansTable,)
tab_group_class = wtabs.AuditDetailTabs
template_name = 'infra_optim/audits/details.html'
redirect_url = 'horizon:admin:audits:index'
@ -103,6 +109,18 @@ class DetailView(horizon.tabs.TabbedTableView):
redirect=self.redirect_url)
return audit
def get_related_action_plans_data(self):
try:
action_plan = self._get_data()
audits = watcher.ActionPlan.list(self.request,
audit=action_plan.uuid)
except Exception as exc:
LOG.exception(exc)
audits = []
msg = _('Action plan list cannot be retrieved.')
horizon.exceptions.handle(self.request, msg)
return audits
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
audit = self._get_data()

View File

@ -26,9 +26,14 @@ class GoalsTable(horizon.tables.DataTable):
'uuid',
verbose_name=_("UUID"),
link="horizon:admin:goals:detail")
name = horizon.tables.Column(
'name',
verbose_name=_('Name'))
display_name = horizon.tables.Column(
'display_name',
verbose_name=_('Name'))
verbose_name=_('Verbose Name'))
def get_object_id(self, datum):
return datum.uuid
@ -36,3 +41,30 @@ class GoalsTable(horizon.tables.DataTable):
class Meta(object):
name = "goals"
verbose_name = _("Goals")
class EfficacySpecificationTable(horizon.tables.DataTable):
name = horizon.tables.Column(
'name',
verbose_name=_("Name"))
description = horizon.tables.Column(
'description',
verbose_name=_("Description"))
unit = horizon.tables.Column(
'unit',
verbose_name=_("Unit"))
schema = horizon.tables.Column(
'schema',
verbose_name=_("Schema"))
def get_object_id(self, datum):
return datum.name
class Meta(object):
name = "efficacy_specification"
verbose_name = _("Efficacy specification")
hidden_title = False

View File

@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.tables
@ -24,6 +26,9 @@ import horizon.workflows
from watcher_dashboard.api import watcher
from watcher_dashboard.content.goals import tables
from watcher_dashboard.content.goals import tabs as wtabs
from watcher_dashboard.content.strategies import tables as strategies_tables
LOG = logging.getLogger(__name__)
class IndexView(horizon.tables.DataTableView):
@ -61,7 +66,10 @@ class IndexView(horizon.tables.DataTableView):
return filters
class DetailView(horizon.tabs.TabbedTableView):
class DetailView(horizon.tables.MultiTableView):
table_classes = (tables.EfficacySpecificationTable,
strategies_tables.RelatedStrategiesTable)
tab_group_class = wtabs.GoalDetailTabs
template_name = 'infra_optim/goals/details.html'
redirect_url = 'horizon:admin:goals:index'
@ -73,7 +81,8 @@ class DetailView(horizon.tabs.TabbedTableView):
try:
goal_uuid = self.kwargs['goal_uuid']
goal = watcher.Goal.get(self.request, goal_uuid)
except Exception:
except Exception as exc:
LOG.exception(exc)
msg = _('Unable to retrieve details for goal "%s".') \
% goal_uuid
horizon.exceptions.handle(
@ -81,6 +90,31 @@ class DetailView(horizon.tabs.TabbedTableView):
redirect=self.redirect_url)
return goal
def get_related_strategies_data(self):
try:
goal = self._get_data()
strategies = watcher.Strategy.list(self.request, goal=goal.uuid)
except Exception as exc:
LOG.exception(exc)
strategies = []
msg = _('Strategy list cannot be retrieved.')
horizon.exceptions.handle(self.request, msg)
return strategies
def get_efficacy_specification_data(self):
try:
goal = self._get_data()
indicators_spec = [watcher.EfficacyIndicatorSpec(spec)
for spec in goal.efficacy_specification]
except Exception as exc:
LOG.exception(exc)
indicators_spec = []
msg = _('Efficacy specification cannot be retrieved.')
horizon.exceptions.handle(self.request, msg)
return indicators_spec
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
goal = self._get_data()

View File

@ -14,26 +14,38 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from django.template.defaultfilters import title # noqa
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.messages
import horizon.tables
from horizon.utils import filters
class StrategiesFilterAction(horizon.tables.FilterAction):
# server = choices query = text
filter_type = "server"
filter_choices = (
('goal', _("Goal ="), True),
)
class StrategiesTable(horizon.tables.DataTable):
uuid = horizon.tables.Column(
'uuid',
verbose_name=_("UUID"),
link="horizon:admin:strategies:detail")
name = horizon.tables.Column(
'name',
verbose_name=_('Name'))
display_name = horizon.tables.Column(
'display_name',
verbose_name=_('Name'),
filters=(title, filters.replace_underscores))
goal_uuid = horizon.tables.Column(
verbose_name=_('Verbose Name'))
goal = horizon.tables.Column(
'goal_uuid',
verbose_name=_("Goal UUID"),
verbose_name=_("Goal"),
)
def get_object_id(self, datum):
@ -42,3 +54,35 @@ class StrategiesTable(horizon.tables.DataTable):
class Meta(object):
name = "strategies"
verbose_name = _("Strategies")
table_actions = (
StrategiesFilterAction,
)
class RelatedStrategiesTable(horizon.tables.DataTable):
uuid = horizon.tables.Column(
'uuid',
verbose_name=_("UUID"),
link="horizon:admin:strategies:detail")
name = horizon.tables.Column(
'name',
verbose_name=_('Name'))
display_name = horizon.tables.Column(
'display_name',
verbose_name=_('Verbose Name'))
goal = horizon.tables.Column(
'goal_uuid',
verbose_name=_("Goal"),
)
def get_object_id(self, datum):
return datum.uuid
class Meta(object):
name = "related_strategies"
verbose_name = _("Related strategies")
hidden_title = False

View File

@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.tables
@ -25,6 +27,8 @@ from watcher_dashboard.api import watcher
from watcher_dashboard.content.strategies import tables
from watcher_dashboard.content.strategies import tabs as wtabs
LOG = logging.getLogger(__name__)
class IndexView(horizon.tables.DataTableView):
table_class = tables.StrategiesTable
@ -40,7 +44,8 @@ class IndexView(horizon.tables.DataTableView):
search_opts = self.get_filters()
try:
strategies = watcher.Strategy.list(self.request, **search_opts)
except Exception:
except Exception as exc:
LOG.exception(exc)
horizon.exceptions.handle(
self.request,
_("Unable to retrieve strategy information."))
@ -73,7 +78,8 @@ class DetailView(horizon.tabs.TabbedTableView):
try:
strategy_uuid = self.kwargs['strategy_uuid']
strategy = watcher.Strategy.get(self.request, strategy_uuid)
except Exception:
except Exception as exc:
LOG.exception(exc)
msg = _('Unable to retrieve details for strategy "%s".') \
% strategy_uuid
horizon.exceptions.handle(
@ -89,7 +95,4 @@ class DetailView(horizon.tabs.TabbedTableView):
def get_tabs(self, request, *args, **kwargs):
strategy = self._get_data()
# ports = self._get_ports()
return self.tab_group_class(request, strategy=strategy,
# ports=ports,
**kwargs)
return self.tab_group_class(request, strategy=strategy, **kwargs)

View File

@ -7,9 +7,12 @@
<div class="col-sm-12">
{% include "infra_optim/action_plans/_details_overview.html" %}
<hr>
<div id="efficacy_indicators">
{{ related_efficacy_indicators_table.render }}
</div>
<div id="wactions">
{{ wactions_table.render }}
{{ related_wactions_table.render }}
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -6,5 +6,5 @@
<p>{% trans "Creates an audit template with specified parameters." %}</p>
<p>
<span>{% trans "Define the optimization goal to achieve, among those which are available." %}</span>
<span>{% trans "Optionaly, you can select the strategy used to achieve your goal. If not set, a strategy will be automatically selected among those which can be used for your goal" %}</span></p>
{% endblock %}
<span>{% trans "Optionally, you can select the strategy used to achieve your goal. If not set, a strategy will be automatically selected among those which can be used for your goal" %}</span></p>
{% endblock %}

View File

@ -31,7 +31,7 @@
</div>
<div class="row">
<div class="col-xs-12">
{{ table.render }}
{{ related_action_plans_table.render }}
</div>
</div>

View File

@ -22,9 +22,12 @@
</dl>
</div>
</div>
<div class="row">
<div class="col-xs-12">
{{ table.render }}
<div class="row detail col-md-12">
<div id="efficacy_specification">
{{ efficacy_specification_table.render }}
</div>
<div id="strategies">
{{ related_strategies_table.render }}
</div>
</div>

View File

@ -52,8 +52,7 @@ class WatcherAPITests(test.APITestCase):
watcherclient = self.stub_watcherclient()
watcherclient.strategy = self.mox.CreateMockAnything()
watcherclient.strategy.list(
goal_uuid=None, detail=True).AndReturn(strategies)
watcherclient.strategy.list(detail=True).AndReturn(strategies)
self.mox.ReplayAll()
ret_val = api.watcher.Strategy.list(self.request)
@ -74,11 +73,13 @@ class WatcherAPITests(test.APITestCase):
self.assertIsInstance(ret_val, dict)
def test_audit_template_list(self):
audit_templates = {'audit_templates': self.api_audit_templates.list()}
audit_templates = {
'audit_templates': self.api_audit_templates.list()}
watcherclient = self.stub_watcherclient()
watcherclient.audit_template = self.mox.CreateMockAnything()
watcherclient.audit_template.list().AndReturn(audit_templates)
watcherclient.audit_template.list(
detail=True).AndReturn(audit_templates)
self.mox.ReplayAll()
ret_val = api.watcher.AuditTemplate.list(self.request)
@ -96,7 +97,7 @@ class WatcherAPITests(test.APITestCase):
watcherclient.audit_template = self.mox.CreateMockAnything()
watcherclient.audit_template.list(
**search_opts).AndReturn(audit_templates)
detail=True, **search_opts).AndReturn(audit_templates)
self.mox.ReplayAll()
ret_val = api.watcher.AuditTemplate.list(
@ -125,8 +126,8 @@ class WatcherAPITests(test.APITestCase):
def test_audit_template_create(self):
audit_template = self.api_audit_templates.first()
name = audit_template['name']
goal_uuid = audit_template['goal_uuid']
strategy_uuid = audit_template['strategy_uuid']
goal = audit_template['goal_uuid']
strategy = audit_template['strategy_uuid']
description = audit_template['description']
host_aggregate = audit_template['host_aggregate']
@ -134,14 +135,14 @@ class WatcherAPITests(test.APITestCase):
watcherclient.audit_template = self.mox.CreateMockAnything()
watcherclient.audit_template.create(
name=name,
goal_uuid=goal_uuid,
strategy_uuid=strategy_uuid,
goal=goal,
strategy=strategy,
description=description,
host_aggregate=host_aggregate).AndReturn(audit_template)
self.mox.ReplayAll()
ret_val = api.watcher.AuditTemplate.create(
self.request, name, goal_uuid, strategy_uuid,
self.request, name, goal, strategy,
description, host_aggregate)
self.assertIsInstance(ret_val, dict)
@ -184,11 +185,10 @@ class WatcherAPITests(test.APITestCase):
watcherclient = self.stub_watcherclient()
watcherclient.audit = self.mox.CreateMockAnything()
watcherclient.audit.list(audit_template=None).AndReturn(audits)
watcherclient.audit.list(detail=True).AndReturn(audits)
self.mox.ReplayAll()
ret_val = api.watcher.Audit.list(
self.request, audit_template_filter=None)
ret_val = api.watcher.Audit.list(self.request)
self.assertIn('audits', ret_val)
for n in ret_val['audits']:
@ -243,10 +243,10 @@ class WatcherAPITests(test.APITestCase):
watcherclient = self.stub_watcherclient()
watcherclient.action_plan = self.mox.CreateMockAnything()
watcherclient.action_plan.list(audit=None).AndReturn(action_plans)
watcherclient.action_plan.list(detail=True).AndReturn(action_plans)
self.mox.ReplayAll()
ret_val = api.watcher.ActionPlan.list(self.request, audit_filter=None)
ret_val = api.watcher.ActionPlan.list(self.request)
self.assertIn('action_plans', ret_val)
for n in ret_val['action_plans']:
@ -267,12 +267,10 @@ class WatcherAPITests(test.APITestCase):
def test_action_plan_start(self):
action_plan_id = self.api_action_plans.first()['uuid']
patch = []
patch.append({'path': '/state', 'value': 'PENDING', 'op': 'replace'})
watcherclient = self.stub_watcherclient()
watcherclient.action_plan = self.mox.CreateMockAnything()
watcherclient.action_plan.update(action_plan_id, patch)
watcherclient.action_plan.start(action_plan_id)
self.mox.ReplayAll()
api.watcher.ActionPlan.start(self.request, action_plan_id)
@ -293,12 +291,10 @@ class WatcherAPITests(test.APITestCase):
watcherclient = self.stub_watcherclient()
watcherclient.action = self.mox.CreateMockAnything()
watcherclient.action.list(
action_plan=None, detail=True).AndReturn(actions)
watcherclient.action.list(detail=True).AndReturn(actions)
self.mox.ReplayAll()
ret_val = api.watcher.Action.list(
self.request, action_plan_filter=None)
ret_val = api.watcher.Action.list(self.request)
self.assertIn('actions', ret_val)
for n in ret_val['actions']: