[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