Add support for new Tempmon Appliance table, etc.

This commit is contained in:
Lance Edgar 2018-10-19 17:55:23 -05:00
parent aa97a36167
commit 40a8761feb
6 changed files with 182 additions and 40 deletions

View file

@ -44,8 +44,10 @@
<%def name="render_main_fields(form)"> <%def name="render_main_fields(form)">
${form.render_field_readonly('client')} ${form.render_field_readonly('client')}
${form.render_field_readonly('config_key')} ${form.render_field_readonly('config_key')}
${form.render_field_readonly('appliance')}
${form.render_field_readonly('appliance_type')} ${form.render_field_readonly('appliance_type')}
${form.render_field_readonly('description')} ${form.render_field_readonly('description')}
${form.render_field_readonly('location')}
${form.render_field_readonly('device_path')} ${form.render_field_readonly('device_path')}
${form.render_field_readonly('notes')} ${form.render_field_readonly('notes')}
${form.render_field_readonly('enabled')} ${form.render_field_readonly('enabled')}

View file

@ -30,6 +30,7 @@ from .core import MasterView
def includeme(config): def includeme(config):
config.include('tailbone.views.tempmon.appliances')
config.include('tailbone.views.tempmon.clients') config.include('tailbone.views.tempmon.clients')
config.include('tailbone.views.tempmon.probes') config.include('tailbone.views.tempmon.probes')
config.include('tailbone.views.tempmon.readings') config.include('tailbone.views.tempmon.readings')

View file

@ -0,0 +1,93 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Views for tempmon appliances
"""
from __future__ import unicode_literals, absolute_import
from rattail_tempmon.db import model as tempmon
from webhelpers2.html import HTML, tags
from tailbone.views.tempmon import MasterView
class TempmonApplianceView(MasterView):
"""
Master view for tempmon appliances.
"""
model_class = tempmon.Appliance
model_title = "TempMon Appliance"
model_title_plural = "TempMon Appliances"
route_prefix = 'tempmon.appliances'
url_prefix = '/tempmon/appliances'
grid_columns = [
'name',
'location',
]
form_fields = [
'name',
'location',
'clients',
'probes',
]
def configure_grid(self, g):
super(TempmonApplianceView, self).configure_grid(g)
g.set_sort_defaults('name')
def configure_form(self, f):
super(TempmonApplianceView, self).configure_form(f)
# clients
if self.viewing:
f.set_renderer('clients', self.render_clients)
else:
f.remove_field('clients')
# probes
if self.viewing:
f.set_renderer('probes', self.render_probes)
elif self.creating or self.editing:
f.remove_field('probes')
def render_clients(self, appliance, field):
clients = {}
for probe in appliance.probes:
if probe.client.uuid not in clients:
clients[probe.client.uuid] = probe.client
if not clients:
return ""
clients = sorted(clients.values(), key=lambda client: client.hostname)
items = [HTML.tag('li', c=[tags.link_to(client.hostname, self.request.route_url('tempmon.clients.view', uuid=client.uuid))])
for client in clients]
return HTML.tag('ul', c=items)
def includeme(config):
TempmonApplianceView.defaults(config)

View file

@ -28,14 +28,11 @@ from __future__ import unicode_literals, absolute_import
import subprocess import subprocess
import six
from rattail_tempmon.db import model as tempmon from rattail_tempmon.db import model as tempmon
import colander import colander
from webhelpers2.html import HTML, tags from webhelpers2.html import HTML, tags
from tailbone import grids
from tailbone.views.tempmon import MasterView from tailbone.views.tempmon import MasterView
@ -68,6 +65,7 @@ class TempmonClientView(MasterView):
'location', 'location',
'disk_type', 'disk_type',
'delay', 'delay',
'appliances',
'probes', 'probes',
'notes', 'notes',
'enabled', 'enabled',
@ -119,6 +117,12 @@ class TempmonClientView(MasterView):
# delay # delay
f.set_helptext('delay', tempmon.Client.delay.__doc__) f.set_helptext('delay', tempmon.Client.delay.__doc__)
# appliances
if self.viewing:
f.set_renderer('appliances', self.render_appliances)
else:
f.remove_field('appliances')
# probes # probes
if self.viewing: if self.viewing:
f.set_renderer('probes', self.render_probes) f.set_renderer('probes', self.render_probes)
@ -149,44 +153,19 @@ class TempmonClientView(MasterView):
if query.count(): if query.count():
raise colander.Invalid(node, "Config key must be unique") raise colander.Invalid(node, "Config key must be unique")
def render_probes(self, client, field): def render_appliances(self, client, field):
if not client.probes: appliances = {}
for probe in client.probes:
if probe.appliance and probe.appliance.uuid not in appliances:
appliances[probe.appliance.uuid] = probe.appliance
if not appliances:
return "" return ""
route_prefix = self.get_route_prefix() appliances = sorted(appliances.values(), key=lambda a: a.name)
view_url = lambda p, i: self.request.route_url('tempmon.probes.view', uuid=p.uuid) items = [HTML.tag('li', c=[tags.link_to(a.name, self.request.route_url('tempmon.appliances.view', uuid=a.uuid))])
actions = [ for a in appliances]
grids.GridAction('view', icon='zoomin', url=view_url), return HTML.tag('ul', c=items)
]
if self.request.has_perm('tempmon.probes.edit'):
url = lambda p, i: self.request.route_url('tempmon.probes.edit', uuid=p.uuid)
actions.append(grids.GridAction('edit', icon='pencil', url=url))
g = grids.Grid(
key='{}.probes'.format(route_prefix),
data=client.probes,
columns=[
'description',
'critical_temp_min',
'good_temp_min',
'good_temp_max',
'critical_temp_max',
'status',
'enabled',
],
labels={
'critical_temp_min': "Crit. Min",
'good_temp_min': "Good Min",
'good_temp_max': "Good Max",
'critical_temp_max': "Crit. Max",
},
url=lambda p: self.request.route_url('tempmon.probes.view', uuid=p.uuid),
linked_columns=['description'],
main_actions=actions,
)
g.set_enum('status', self.enum.TEMPMON_PROBE_STATUS)
g.set_type('enabled', 'boolean')
return HTML.literal(g.render_grid())
def delete_instance(self, client): def delete_instance(self, client):
# bulk-delete all readings first # bulk-delete all readings first

View file

@ -28,7 +28,9 @@ from __future__ import unicode_literals, absolute_import
from rattail_tempmon.db import Session as RawTempmonSession from rattail_tempmon.db import Session as RawTempmonSession
from tailbone import views from webhelpers2.html import HTML
from tailbone import views, grids
from tailbone.db import TempmonSession from tailbone.db import TempmonSession
@ -40,3 +42,45 @@ class MasterView(views.MasterView):
def get_bulk_delete_session(self): def get_bulk_delete_session(self):
return RawTempmonSession() return RawTempmonSession()
def render_probes(self, obj, field):
"""
This method is used by Appliance and Client views.
"""
if not obj.probes:
return ""
route_prefix = self.get_route_prefix()
view_url = lambda p, i: self.request.route_url('tempmon.probes.view', uuid=p.uuid)
actions = [
grids.GridAction('view', icon='zoomin', url=view_url),
]
if self.request.has_perm('tempmon.probes.edit'):
url = lambda p, i: self.request.route_url('tempmon.probes.edit', uuid=p.uuid)
actions.append(grids.GridAction('edit', icon='pencil', url=url))
g = grids.Grid(
key='{}.probes'.format(route_prefix),
data=obj.probes,
columns=[
'description',
'critical_temp_min',
'good_temp_min',
'good_temp_max',
'critical_temp_max',
'status',
'enabled',
],
labels={
'critical_temp_min': "Crit. Min",
'good_temp_min': "Good Min",
'good_temp_max': "Good Max",
'critical_temp_max': "Crit. Max",
},
url=lambda p: self.request.route_url('tempmon.probes.view', uuid=p.uuid),
linked_columns=['description'],
main_actions=actions,
)
g.set_enum('status', self.enum.TEMPMON_PROBE_STATUS)
g.set_type('enabled', 'boolean')
return HTML.literal(g.render_grid())

View file

@ -61,6 +61,7 @@ class TempmonProbeView(MasterView):
grid_columns = [ grid_columns = [
'client', 'client',
'config_key', 'config_key',
'appliance',
'appliance_type', 'appliance_type',
'description', 'description',
'device_path', 'device_path',
@ -71,8 +72,10 @@ class TempmonProbeView(MasterView):
form_fields = [ form_fields = [
'client', 'client',
'config_key', 'config_key',
'appliance',
'appliance_type', 'appliance_type',
'description', 'description',
'location',
'device_path', 'device_path',
'critical_temp_max', 'critical_temp_max',
'critical_max_timeout', 'critical_max_timeout',
@ -133,6 +136,18 @@ class TempmonProbeView(MasterView):
f.set_widget('client_uuid', dfwidget.SelectWidget(values=client_values)) f.set_widget('client_uuid', dfwidget.SelectWidget(values=client_values))
f.set_label('client_uuid', "Tempmon Client") f.set_label('client_uuid', "Tempmon Client")
# appliance
f.set_renderer('appliance', self.render_appliance)
if self.creating or self.editing:
f.replace('appliance', 'appliance_uuid')
appliances = self.Session.query(tempmon.Appliance)\
.order_by(tempmon.Appliance.name)
appliance_values = [(appliance.uuid, appliance.name)
for appliance in appliances]
appliance_values.insert(0, ('', "(none)"))
f.set_widget('appliance_uuid', dfwidget.SelectWidget(values=appliance_values))
f.set_label('appliance_uuid', "Appliance")
# appliance_type # appliance_type
f.set_enum('appliance_type', self.enum.TEMPMON_APPLIANCE_TYPE) f.set_enum('appliance_type', self.enum.TEMPMON_APPLIANCE_TYPE)
@ -167,6 +182,14 @@ class TempmonProbeView(MasterView):
url = self.request.route_url('tempmon.clients.view', uuid=client.uuid) url = self.request.route_url('tempmon.clients.view', uuid=client.uuid)
return tags.link_to(text, url) return tags.link_to(text, url)
def render_appliance(self, probe, field):
appliance = probe.appliance
if not appliance:
return ""
text = six.text_type(appliance)
url = self.request.route_url('tempmon.appliances.view', uuid=appliance.uuid)
return tags.link_to(text, url)
def delete_instance(self, probe): def delete_instance(self, probe):
# bulk-delete all readings first # bulk-delete all readings first
readings = self.Session.query(tempmon.Reading)\ readings = self.Session.query(tempmon.Reading)\