[node-patches] Change in ovirt-node[master]: Add ping example page

fabiand at fedoraproject.org fabiand at fedoraproject.org
Tue Dec 11 20:09:32 UTC 2012


Fabian Deutsch has uploaded a new change for review.

Change subject: Add ping example page
......................................................................

Add ping example page

Change-Id: I6610230bd32a8602b3fdde135a5b5827337633a0
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/src/ovirt/node/plugins/example.py
A scripts/tui/src/ovirt/node/plugins/ping.py
M scripts/tui/src/ovirt/node/tui.py
M scripts/tui/src/ovirt/node/utils.py
M scripts/tui/src/ovirt/node/valid.py
M scripts/tui/src/ovirt/node/widgets.py
6 files changed, 194 insertions(+), 53 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/69/9869/1

diff --git a/scripts/tui/src/ovirt/node/plugins/example.py b/scripts/tui/src/ovirt/node/plugins/example.py
index fbb28a8..83ccac7 100644
--- a/scripts/tui/src/ovirt/node/plugins/example.py
+++ b/scripts/tui/src/ovirt/node/plugins/example.py
@@ -35,7 +35,7 @@
         nospace = lambda v: "No space allowed." if " " in v else None
 
         return {
-                "foo.hostname": ovirt.node.valid.Hostname(),
+                "foo.hostname": ovirt.node.valid.FQDN(),
                 "foo.port": ovirt.node.valid.Number(),
                 "foo.password": nospace
             }
diff --git a/scripts/tui/src/ovirt/node/plugins/ping.py b/scripts/tui/src/ovirt/node/plugins/ping.py
new file mode 100644
index 0000000..6b884f2
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/ping.py
@@ -0,0 +1,84 @@
+"""
+A ping tool page
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+from ovirt.node.plugins import Header, Label, Entry, PasswordEntry
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "Tools (ping)"
+
+    def model(self):
+        """Returns the model of this plugin
+        This is expected to parse files and all stuff to build up the model.
+        """
+        if not self._model:
+            self._model = {
+                # The target address
+                "ping.address": "127.0.0.1",
+                "ping.count": "3",
+                # The result field
+                "ping.result": ""
+            }
+        return self._model
+
+    def validators(self):
+        """Validators validate the input on change and give UI feedback
+        """
+        return {
+                # The address must be fqdn, ipv4 or ipv6 address
+                "ping.address": ovirt.node.valid.FQDNOrIPAddress(),
+                "ping.count": ovirt.node.valid.Number(min=1, max=20),
+            }
+
+    def ui_content(self):
+        """Describes the UI this plugin requires
+        This is an ordered list of (path, widget) tuples.
+        """
+        widgets = [
+            ("ping.header", Header("Ping a remote host")),
+            ("ping.address", Entry("Address")),
+            ("ping.count", Entry("Count")),
+            ("ping.result", Label("Result:")),
+        ]
+        self._widgets = dict(widgets)
+        return widgets
+
+    def on_change(self, changes):
+        """Applies the changes to the plugins model, will do all required logic
+        """
+        LOGGER.debug("New (valid) address: %s" % changes)
+        if "ping.address" in changes:
+            self._model.update(changes)
+        if "ping.count" in changes:
+            self._model.update(changes)
+
+    def on_merge(self, changes):
+        """Applies the changes to the plugins model, will do all required logic
+        """
+
+        if "ping.address" in self._model:
+            addr = self._model["ping.address"]
+            count = self._model["ping.count"]
+            LOGGER.debug("Pinging %s" % addr)
+
+            cmd = "ping"
+            if ovirt.node.valid.IPv6Address().validate(addr):
+                cmd = "ping6"
+
+            cmd = "%s -c %s %s" % (cmd, count, addr)
+            out = cmd
+            for line in ovirt.node.utils.pipe_async(cmd):
+                LOGGER.debug("xx" + line)
+                out += line
+                self._widgets["ping.result"].text("Result:\n\n%s" % out)
diff --git a/scripts/tui/src/ovirt/node/tui.py b/scripts/tui/src/ovirt/node/tui.py
index 6ac711c..fb6e0ad 100644
--- a/scripts/tui/src/ovirt/node/tui.py
+++ b/scripts/tui/src/ovirt/node/tui.py
@@ -83,7 +83,8 @@
             widget = widget_class(item.label, value)
 
             def on_item_enabled_change_cb(w, v):
-                LOGGER.debug("Model changed, updating widget '%s': %s" % (w, v))
+                LOGGER.debug("Model changed, updating widget '%s': %s" % (w,
+                                                                          v))
                 if widget.selectable() != v:
                     widget.enable(v)
             item.connect_signal("enabled[change]", on_item_enabled_change_cb)
@@ -102,6 +103,7 @@
                 except ovirt.node.plugins.InvalidData as e:
                     widget.notice = e.message
                     LOGGER.error("Invalid data when updating: %s" % e)
+                self.__loop.draw_screen()
             urwid.connect_signal(widget, 'change', on_widget_value_change)
 
         elif type(item) in [ovirt.node.plugins.Header, \
@@ -109,9 +111,10 @@
             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.popup("foo")
+                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)
 
         return widget
@@ -159,11 +162,12 @@
         self.register_hotkey(["esc"], self.quit)
         self.register_hotkey(["q"], self.quit)
 
-    def popup(self, msg=None, buttons=None):
+    def popup(self, title, msg, buttons=None):
         LOGGER.debug("Launching popup")
 
-        dialog = ovirt.node.widgets.ModalDialog(urwid.Filler(urwid.Text(msg)), "Title",
-                        "esc", self.__loop.widget)
+        dialog = ovirt.node.widgets.ModalDialog(urwid.Filler(urwid.Text(msg)),
+                                                title, "esc",
+                                                self.__loop.widget)
         self.__loop.widget = dialog
 
     def suspended(self):
diff --git a/scripts/tui/src/ovirt/node/utils.py b/scripts/tui/src/ovirt/node/utils.py
index bd6b070..b2274be 100644
--- a/scripts/tui/src/ovirt/node/utils.py
+++ b/scripts/tui/src/ovirt/node/utils.py
@@ -55,3 +55,23 @@
     if stderr:
         LOGGER.warning("error '%s'" % stderr)
     return (system_cmd.returncode == 0, stdout)
+
+
+def pipe_async(cmd, stdin=None):
+    """Run a command interactively and yields the process output.
+    This functions allows to pass smoe input to a running command.
+
+    Args:
+        cmd: Commandline to be run
+        stdin: Data to be written to cmd's stdin
+
+    Yields:
+        Lines read from stdout
+    """
+    LOGGER.debug("run async '%s'" % cmd)
+    process = popen_closefds(cmd, shell=True, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+    if stdin:
+        process.stdin.write(stdin)
+    while process.poll() != 0:
+        yield process.stdout.readline()
diff --git a/scripts/tui/src/ovirt/node/valid.py b/scripts/tui/src/ovirt/node/valid.py
index b59f551..602c03f 100644
--- a/scripts/tui/src/ovirt/node/valid.py
+++ b/scripts/tui/src/ovirt/node/valid.py
@@ -2,6 +2,7 @@
 
 import re
 import logging
+import socket
 
 import ovirt.node.plugins
 
@@ -10,16 +11,39 @@
 LOGGER = logging.getLogger(__name__)
 
 
-class RegexValidator(str):
-    # pattern defined by subclass
-    # description defined by subclass
+class Validator(object):
     __exception_msg = "The field must contain {description}."
+    description = None
+
+    def validate(self, value):
+        """Validate a given value, return true if valid.
+
+        Args:
+            value: The value to be validated
+
+        Returns:
+            True if valid
+        """
+        raise Exception("To be implemented by subclass")
 
     def __call__(self, value):
-        if re.search(self.pattern, value) == None:
-            msg = self.__exception_msg.format(description=self.description)
-            raise ovirt.node.plugins.InvalidData(msg)
+        if not self.description:
+            self.description = self.__doc__
+        if not self.validate(value):
+            self.raise_exception()
         return True
+
+    def raise_exception(self):
+        msg = self.__exception_msg.format(description=self.description)
+        raise ovirt.node.plugins.InvalidData(msg)
+
+class RegexValidator(Validator):
+    # pattern defined by subclass
+
+    def validate(self, value):
+        if type(self.pattern) in [str, unicode]:
+            self.pattern = (self.pattern, )
+        return re.compile(*self.pattern).search(value) != None
 
 
 class Text(RegexValidator):
@@ -35,58 +59,65 @@
 class Number(RegexValidator):
     description = "a number"
     pattern = "^\d+$"
+    minmax = None
 
+    def __init__(self, min=None, max=None):
+        if min or max:
+            self.minmax = (min, max)
+            self.description = "a number (%s<%s)" % (min, max)
+
+    def validate(self, value):
+        valid = True
+        RegexValidator.validate(self, value)
+        try:
+            value = int(value)
+            LOGGER.debug("val %d" % value)
+            min, max = self.minmax
+            if min and value < min:
+                LOGGER.debug("min %s" % min)
+                self.raise_exception()
+            if max and value > max:
+                LOGGER.debug("max %s" % max)
+                self.raise_exception()
+        except:
+            valid = False
+        return valid
 
 class NoSpaces(RegexValidator):
     description = "a string without spaces"
     pattern = "^\S*$"
 
 
-class Hostname(RegexValidator):
-    description = "a valid hostname"
-    pattern = ("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)" +
-               "*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$")
+class FQDN(RegexValidator):
+    description = "a valid FQDN"
+    pattern = ("^(([a-z]|[a-z][a-z0-9\-]*[a-z0-9])\.)" +
+               "*([a-z]|[a-z][a-z0-9\-]*[a-z0-9])$", re.I)
 
 
-class IPv4Address(RegexValidator):
+class IPv4Address(Validator):
     description = "a valid IPv4 address"
-    pattern = ("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)" +
-               "{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")
+    family = socket.AF_INET
+    def validate(self, value):
+        valid = True
+        try:
+            socket.inet_pton(self.family, value)
+        except:
+            valid = False
+        return valid
 
 
-class IPv6Address(RegexValidator):
+class IPv6Address(IPv4Address):
     description = "a valid IPv6 address"
-    pattern = ("/^(?>(?>([a-f0-9]{1,4})(?>:(?1)){7}|(?!(?:.*[a-f0-9](?>:|$" +
-               ")){7,})((?1)(?>:(?1)){0,5})?::(?2)?)|(?>(?>(?1)(?>:(?1)){5}" +
-               ":|(?!(?:.*[a-f0-9]:){5,})(?3)?::(?>((?1)(?>:(?1)){0,3}):)?)?" +
-               "(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?" +
-               "4)){3}))$/iD")
+    family = socket.AF_INET6
 
 
-class OrCombinedValidator(str):
-    _validators = None
+class FQDNOrIPAddress(Validator):
+    """Allows any FQDN, IPv4 or IPv6 address
+    """
+    description = " or ".join([FQDN.description, IPv4Address.description,
+                               IPv6Address.description])
 
-    def __init__(self, validators):
-        self._validators = validators
-
-    def __call__(self, value):
-        is_valid = False
-        msgs = []
-
-        for v in self._validators:
-            LOGGER.debug(v)
-            try:
-                msg = v(value)
-                if msg:
-                    msgs.append(msg)
-                else:
-                    is_valid = True
-                    break
-            except ovirt.node.plugins.InvalidData as e:
-                msgs.append(str(e))
-
-        if not is_valid:
-            msg = ", ".join(msgs)
-            raise ovirt.node.plugins.InvalidData(msg)
-
-        return True
+    def validate(self, value):
+        return (FQDN().validate(value) or \
+                IPv4Address().validate(value) or \
+                IPv6Address().validate(value))
diff --git a/scripts/tui/src/ovirt/node/widgets.py b/scripts/tui/src/ovirt/node/widgets.py
index 471841d..3a4e9d4 100644
--- a/scripts/tui/src/ovirt/node/widgets.py
+++ b/scripts/tui/src/ovirt/node/widgets.py
@@ -134,6 +134,7 @@
             self._label.set_text(value)
         return self._label.get_text()
 
+
 class Header(Label):
     """A read only widget representing a header
     """
@@ -185,6 +186,7 @@
 
         super(Entry, self).__init__(self._pile)
 
+
 class PasswordEntry(Entry):
     def __init__(self, label, value=None):
         super(PasswordEntry, self).__init__(label, value, mask="*")


--
To view, visit http://gerrit.ovirt.org/9869
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6610230bd32a8602b3fdde135a5b5827337633a0
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