[node-patches] Change in ovirt-node[master]: Add ovirt.node.plugins.status
fabiand at fedoraproject.org
fabiand at fedoraproject.org
Tue Dec 11 20:09:33 UTC 2012
Fabian Deutsch has uploaded a new change for review.
Change subject: Add ovirt.node.plugins.status
......................................................................
Add ovirt.node.plugins.status
Change-Id: I9e8bf07f6a577a05628b77cfd2bd075a3715fc9f
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/src/ovirt/node/plugins/__init__.py
M scripts/tui/src/ovirt/node/plugins/kdump.py
A scripts/tui/src/ovirt/node/plugins/status.py
M scripts/tui/src/ovirt/node/tui.py
M scripts/tui/src/ovirt/node/widgets.py
5 files changed, 219 insertions(+), 83 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/81/9881/1
diff --git a/scripts/tui/src/ovirt/node/plugins/__init__.py b/scripts/tui/src/ovirt/node/plugins/__init__.py
index fa135e6..d9c26f0 100644
--- a/scripts/tui/src/ovirt/node/plugins/__init__.py
+++ b/scripts/tui/src/ovirt/node/plugins/__init__.py
@@ -87,24 +87,23 @@
"""
raise NotImplementedError()
- def validate(self, path, value):
- """Validates a value against the validator of a given path
+ def validate(self, changes):
+ """Test changes against the validators
Args:
- path: A model path for a validator
- value: The value to be validated
+ changes: A dict of (path, value) to be checked
Returns:
True on a valid value or if there is no validator for a path
-
Raises:
InvalidData on any invalid data
"""
- if path in self.validators():
- msg = self.validators()[path](value)
- # True and None are allowed values
- if msg not in [True, None]:
- raise ovirt.node.plugins.InvalidData(msg)
+ for path, value in changes.items():
+ if path in self.validators():
+ msg = self.validators()[path](value)
+ # True and None are allowed values
+ if msg not in [True, None]:
+ raise ovirt.node.plugins.InvalidData(msg)
return True
def has_ui(self):
@@ -150,7 +149,7 @@
"""
raise NotImplementedError()
- def validate(self, model=None):
+ def check_semantics(self, model=None):
"""Simulate a complete model change.
This runs all current model values throught the checks to see
if the model validates.
@@ -217,19 +216,26 @@
return self.on_merge(real_changes)
+# http://stackoverflow.com/questions/739654/understanding-python-decorators
class Widget(object):
_signal_cbs = None
- signals = []
- signaling_properties = []
def __init__(self):
"""Registers all widget signals.
All signals must be given in self.signals
"""
- for name in self.signals:
- self._register_signal(name)
- for name in self.signaling_properties:
- self._register_signaling_property(name)
+ LOGGER.debug("Initializing new %s" % self)
+
+ @staticmethod
+ def signal_change(func):
+ """A decorator for methods which should emit signals
+ """
+ def wrapper(self, userdata=None, *args, **kwargs):
+ signame = func.__name__
+ self._register_signal(signame)
+ self.emit_signal(signame, userdata)
+ return func(self, userdata)
+ return wrapper
def _register_signal(self, name):
"""Each signal that get's emitted must be registered using this
@@ -241,12 +247,15 @@
self._signal_cbs = {}
if name not in self._signal_cbs:
self._signal_cbs[name] = []
-# LOGGER.debug("Registered new signal '%s' for '%s'" % (name, self))
+ LOGGER.debug("Registered new signal '%s' for '%s'" % (name, self))
def connect_signal(self, name, cb):
"""Connect an callback to a signal
"""
- assert name in self._signal_cbs, "Unregistered signal '%s'" % name
+ if not self._signal_cbs:
+ raise Exception("Signals not initialized %s for %s" % (name, self))
+ if name not in self._signal_cbs:
+ raise Exception("Unregistered signal %s for %s" % (name, self))
self._signal_cbs[name].append(cb)
def emit_signal(self, name, userdata=None):
@@ -258,45 +267,67 @@
LOGGER.debug("CB for sig %s: %s" % (name, cb))
cb(self, userdata)
- def _register_signaling_property(self, name):
- LOGGER.debug("Registered new property '%s' for '%s'" % (name, self))
- if "_%s" % name not in self.__dict__:
- self.__dict__["_%s" % name] = None
- self._register_signal("%s[change]" % name)
-
- def _signaling_property(self, name, valid_value_cb, new_value):
- if valid_value_cb():
- self.emit_signal("%s[change]" % name, new_value)
- self.__dict__["_%s" % name] = new_value
- return self.__dict__["_%s" % name]
+ def set_text(self, value):
+ """A general way to set the "text" of a widget
+ """
+ raise NotImplementedError
class InputWidget(Widget):
- signaling_properties = ["enabled"]
+ """
+ """
+ def __init__(self, is_enabled):
+ self.enabled(is_enabled)
+ super(InputWidget, self).__init__()
+
+ @Widget.signal_change
def enabled(self, is_enabled=None):
- return self._signaling_property("enabled", \
- lambda: is_enabled in [True, False],
- is_enabled)
+ if is_enabled in [True, False]:
+ self._enabled = is_enabled
+ return self._enabled
+
+ @Widget.signal_change
+ def text(self, text=None):
+ if text != None:
+ self._text = text
+ return self._text
+
+ def set_text(self, txt):
+ self.text(txt)
class Label(Widget):
"""Represents a r/o label
"""
- signaling_properties = ["text"]
def __init__(self, text):
- self._text = text
+ self.text(text)
super(Label, self).__init__()
- def text(self, value=None):
- return self._signaling_property("text", \
- lambda: value != None,
- value)
+ @Widget.signal_change
+ def text(self, text=None):
+ if text != None:
+ self._text = text
+ return self._text
+
+ def set_text(self, txt):
+ self.text(txt)
class Header(Label):
pass
+
+
+class KeywordLabel(Label):
+ """A label consisting of a prominent keyword and a value.
+ E.g.: <b>Networking:</b> Enabled
+ """
+
+ def __init__(self, keyword, text=""):
+ super(Label, self).__init__()
+ self.keyword = keyword
+ self.text(text)
class Entry(InputWidget):
@@ -304,27 +335,25 @@
TODO multiline
"""
- def __init__(self, label, value=None, initial_value_from_model=True,
- enabled=True):
+ def __init__(self, label, value=None, enabled=True):
self.label = label
- self.value = value
- self.initial_value_from_model = initial_value_from_model
- self._enabled = enabled
- super(Entry, self).__init__()
+ self._text = value
+ super(Entry, self).__init__(enabled)
class PasswordEntry(Entry):
pass
-class Button(Label, InputWidget):
- def __init__(self, label="Save", enabled=True):
- self._enabled = enabled
- super(Button, self).__init__(label)
+class Button(Label):
+ def __init__(self, label, enabled=True):
+ Label.__init__(self, label)
+# InputWidget.__init__(self, enabled)
class SaveButton(Button):
- pass
+ def __init__(self, enabled=True):
+ super(SaveButton, self).__init__(self, "Save", enabled)
class Divider(Widget):
@@ -333,18 +362,21 @@
class Options(Widget):
- signals = ["change"]
- signaling_properties = ["option"]
def __init__(self, label, options):
self.label = label
self.options = options
+ self.option(options[0])
super(Options, self).__init__()
+ @Widget.signal_change
def option(self, option=None):
- return self._signaling_property("option", \
- lambda: option in self.options,
- option)
+ if option in self.options:
+ self._option = option
+ return self._option
+
+ def set_text(self, txt):
+ self.option(txt)
class InvalidData(Exception):
diff --git a/scripts/tui/src/ovirt/node/plugins/kdump.py b/scripts/tui/src/ovirt/node/plugins/kdump.py
index a4e1c12..2f62b66 100644
--- a/scripts/tui/src/ovirt/node/plugins/kdump.py
+++ b/scripts/tui/src/ovirt/node/plugins/kdump.py
@@ -43,8 +43,9 @@
def validators(self):
"""Validators validate the input on change and give UI feedback
"""
+ options = dict(self._types).keys()
return {
- "kdump.type": ovirt.node.valid.Options(dict(self._types).keys()),
+ "kdump.type": ovirt.node.valid.Options(options),
"kdump.ssh_location": ovirt.node.valid.NoSpaces(),
"kdump.nfs_location": ovirt.node.valid.NoSpaces(),
}
@@ -86,6 +87,6 @@
"""
if effective_changes:
- LOGGER.debug("Generating kdump.conf according to model and changes")
+ LOGGER.debug("Generating conf according to model and changes")
else:
- LOGGER.debug("Generating no new kdump.conf as there are no changes")
+ LOGGER.debug("Generating no new conf as there are no changes")
diff --git a/scripts/tui/src/ovirt/node/plugins/status.py b/scripts/tui/src/ovirt/node/plugins/status.py
new file mode 100644
index 0000000..3814517
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/status.py
@@ -0,0 +1,57 @@
+"""
+Status plugin
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.plugins
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+ _model = None
+ _widgets = None
+
+ def name(self):
+ return "Status"
+
+ def rank(self):
+ return 10
+
+ def model(self):
+ if not self._model:
+ self._model = {
+ "status.networking": "On",
+ "status.logs": "Local",
+ "status.vms.running": "42",
+ }
+ return self._model
+
+ def validators(self):
+ """Validators validate the input on change and give UI feedback
+ """
+ return {}
+
+ def ui_config(self):
+ return {
+ "save_button": False
+ }
+
+ def ui_content(self):
+ """Describes the UI this plugin requires
+ This is an ordered list of (path, widget) tuples.
+ """
+ widgets = [
+ ("status.networking",
+ ovirt.node.plugins.KeywordLabel("Networking")),
+ ("status.logs",
+ ovirt.node.plugins.KeywordLabel("Logs")),
+ ("status.vms.running",
+ ovirt.node.plugins.KeywordLabel("Running VMs")),
+ ]
+ # Save it "locally" as a dict, for better accessability
+ self._widgets = dict(widgets)
+ return widgets
diff --git a/scripts/tui/src/ovirt/node/tui.py b/scripts/tui/src/ovirt/node/tui.py
index 9a937ce..8ab13fe 100644
--- a/scripts/tui/src/ovirt/node/tui.py
+++ b/scripts/tui/src/ovirt/node/tui.py
@@ -41,6 +41,8 @@
('plugin.widget.divider', 'dark gray', ''),
('plugin.widget.button', 'dark blue', ''),
('plugin.widget.button.disabled', 'light gray', ''),
+ ('plugin.widget.label', '', ''),
+ ('plugin.widget.label.keyword', 'bold', ''),
]
def __init__(self, app):
@@ -75,6 +77,7 @@
ovirt.node.plugins.SaveButton: ovirt.node.widgets.Button,
ovirt.node.plugins.Divider: ovirt.node.widgets.Divider,
ovirt.node.plugins.Options: ovirt.node.widgets.Options,
+ ovirt.node.plugins.KeywordLabel: ovirt.node.widgets.KeywordLabel,
}
assert type(item) in item_to_widget_map.keys(), \
@@ -85,11 +88,7 @@
if type(item) in [ovirt.node.plugins.Entry, \
ovirt.node.plugins.PasswordEntry]:
- value = None
- if item.initial_value_from_model:
- value = plugin.model()[path]
-
- widget = widget_class(item.label, value)
+ widget = widget_class(item.label)
widget.enable(item.enabled)
@@ -98,14 +97,15 @@
v))
if widget.selectable() != v:
widget.enable(v)
- item.connect_signal("enabled[change]", on_item_enabled_change_cb)
+ item.connect_signal("enabled", on_item_enabled_change_cb)
def on_widget_value_change(widget, new_value):
LOGGER.debug("Widget changed, updating model '%s'" % path)
try:
- plugin.validate(path, new_value)
- plugin._on_ui_change({path: new_value})
+ change = {path: new_value}
+ plugin.validate(change)
+ plugin._on_ui_change(change)
widget.notice = ""
widget.valid(True)
plugin.__save_button.enable(True)
@@ -126,15 +126,19 @@
urwid.connect_signal(widget, 'change', on_widget_value_change)
elif type(item) in [ovirt.node.plugins.Header, \
- ovirt.node.plugins.Label]:
- widget = widget_class(item.text())
+ ovirt.node.plugins.Label,
+ ovirt.node.plugins.KeywordLabel]:
+ if type(item) is ovirt.node.plugins.KeywordLabel:
+ widget = widget_class(item.keyword, item.text())
+ else:
+ widget = widget_class(item.text())
def on_item_text_change_cb(w, v):
LOGGER.debug("Model changed, updating widget '%s': %s" % (w,
v))
widget.text(v)
self.__loop.draw_screen()
- item.connect_signal("text[change]", on_item_text_change_cb)
+ item.connect_signal("text", on_item_text_change_cb)
elif type(item) in [ovirt.node.plugins.Button,
ovirt.node.plugins.SaveButton]:
@@ -162,6 +166,12 @@
plugin._on_ui_change({path: data})
urwid.connect_signal(widget, "change", on_widget_change_cb)
+ if type(item) in [ovirt.node.plugins.Entry,
+ ovirt.node.plugins.PasswordEntry,
+ ovirt.node.plugins.KeywordLabel,
+ ovirt.node.plugins.Options]:
+ widget.set_text(plugin.model()[path])
+
return widget
def __build_plugin_widget(self, plugin):
@@ -175,15 +185,16 @@
ui_content = plugin.ui_content()
config.update(plugin.ui_config())
- for path, item in ui_content:
- widget = self.__build_widget_for_item(plugin, path, item)
- widgets.append(("flow", widget))
-
# Always create the SaveButton, but only display it if requested
# FIXME introduce a widget for the plugin page
save = ovirt.node.widgets.Button("Save")
urwid.connect_signal(save, 'click', lambda x: plugin._on_ui_save())
plugin.__save_button = save
+
+ for path, item in ui_content:
+ widget = self.__build_widget_for_item(plugin, path, item)
+ widgets.append(("flow", widget))
+
if config["save_button"]:
widgets.append(urwid.Filler(save))
@@ -191,7 +202,7 @@
LOGGER.debug("Triggering initial sematic checks for '%s'" % plugin)
try:
- plugin.validate()
+ plugin.check_semantics()
except:
self.notify("error", "Initial model validation failed.")
diff --git a/scripts/tui/src/ovirt/node/widgets.py b/scripts/tui/src/ovirt/node/widgets.py
index 3254f19..0d09392 100644
--- a/scripts/tui/src/ovirt/node/widgets.py
+++ b/scripts/tui/src/ovirt/node/widgets.py
@@ -139,14 +139,40 @@
self._label.set_text(value)
return self._label.get_text()
+ def set_text(self, txt):
+ self.text(txt)
+
class Header(Label):
"""A read only widget representing a header
"""
+ _header_attr = "plugin.widget.header"
def __init__(self, text):
super(Header, self).__init__("\n %s\n" % text)
- self._label_attrmap.set_attr_map({None: "plugin.widget.header"})
+ self._label_attrmap.set_attr_map({None: self._header_attr})
+
+
+class KeywordLabel(Label):
+ """A read only widget consisting of a "<b><keyword>:</b> <value>"
+ """
+ _keyword_attr = "plugin.widget.label.keyword"
+ _text_attr = "plugin.widget.label"
+
+ def __init__(self, keyword, text=""):
+ super(KeywordLabel, self).__init__(text)
+ self._keyword = keyword
+ self.text(text)
+# self._label_attrmap.set_attr_map({None: ""})
+
+ def text(self, text=None):
+ if text is not None:
+ self._text = text
+ keyword_markup = (self._keyword_attr, self._keyword)
+ text_markup = (self._text_attr, self._text)
+ markup = [keyword_markup, ": ", text_markup]
+ self._label.set_text(markup)
+ return self._text
class Entry(urwid.WidgetWrap):
@@ -170,7 +196,7 @@
attr_map = {None: "plugin.widget.entry.frame.invalid"}
self._linebox_attrmap.set_attr_map(attr_map)
- def __init__(self, label, value=None, mask=None):
+ def __init__(self, label, mask=None):
self._label = urwid.Text("\n" + label + ":")
self._edit = urwid.Edit(mask=mask)
self._edit_attrmap = urwid.AttrMap(self._edit, "plugin.widget.entry")
@@ -185,26 +211,25 @@
self._pile = urwid.Pile([self._columns, self._notice_attrmap])
- if value:
- self._edit.set_edit_text(value)
-
def on_widget_change_cb(widget, new_value):
urwid.emit_signal(self, 'change', self, new_value)
urwid.connect_signal(self._edit, 'change', on_widget_change_cb)
super(Entry, self).__init__(self._pile)
+ def set_text(self, txt):
+ self._edit.set_edit_text(txt)
+
class PasswordEntry(Entry):
- def __init__(self, label, value=None):
- super(PasswordEntry, self).__init__(label, value, mask="*")
+ def __init__(self, label):
+ super(PasswordEntry, self).__init__(label, mask="*")
class Button(urwid.WidgetWrap):
signals = ["click"]
selectable = lambda self: True
-
def __init__(self, label):
self._button = urwid.Button(label)
@@ -264,6 +289,16 @@
data = self._button_to_key[widget]
urwid.emit_signal(self, "change", widget, data)
+ def select(self, key):
+ for button in self._buttons:
+ if button in self._button_to_key:
+ bkey = self._button_to_key[button]
+ if key == bkey:
+ button.set_state(True)
+
+ def set_text(self, txt):
+ self.select(txt)
+
#https://github.com/pazz/alot/blob/master/alot/widgets/globals.py
class ChoiceWidget(urwid.Text):
--
To view, visit http://gerrit.ovirt.org/9881
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9e8bf07f6a577a05628b77cfd2bd075a3715fc9f
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-node
Gerrit-Branch: master
Gerrit-Owner: Fabian Deutsch <fabiand at fedoraproject.org>
More information about the node-patches
mailing list