[node-patches] Change in ovirt-node[master]: network: Improve page and NIC details dialog

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


Fabian Deutsch has uploaded a new change for review.

Change subject: network: Improve page and NIC details dialog
......................................................................

network: Improve page and NIC details dialog

Change-Id: Ie20873b30fa18250fa9fdbe7e84753cb96bd67a8
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/src/ovirt/node/app.py
M scripts/tui/src/ovirt/node/config/defaults.py
M scripts/tui/src/ovirt/node/plugins.py
M scripts/tui/src/ovirt/node/setup/logging_page.py
M scripts/tui/src/ovirt/node/setup/network_page.py
M scripts/tui/src/ovirt/node/setup/ping.py
M scripts/tui/src/ovirt/node/setup/security_page.py
M scripts/tui/src/ovirt/node/setup/status_page.py
M scripts/tui/src/ovirt/node/setup/support_page.py
M scripts/tui/src/ovirt/node/ui/__init__.py
M scripts/tui/src/ovirt/node/ui/builder.py
M scripts/tui/src/ovirt/node/ui/tui.py
M scripts/tui/src/ovirt/node/ui/widgets.py
M scripts/tui/src/ovirt/node/utils/network.py
M scripts/tui/src/ovirt/node/valid.py
15 files changed, 471 insertions(+), 215 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/34/9934/1

diff --git a/scripts/tui/src/ovirt/node/app.py b/scripts/tui/src/ovirt/node/app.py
index 483c96d..65e5cd7 100644
--- a/scripts/tui/src/ovirt/node/app.py
+++ b/scripts/tui/src/ovirt/node/app.py
@@ -46,7 +46,7 @@
     def __init__(self, plugin_base, ui_backend="urwid"):
         super(Application, self).__init__()
         self.__parse_cmdline()
-        
+
         ui_backend_class = {
             "urwid": ovirt.node.ui.tui.UrwidTUI
         }[ui_backend]
@@ -87,7 +87,8 @@
     def __check_terminal_size(self):
         cols, rows = self.ui.size()
         if cols < 80 or rows < 24:
-            self.logger.warning("Window size is too small: %dx%d" % (cols, rows))
+            self.logger.warning("Window size is too small: %dx%d" % (cols,
+                                                                     rows))
 
     def model(self, plugin_name):
         model = None
diff --git a/scripts/tui/src/ovirt/node/config/defaults.py b/scripts/tui/src/ovirt/node/config/defaults.py
index ee1028f..7ca3438 100644
--- a/scripts/tui/src/ovirt/node/config/defaults.py
+++ b/scripts/tui/src/ovirt/node/config/defaults.py
@@ -29,7 +29,7 @@
 There are classes for all components which can be configured through that
 central configuration file.
 Each class (for a component) can have a configure and apply_config method. Look
-at the CentralNodeConfiguration for more informations.
+at the NodeConfigFileSection for more informations.
 
 Each class should implement a configure method, mainly to define all the
 required arguments (or keys).
@@ -155,9 +155,11 @@
         return self.provider.get_dict()
 
 
-class CentralNodeConfiguration(base.Base):
+class NodeConfigFileSection(base.Base):
+    none_value = ""
+
     def __init__(self, cfgfile=None):
-        super(CentralNodeConfiguration, self).__init__()
+        super(NodeConfigFileSection, self).__init__()
         self.defaults = cfgfile or ConfigFile()
 
     def update(self, *args, **kwargs):
@@ -181,7 +183,7 @@
         values = ()
         cfg = self.defaults.get_dict()
         for key in self.keys:
-            value = cfg[key] if key in cfg else ""
+            value = cfg[key] if key in cfg else self.none_value
             values += (value,)
         assert len(varnames) == len(values)
         return zip(varnames, values)
@@ -196,7 +198,8 @@
 
     def _map_config_and_update_defaults(self, *args, **kwargs):
         assert len(args) == 0
-        assert (set(self.keys) ^ set(kwargs.keys())) == set()
+        assert (set(self.keys) ^ set(kwargs.keys())) == set(), \
+               "Keys: %s, Args: %s" % (self.keys, kwargs)
         new_dict = {k.upper(): v for k, v in kwargs.items()}
         self.defaults.update(new_dict, remove_empty=True)
 
@@ -207,7 +210,7 @@
         ...     keys = None
         ...     def _map_config_and_update_defaults(self, *args, **kwargs):
         ...         return kwargs
-        ...     @CentralNodeConfiguration.map_and_update_defaults_decorator
+        ...     @NodeConfigFileSection.map_and_update_defaults_decorator
         ...     def meth(self, a, b, c):
         ...         assert type(a) is int
         ...         assert type(b) is int
@@ -218,6 +221,7 @@
         {'OVIRT_A': 1, 'OVIRT_B': 2, 'OVIRT_C': 'c3'}
         """
         def wrapper(self, *args, **kwargs):
+            assert kwargs == {}, "kwargs are not allowed for these functions"
             if len(self.keys) != len(args):
                 raise Exception("There are not enough arguments given for " +
                                 "%s of %s" % (func, self))
@@ -230,7 +234,7 @@
         return wrapper
 
 
-class Network(CentralNodeConfiguration):
+class Network(NodeConfigFileSection):
     """Sets network stuff
     - OVIRT_BOOTIF
     - OVIRT_IP_ADDRESS, OVIRT_IP_NETMASK, OVIRT_IP_GATEWAY
@@ -246,14 +250,14 @@
     >>> data[:3]
     [('iface', 'eth0'), ('bootproto', 'static'), ('ipaddr', '10.0.0.1')]
     >>> data [3:]
-    [('netmask', '255.0.0.0'), ('gw', '10.0.0.255'), ('vlanid', '20')]
+    [('netmask', '255.0.0.0'), ('gateway', '10.0.0.255'), ('vlanid', '20')]
 
     >>> n.clear()
     >>> data = n.retrieve()
     >>> data [:3]
-    [('iface', None), ('bootproto', None), ('ipaddr', None)]
+    [('iface', ''), ('bootproto', ''), ('ipaddr', '')]
     >>> data [3:]
-    [('netmask', None), ('gw', None), ('vlanid', None)]
+    [('netmask', ''), ('gateway', ''), ('vlanid', '')]
     """
     keys = ("OVIRT_BOOTIF",
             "OVIRT_BOOTPROTO",
@@ -262,18 +266,18 @@
             "OVIRT_GATEWAY",
             "OVIRT_VLAN")
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
-    def update(self, iface, bootproto, ipaddr=None, netmask=None, gw=None,
+    @NodeConfigFileSection.map_and_update_defaults_decorator
+    def update(self, iface, bootproto, ipaddr=None, netmask=None, gateway=None,
                   vlanid=None):
-        if bootproto not in ["static", "none", "dhcp"]:
+        if bootproto not in ["static", "none", "dhcp", None]:
             raise exceptions.InvalidData("Unknown bootprotocol: %s" %
                                          bootproto)
-        (valid.IPv4Address() | valid.Empty())(ipaddr)
-        (valid.IPv4Address() | valid.Empty())(netmask)
-        (valid.IPv4Address() | valid.Empty())(gw)
+        (valid.IPv4Address() | valid.Empty(or_none=True))(ipaddr)
+        (valid.IPv4Address() | valid.Empty(or_none=True))(netmask)
+        (valid.IPv4Address() | valid.Empty(or_none=True))(gateway)
 
 
-class Nameservers(CentralNodeConfiguration):
+class Nameservers(NodeConfigFileSection):
     """Configure nameservers
     >>> fn = "/tmp/cfg_dummy"
     >>> cfgfile = ConfigFile(fn, SimpleProvider)
@@ -286,7 +290,7 @@
     """
     keys = ("OVIRT_DNS",)
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, servers):
         assert type(servers) is list
         servers = filter(lambda i: i.strip() not in ["", None], servers)
@@ -296,7 +300,7 @@
                 }
 
     def retrieve(self):
-        cfg = dict(CentralNodeConfiguration.retrieve(self))
+        cfg = dict(NodeConfigFileSection.retrieve(self))
         return {
                 "servers": cfg["servers"].split(",")
                 }
@@ -346,7 +350,7 @@
         utils.fs.persist_config("/etc/resolv.conf")
 
 
-class Timeservers(CentralNodeConfiguration):
+class Timeservers(NodeConfigFileSection):
     """Configure timeservers
 
     >>> fn = "/tmp/cfg_dummy"
@@ -360,7 +364,7 @@
     """
     keys = ("OVIRT_NTP",)
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, servers):
         assert type(servers) is list
         servers = filter(lambda i: i.strip() not in ["", None], servers)
@@ -370,13 +374,13 @@
                 }
 
     def retrieve(self):
-        cfg = dict(CentralNodeConfiguration.retrieve(self))
+        cfg = dict(NodeConfigFileSection.retrieve(self))
         return {
                 "servers": cfg["servers"].split(",")
                 }
 
 
-class Syslog(CentralNodeConfiguration):
+class Syslog(NodeConfigFileSection):
     """Configure rsyslog
 
     >>> fn = "/tmp/cfg_dummy"
@@ -391,13 +395,13 @@
     keys = ("OVIRT_SYSLOG_SERVER",
             "OVIRT_SYSLOG_PORT")
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, server, port):
         valid.FQDNOrIPAddress()(server)
         valid.Port()(port)
 
 
-class Collectd(CentralNodeConfiguration):
+class Collectd(NodeConfigFileSection):
     """Configure collectd
 
     >>> fn = "/tmp/cfg_dummy"
@@ -412,13 +416,13 @@
     keys = ("OVIRT_COLLECTD_SERVER",
             "OVIRT_COLLECTD_PORT")
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, server, port):
         valid.FQDNOrIPAddress()(server)
         valid.Port()(port)
 
 
-class RHN(CentralNodeConfiguration):
+class RHN(NodeConfigFileSection):
     keys = ("OVIRT_RHN_TYPE",
             "OVIRT_RHN_URL",
             "OVIRT_RHN_CA_CERT",
@@ -431,13 +435,13 @@
             "OVIRT_RHN_PROXYUSER",
             "OVIRT_RHN_PROXYPASSWORD")
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, rhntype, url, ca_cert, username, password, profile,
                   activationkey, org, proxy, proxyuser, proxypassword):
         pass
 
 
-class KDump(CentralNodeConfiguration):
+class KDump(NodeConfigFileSection):
     """Configure kdump
 
     >>> fn = "/tmp/cfg_dummy"
@@ -452,13 +456,13 @@
     keys = ("OVIRT_KDUMP_NFS",
             "OVIRT_KDUMP_SSH")
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, nfs, ssh):
         valid.FQDNOrIPAddress()(nfs)
         valid.URL()(ssh)
 
 
-class iSCSI(CentralNodeConfiguration):
+class iSCSI(NodeConfigFileSection):
     """Configure iSCSI
 
     >>> fn = "/tmp/cfg_dummy"
@@ -476,13 +480,13 @@
             "OVIRT_ISCSI_TARGET_IP",
             "OVIRT_ISCSI_TARGET_PORT")
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, name, target_name, target_host, target_port):
         # FIXME add validation
         pass
 
 
-class SNMP(CentralNodeConfiguration):
+class SNMP(NodeConfigFileSection):
     """Configure SNMP
 
     >>> fn = "/tmp/cfg_dummy"
@@ -494,13 +498,13 @@
     """
     keys = ("OVIRT_SNMP_PASSWORD",)
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, password):
         # FIXME add validation
         pass
 
 
-class Netconsole(CentralNodeConfiguration):
+class Netconsole(NodeConfigFileSection):
     """Configure netconsole
 
     >>> fn = "/tmp/cfg_dummy"
@@ -515,13 +519,13 @@
     keys = ("OVIRT_NETCONSOLE_SERVER",
             "OVIRT_NETCONSOLE_PORT")
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, server, port):
         # FIXME add validation
         pass
 
 
-class CIM(CentralNodeConfiguration):
+class CIM(NodeConfigFileSection):
     """Configure CIM
 
     >>> fn = "/tmp/cfg_dummy"
@@ -533,7 +537,7 @@
     """
     keys = ("OVIRT_CIM_ENABLED",)
 
-    @CentralNodeConfiguration.map_and_update_defaults_decorator
+    @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, enabled):
         return {
                 "OVIRT_CIM_ENABLED": "1" if utils.parse_bool(enabled) else "0"
diff --git a/scripts/tui/src/ovirt/node/plugins.py b/scripts/tui/src/ovirt/node/plugins.py
index 4f55be2..3d82a31 100644
--- a/scripts/tui/src/ovirt/node/plugins.py
+++ b/scripts/tui/src/ovirt/node/plugins.py
@@ -206,7 +206,7 @@
         Args:
             changes (dict): changes which shall be applied to the model
         Returns:
-            True on success, or False otherwise
+            (False)True on (no)success or a ui.Dialog/ui.Page
         Raises:
             Errors
         """
@@ -235,7 +235,11 @@
         """
         self.logger.debug("Request to apply model changes")
         effective_changes = self.pending_changes() or {}
-        successfull_merge = self.on_merge(effective_changes) is not False
+        successfull_merge = self.on_merge(effective_changes)
+        if successfull_merge is None:
+            #raise Exception("on_save needs to return " +
+            #                "True/False or a Page/Dialog")
+            successfull_merge = True
         if successfull_merge:
             self.logger.info("Changes were merged successfully")
             self.__changes = {}
@@ -273,3 +277,83 @@
         else:
             self.logger.debug("No effective changes detected.")
         return effective_changes if len(effective_changes) > 0 else None
+
+
+class ChangesHelper(base.Base):
+    def __init__(self, changes):
+        self.changes = changes
+
+    def if_keys_exist_run(self, keys, func):
+        """Run func if all keys are present in the changes.
+        The values of the change entries for each key in keys are passed as
+        arguments to the function func
+
+        >>> changes = {
+        ... "foo": 1,
+        ... "bar": 2,
+        ... "baz": 0
+        ... }
+        >>> helper = ChangesHelper(changes)
+        >>> cb = lambda foo, bar: foo + bar
+        >>> helper.if_keys_exist_run(["foo", "bar"], cb)
+        3
+        >>> helper.if_keys_exist_run(["foo", "baz"], cb)
+        1
+        >>> helper.if_keys_exist_run(["win", "dow"], cb)
+
+        Args:
+            keys: A list of keys which need to be present in the changes
+            func: The function to be run, values of keys are passed as args
+        """
+        if self.all_keys_in_change(keys):
+            return func(*[self.changes[key] for key in keys])
+
+    def get_key_values(self, keys):
+        assert self.all_keys_in_change(keys), "Missing keys: %s" % ( \
+                                set(keys).difference(set(self.changes.keys())))
+        return [self.changes[key] for key in keys]
+
+    def all_keys_in_change(self, keys):
+        return set(keys).issubset(set(self.changes.keys()))
+
+    def any_key_in_change(self, keys):
+        return any([key in self.changes for key in keys])
+
+    def __getitem__(self, key):
+        if key in self.changes:
+            return self.changes[key]
+        return None
+
+
+class WidgetsHelper(dict, base.Base):
+    """A helper class to handle widgets
+    """
+    def __init__(self):
+        super(WidgetsHelper, self).__init__()
+        base.Base.__init__(self)
+
+    def subset(self, paths):
+        return [self[p] for p in paths]
+
+    def group(self, paths):
+        """Group the specified (by-path) widgets
+
+        Args:
+            paths: A list of paths of widgets to be grouped
+        Returns:
+            A WidgetsHelper.WidgetGroup
+        """
+        return WidgetsHelper.WidgetGroup(self, paths)
+
+    class WidgetGroup(list, base.Base):
+        def __init__(self, widgethelper, paths):
+            super(WidgetsHelper.WidgetGroup, self).__init__()
+            base.Base.__init__(self)
+            self.widgethelper = widgethelper
+            self.extend(paths)
+
+        def enabled(self, is_enable):
+            """Enable or disable all widgets of this group
+            """
+            self.logger.debug("Enabling widget group: %s" % self)
+            map(lambda w: w.enabled(is_enable), self.widgethelper.subset(self))
\ No newline at end of file
diff --git a/scripts/tui/src/ovirt/node/setup/logging_page.py b/scripts/tui/src/ovirt/node/setup/logging_page.py
index 9069014..5b87d9e 100644
--- a/scripts/tui/src/ovirt/node/setup/logging_page.py
+++ b/scripts/tui/src/ovirt/node/setup/logging_page.py
@@ -54,7 +54,7 @@
         """Validators validate the input on change and give UI feedback
         """
         return {
-                "max_log_size": ovirt.node.valid.Number(min=0),
+                "max_log_size": ovirt.node.valid.Number(range=[0, None]),
                 "rsyslog.address": ovirt.node.valid.FQDNOrIPAddress(),
                 "rsyslog.port": ovirt.node.valid.Port(),
                 "netconsole.address": ovirt.node.valid.FQDNOrIPAddress(),
diff --git a/scripts/tui/src/ovirt/node/setup/network_page.py b/scripts/tui/src/ovirt/node/setup/network_page.py
index 8fd528e..3be2acc 100644
--- a/scripts/tui/src/ovirt/node/setup/network_page.py
+++ b/scripts/tui/src/ovirt/node/setup/network_page.py
@@ -18,17 +18,17 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.  A copy of the GNU General Public License is
 # also available at http://www.gnu.org/copyleft/gpl.html.
-
 """
-Network plugin
+Network page plugin
 """
 
-import ovirt.node.plugins
-import ovirt.node.valid
-import ovirt.node.ui
-import ovirt.node.utils.network
-import ovirt.node.config.network
+import time
+
+from ovirt.node import ui
 from ovirt.node.config import defaults
+from ovirt.node import plugins
+import ovirt.node.utils.network
+from ovirt.node import valid
 
 
 class Plugin(ovirt.node.plugins.NodePlugin):
@@ -43,6 +43,18 @@
         "ntp[1]": "",
     }
     _widgets = None
+
+    def __init__(self, app):
+        super(Plugin, self).__init__(app)
+        self._widgets = plugins.WidgetsHelper()
+
+            # Keys/Paths to widgets related to NIC settings
+        self._nic_details_group = self._widgets.group([
+                                                "dialog.nic.ipv4.bootproto",
+                                                "dialog.nic.ipv4.address",
+                                                "dialog.nic.ipv4.netmask",
+                                                "dialog.nic.ipv4.gateway",
+                                                "dialog.nic.vlanid"])
 
     def name(self):
         return "Network"
@@ -64,14 +76,20 @@
 
     def validators(self):
         Empty = ovirt.node.valid.Empty
-        ip_or_empty = ovirt.node.valid.IPAddress() | Empty()
-        fqdn_ip_or_empty = ovirt.node.valid.FQDNOrIPAddress() | Empty()
+        ip_or_empty = valid.IPAddress() | Empty()
+        fqdn_ip_or_empty = valid.FQDNOrIPAddress() | Empty()
+
         return {
-                "hostname": ovirt.node.valid.FQDNOrIPAddress(),
-                "dns[0]": ovirt.node.valid.IPAddress(),
+                "hostname": valid.FQDNOrIPAddress(),
+                "dns[0]": valid.IPAddress(),
                 "dns[1]": ip_or_empty,
-                "ntp[0]": ovirt.node.valid.FQDNOrIPAddress(),
+                "ntp[0]": valid.FQDNOrIPAddress(),
                 "ntp[1]": fqdn_ip_or_empty,
+
+                "dialog.nic.ipv4.address": valid.IPv4Address(),
+                "dialog.nic.ipv4.netmask": valid.IPv4Address(),
+                "dialog.nic.ipv4.gateway": valid.IPv4Address(),
+                "dialog.nic.vlanid": valid.Number(range=[0, 4096]),
             }
 
     def ui_content(self):
@@ -80,33 +98,33 @@
         """
         widgets = [
             ("hostname",
-                ovirt.node.ui.Entry("Hostname:")),
-            ("hostname._space", ovirt.node.ui.Divider()),
+                ui.Entry("Hostname:")),
+            ("hostname._space", ui.Divider()),
 
-            ("nics", ovirt.node.ui.Table("Available System NICs",
+            ("nics", ui.Table("Available System NICs",
                         "Device   Status         Model    MAC Address",
                         self._get_nics())),
-            ("nics._space", ovirt.node.ui.Divider()),
+            ("nics._space", ui.Divider()),
 
-            ("dns[0]", ovirt.node.ui.Entry("DNS Server 1:")),
-            ("dns[1]", ovirt.node.ui.Entry("DNS Server 2:")),
-            ("dns._space", ovirt.node.ui.Divider()),
+            ("dns[0]", ui.Entry("DNS Server 1:")),
+            ("dns[1]", ui.Entry("DNS Server 2:")),
+            ("dns._space", ui.Divider()),
 
-            ("ntp[0]", ovirt.node.ui.Entry("NTP Server 1:")),
-            ("ntp[1]", ovirt.node.ui.Entry("NTP Server 2:")),
-            ("ntp._space", ovirt.node.ui.Divider()),
+            ("ntp[0]", ui.Entry("NTP Server 1:")),
+            ("ntp[1]", ui.Entry("NTP Server 2:")),
+            ("ntp._space", ui.Divider()),
         ]
         # Save it "locally" as a dict, for better accessability
-        self._widgets = dict(widgets)
+        self._widgets.update(dict(widgets))
 
-        page = ovirt.node.ui.Page(widgets)
+        page = ui.Page(widgets)
         return page
 
     def _get_nics(self):
         justify = lambda txt, l: txt.ljust(l)[0:l]
         node_nics = []
         first_nic = None
-        for name, nic in ovirt.node.utils.network.node_nics().items():
+        for name, nic in sorted(ovirt.node.utils.network.node_nics().items()):
             if first_nic == None:
                 first_nic = name
             bootproto = "Configured" if nic["bootproto"] else "Unconfigured"
@@ -122,7 +140,7 @@
 
     def _build_dialog(self, path, txt, widgets):
         self._widgets.update(dict(widgets))
-        self._widgets[path] = ovirt.node.ui.Dialog(txt, widgets)
+        self._widgets[path] = ui.Dialog(txt, widgets)
         return self._widgets[path]
 
     def _build_nic_details_dialog(self):
@@ -130,7 +148,9 @@
         iface = self._model["nics"]
         self.logger.debug("Getting informations for NIC details page")
         live = ovirt.node.utils.network.node_nics()[iface]
+        cfg = dict(defaults.Network().retrieve())
 
+        self.logger.debug(cfg)
         self._model.update({
             "dialog.nic.iface": live["name"],
             "dialog.nic.driver": live["driver"],
@@ -139,100 +159,150 @@
             "dialog.nic.link_status": "Connected" if live["link_detected"]
                                                   else "Disconnected",
             "dialog.nic.hwaddress": live["hwaddr"],
-            "dialog.nic.ipv4.bootproto": live["bootproto"],
-            "dialog.nic.ipv4.address": live["ipaddr"] or "",
-            "dialog.nic.ipv4.netmask": live["netmask"] or "",
-            "dialog.nic.ipv4.gateway": live["gateway"] or "",
-            "dialog.nic.vlanid": live["vlanid"] or "",
+
+            "dialog.nic.ipv4.bootproto": cfg["bootproto"],
+            "dialog.nic.ipv4.address": cfg["ipaddr"] or "",
+            "dialog.nic.ipv4.netmask": cfg["netmask"] or "",
+            "dialog.nic.ipv4.gateway": cfg["gateway"] or "",
+            "dialog.nic.vlanid": cfg["vlanid"] or "",
         })
 
         padd = lambda l: l.ljust(14)
         dialog = self._build_dialog("dialog.nic", "NIC Details: %s" % iface, [
-            ("dialog.nic._row[0]", ovirt.node.ui.Row([
+            ("dialog.nic._row[0]", ui.Row([
                 ("dialog.nic.iface",
-                    ovirt.node.ui.KeywordLabel(padd("Interface: "))),
+                    ui.KeywordLabel(padd("Interface: "))),
                 ("dialog.nic.driver",
-                    ovirt.node.ui.KeywordLabel(padd("Driver: "))),
+                    ui.KeywordLabel(padd("Driver: "))),
                 ])),
 
-            ("dialog.nic._row[1]", ovirt.node.ui.Row([
+            ("dialog.nic._row[1]", ui.Row([
                 ("dialog.nic.protocol",
-                    ovirt.node.ui.KeywordLabel(padd("Protocol: "))),
+                    ui.KeywordLabel(padd("Protocol: "))),
                 ("dialog.nic.vendor",
-                    ovirt.node.ui.KeywordLabel(padd("Vendor: "))),
+                    ui.KeywordLabel(padd("Vendor: "))),
                 ])),
 
-            ("dialog.nic._row[2]", ovirt.node.ui.Row([
+            ("dialog.nic._row[2]", ui.Row([
                 ("dialog.nic.link_status",
-                    ovirt.node.ui.KeywordLabel(padd("Link Status: "))),
+                    ui.KeywordLabel(padd("Link Status: "))),
                 ("dialog.nic.hwaddress",
-                    ovirt.node.ui.KeywordLabel(padd("MAC Address: "))),
+                    ui.KeywordLabel(padd("MAC Address: "))),
                 ])),
 
-            ("dialog.nic._divider[0]", ovirt.node.ui.Divider()),
+            ("dialog.nic._divider[0]", ui.Divider()),
 
-            ("dialog.nic.ipv4._header", ovirt.node.ui.Header("IPv4 Settings")),
-            ("dialog.nic.ipv4.bootproto", ovirt.node.ui.Options(
+            ("dialog.nic.ipv4._header", ui.Header("IPv4 Settings")),
+            ("dialog.nic.ipv4.bootproto", ui.Options(
                 "Bootprotocol: ", [
                     ("none", "Disabled"),
                     ("dhcp", "DHCP"),
                     ("static", "Static")
                 ])),
             ("dialog.nic.ipv4.address",
-                    ovirt.node.ui.Entry(padd("IP Address: "))),
+                    ui.Entry(padd("IP Address: "))),
             ("dialog.nic.ipv4.netmask",
-                    ovirt.node.ui.Entry(padd("Netmask: "))),
+                    ui.Entry(padd("Netmask: "))),
             ("dialog.nic.ipv4.gateway",
-                    ovirt.node.ui.Entry(padd("Gateway: "))),
+                    ui.Entry(padd("Gateway: "))),
 
-            ("dialog.nic._divider[1]", ovirt.node.ui.Divider()),
+            ("dialog.nic._divider[1]", ui.Divider()),
 
             ("dialog.nic.vlanid",
-                    ovirt.node.ui.Entry(padd("VLAN ID: "))),
+                    ui.Entry(padd("VLAN ID: "))),
 
-            ("dialog.nic._buttons", ovirt.node.ui.Row([
+            ("dialog.nic._buttons", ui.Row([
                 ("dialog.nic.save",
-                        ovirt.node.ui.Button("Save & Close")),
+                        ui.Button("Save & Close")),
                 ("dialog.nic.close",
-                        ovirt.node.ui.Button("Close")),
+                        ui.Button("Close")),
             ]))
         ])
 
         dialog.has_save_button = False
+
+        self._nic_details_group.enabled(False)
+
         return dialog
 
     def on_change(self, changes):
-        pass
+        self.logger.info("Checking network stuff")
+        helper = plugins.ChangesHelper(changes)
+        bootproto = helper["dialog.nic.ipv4.bootproto"]
+        if bootproto:
+            if bootproto in ["static"]:
+                self._nic_details_group.enabled(True)
+            else:
+                self._nic_details_group.enabled(False)
+            self._widgets["dialog.nic.ipv4.bootproto"].enabled(True)
 
     def on_merge(self, effective_changes):
+        self.logger.info("Saving network stuff")
         changes = self.pending_changes(False)
         effective_model = dict(self._model)
         effective_model.update(effective_changes)
-        self.logger.info("effm %s" % effective_model)
-        self.logger.info("effc %s" % effective_changes)
-        self.logger.info("allc %s" % changes)
+        self.logger.info("Effective model %s" % effective_model)
+        self.logger.info("Effective changes %s" % effective_changes)
+        self.logger.info("All changes %s" % changes)
 
-        nameservers = []
-        for key in ["dns[0]", "dns[1]"]:
-            if key in effective_changes:
-                nameservers.append(effective_changes[key])
-        if nameservers:
-            self.logger.info("Setting new nameservers: %s" % nameservers)
-            model = ovirt.node.config.defaults.Nameservers()
-            model.update(nameservers)
-
-        timeservers = []
-        for key in ["ntp[0]", "ntp[1]"]:
-            if key in effective_changes:
-                timeservers.append(effective_changes[key])
-        if timeservers:
-            self.logger.info("Setting new timeservers: %s" % timeservers)
-            model = ovirt.node.config.defaults.Timeservers()
-            model.update(timeservers)
-
+        # Special case: A NIC was selected, display that dialog!
         if "nics" in changes and len(changes) == 1:
             iface = changes["nics"]
             self.logger.debug("Opening NIC Details dialog for '%s'" % iface)
             return self._build_nic_details_dialog()
 
-        return True
\ No newline at end of file
+        def set_progress(txt):
+            set_progress.txt += txt + "\n"
+            progress.set_text(set_progress.txt)
+        set_progress.txt = "Applying changes ...\n"
+
+        progress = ui.Label(set_progress.txt)
+        d = self.application.ui.show_dialog(self._build_dialog("dialog.dia",
+                                                               "fooo", [
+            ("dialog.dia.text[0]", progress),
+            ]))
+
+        nameservers = []
+        for key in ["dns[0]", "dns[1]"]:
+            if key in effective_changes:
+                nameservers.append(effective_model[key])
+        if nameservers:
+            set_progress("Applying DNS changes.")
+            self.logger.info("Setting new nameservers: %s" % nameservers)
+            model = defaults.Nameservers()
+            model.update(nameservers)
+
+        timeservers = []
+        for key in ["ntp[0]", "ntp[1]"]:
+            if key in effective_changes:
+                timeservers.append(effective_model[key])
+        if timeservers:
+            set_progress("Applying NTP changes.")
+            self.logger.info("Setting new timeservers: %s" % timeservers)
+            model = defaults.Timeservers()
+            model.update(timeservers)
+
+        change_helper = plugins.ChangesHelper(effective_changes)
+        if change_helper.any_key_in_change(self._nic_details_group):
+            # If any networking related key was changed, reconfigure networking
+            helper = plugins.ChangesHelper(effective_model)
+            self._configure_nic(*helper.get_key_values(self._nic_details_group))
+
+        set_progress("All changes were applied.")
+        time.sleep(3)
+        d.close()
+
+    def _configure_nic(self, bootproto, ipaddr, netmask, gateway, vlanid):
+        model = defaults.Network()
+        iface = self._model["dialog.nic.iface"]
+        if bootproto == "none":
+            self.logger.debug("Configuring no networking")
+            model.update(None, None, None, None, None, None)
+        elif bootproto == "dhcp":
+            self.logger.debug("Configuring dhcp")
+            model.update(iface, "dhcp", None, None, None, vlanid)
+        elif bootproto == "static":
+            self.logger.debug("Configuring static ip")
+            model.update(iface, "none", ipaddr, netmask, gateway, vlanid)
+        else:
+            self.logger.debug("No interface configuration found")
diff --git a/scripts/tui/src/ovirt/node/setup/ping.py b/scripts/tui/src/ovirt/node/setup/ping.py
index bb5d8d1..6c6437f 100644
--- a/scripts/tui/src/ovirt/node/setup/ping.py
+++ b/scripts/tui/src/ovirt/node/setup/ping.py
@@ -60,7 +60,7 @@
         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),
+                "ping.count": ovirt.node.valid.Number(range=[1, 20]),
             }
 
     def ui_content(self):
diff --git a/scripts/tui/src/ovirt/node/setup/security_page.py b/scripts/tui/src/ovirt/node/setup/security_page.py
index bb62d45..9de6d85 100644
--- a/scripts/tui/src/ovirt/node/setup/security_page.py
+++ b/scripts/tui/src/ovirt/node/setup/security_page.py
@@ -50,9 +50,10 @@
         return self._model
 
     def validators(self):
+        number_or_empty = ovirt.node.valid.Number(range=[0, None]) | \
+                          ovirt.node.valid.Empty()
         return {
-                "stringrng.bytes_used": ovirt.node.valid.Number(min=0) | \
-                                        ovirt.node.valid.Empty,
+                "strongrng.num_bytes": number_or_empty,
                 "passwd.admin.password": ovirt.node.valid.Text(),
                 "passwd.admin.password_confirmation": ovirt.node.valid.Text(),
             }
diff --git a/scripts/tui/src/ovirt/node/setup/status_page.py b/scripts/tui/src/ovirt/node/setup/status_page.py
index 9e19019..c78ae49 100644
--- a/scripts/tui/src/ovirt/node/setup/status_page.py
+++ b/scripts/tui/src/ovirt/node/setup/status_page.py
@@ -48,7 +48,9 @@
 
     def model(self):
         net_status, net_br, net_addrs = utils.network.networking_status()
-        net_addrs_str = "\nIPv4: {inet}\nIPv6: {inet6}".format(**net_addrs)
+        net_addrs_str = ""
+        if net_addrs:
+            net_addrs_str = "\nIPv4: {inet}\nIPv6: {inet6}".format(**net_addrs)
 
         num_domains = "N/A"
 #        with virt.LibvirtConnection() as con:
diff --git a/scripts/tui/src/ovirt/node/setup/support_page.py b/scripts/tui/src/ovirt/node/setup/support_page.py
index 992c4ca..5d829c9 100644
--- a/scripts/tui/src/ovirt/node/setup/support_page.py
+++ b/scripts/tui/src/ovirt/node/setup/support_page.py
@@ -30,8 +30,8 @@
 class Plugin(ovirt.node.plugins.NodePlugin):
     def __init__(self, application):
         # Register F8: Display this plugin when F( is pressed
-        application.ui.register_hotkey(["f8"],
-                                lambda: application.ui.display_plugin(self))
+        display_page = lambda: application.ui.display_page(self.ui_content())
+        application.ui.register_hotkey(["f8"], display_page)
 
     def name(self):
         return "Support"
diff --git a/scripts/tui/src/ovirt/node/ui/__init__.py b/scripts/tui/src/ovirt/node/ui/__init__.py
index 2423f67..cc4191a 100644
--- a/scripts/tui/src/ovirt/node/ui/__init__.py
+++ b/scripts/tui/src/ovirt/node/ui/__init__.py
@@ -229,7 +229,7 @@
         self.char = char
 
 
-class Options(Element):
+class Options(InputElement):
     """A selection of options
 
     Args:
@@ -237,7 +237,7 @@
         options:
     """
     def __init__(self, label, options):
-        super(Options, self).__init__()
+        super(Options, self).__init__(None, True)
         self.label = label
         self.options = options
         self.option(options[0])
@@ -348,5 +348,23 @@
         self.logger.debug("Registering hotkey '%s': %s" % (hotkey, cb))
         self._hotkeys[str(hotkey)] = cb
 
+    def show_dialog(self, dialog):
+        """Show a dialog.
+        The dialog can be closed using dialog.close()
+
+        Args:
+            dialog: The dialog to be shown
+        """
+        raise NotImplementedError
+
+    def show_page(self, page):
+        """Show / switch to a page.
+        Displays the given page (which does not need to be patr of a plugin)
+
+        Args:
+            page: The page to be shown
+        """
+        raise NotImplementedError
+
     def run(self):
         raise NotImplementedError
diff --git a/scripts/tui/src/ovirt/node/ui/builder.py b/scripts/tui/src/ovirt/node/ui/builder.py
index 18718e9..471657e 100644
--- a/scripts/tui/src/ovirt/node/ui/builder.py
+++ b/scripts/tui/src/ovirt/node/ui/builder.py
@@ -125,12 +125,16 @@
         widget_class = ovirt.node.ui.widgets.PasswordEntry
 
     widget = widget_class(item.label, align_vertical=item.align_vertical)
-    widget.enable(item.enabled)
+    widget.enable(item.enabled())
 
     def on_item_enabled_change_cb(w, v):
         LOGGER.debug("Element changed, updating entry '%s': %s" % (w, v))
         if widget.selectable() != v:
             widget.enable(v)
+        if v == False:
+            widget.notice = ""
+            widget.valid(True)
+
     item.connect_signal("enabled", on_item_enabled_change_cb)
 
     def on_widget_value_change(widget, new_value):
@@ -148,14 +152,15 @@
 
         except ovirt.node.exceptions.InvalidData as e:
             LOGGER.error("Invalid data when updating: %s" % e)
-            widget.notice = e.message
+            if widget._selectable:
+                widget.notice = e.message
             widget.valid(False)
             plugin._save_button.enable(False)
 
         # FIXME page validation must happen within tui, not plugin
         # as UI data should be handled in tui
 
-        tui.draw_screen()
+        tui._draw_screen()
     urwid.connect_signal(widget, 'change', on_widget_value_change)
 
     return widget
@@ -175,7 +180,7 @@
         widget.text(v)
         # Redraw the screen if widget text is updated "outside" of the
         # mainloop
-        tui.draw_screen()
+        tui._draw_screen()
     item.connect_signal("text", on_item_text_change_cb)
 
     return widget
@@ -237,7 +242,7 @@
     def on_item_current_change_cb(w, v):
         LOGGER.debug("Model changed, updating progressbar '%s': %s" % (w, v))
         widget.set_completion(v)
-        tui.draw_screen()
+        tui._draw_screen()
     item.connect_signal("current", on_item_current_change_cb)
 
     return widget
@@ -276,13 +281,11 @@
 
         if type(result) in [ovirt.node.ui.Page]:
             LOGGER.debug("Page requested.")
-            w = build_page(tui, plugin, result)
-            tui.display_page(w)
+            tui.show_page(result)
 
         elif type(result) in [ovirt.node.ui.Dialog]:
             LOGGER.debug("Dialog requested.")
-            w = build_page(tui, plugin, result)
-            dialog = tui.display_dialog(w, result.title)
+            dialog = tui.show_dialog(result)
 
             def on_item_close_changed_cb(i, v):
                 dialog.close()
diff --git a/scripts/tui/src/ovirt/node/ui/tui.py b/scripts/tui/src/ovirt/node/ui/tui.py
index ab1f005..b6a2a8d 100644
--- a/scripts/tui/src/ovirt/node/ui/tui.py
+++ b/scripts/tui/src/ovirt/node/ui/tui.py
@@ -23,15 +23,12 @@
 The urwid TUI base library
 """
 
+import timeit
 import urwid
 
-import logging
-import timeit
-
-import ovirt.node.ui.widgets
+from ovirt.node import ui
 import ovirt.node.ui.builder
-
-LOGGER = logging.getLogger(__name__)
+import ovirt.node.ui.widgets
 
 
 class UrwidTUI(ovirt.node.ui.Window):
@@ -50,14 +47,17 @@
     _current_plugin = None
 
     header = u"\n Configuration TUI\n"
-    footer = u"Press ctrl+c to exit"
+    footer = u"Press ctrl+c to quit"
 
     element_styles = {
-        "text": "dark gray",
-        "label": "dark gray, bold",
+        "text": "black",
+        "label": "black, bold",
+        "disabled": "white",
+        "background": "light gray"
     }
 
-    palette = [(None, 'default', 'light gray', 'bold', None, None),
+    palette = [(None, 'default', element_styles["background"], 'bold',
+                None, None),
                ('screen', None),
                ('header', 'white', 'dark blue'),
                ('table', element_styles["text"]),
@@ -69,10 +69,12 @@
                ('main.menu.frame', element_styles["text"]),
                ('notice', 'light red'),
                ('plugin.widget.entry', element_styles["text"]),
-               ('plugin.widget.entry.disabled', 'dark gray', 'light gray'),
+               ('plugin.widget.entry.disabled', element_styles["disabled"]),
                ('plugin.widget.entry.label', element_styles["label"]),
                ('plugin.widget.entry.frame', element_styles["text"]),
                ('plugin.widget.entry.frame.invalid', 'dark red'),
+               ('plugin.widget.entry.frame.disabled',
+                element_styles["disabled"]),
                ('plugin.widget.notice', 'light red'),
                ('plugin.widget.header', 'black, bold'),
                ('plugin.widget.divider', element_styles["text"]),
@@ -94,11 +96,53 @@
         super(UrwidTUI, self).__init__(app)
         self.logger.info("Creating urwid tui for '%s'" % app)
 
+    def show_body(self, body):
+        """
+        """
+        assert type(body) is ui.Page
+        widget = ui.builder.build_page(self, self._current_plugin, body)
+        self.__display_as_body(widget)
+
+    def show_page(self, page):
+        """Shows the ui.Page as a page.
+        This transforms the abstract ui.Page to a urwid specififc version
+        and displays it.
+        """
+        assert type(page) is ui.Page
+        widget = ui.builder.build_page(self, self._current_plugin, page)
+        self.__display_as_page(widget)
+
+    def show_dialog(self, dialog):
+        """Shows the ui.Dialog as a dialog.
+        This transforms the abstract ui.Dialog to a urwid specififc version
+        and displays it.
+        """
+        assert type(dialog) is ui.Dialog
+        widget = ui.builder.build_page(self, self._current_plugin, dialog)
+        return self.__display_as_dialog(widget, dialog.title)
+
+    def quit(self):
+        """Quit the UI
+        """
+        self.logger.info("Quitting, exitting mainloop")
+        raise urwid.ExitMainLoop()
+
+    def run(self):
+        """Run the UI
+        """
+        self.__main_frame = self.__create_screen()
+        self.__register_default_hotkeys()
+
+        self.__loop = urwid.MainLoop(self.__main_frame,
+                              self._convert_palette(),
+                              input_filter=self.__filter_hotkeys)
+        self.__loop.run()
+
     def __build_menu(self):
         self.__menu = ovirt.node.ui.widgets.PluginMenu(self._plugins)
 
         def menu_item_changed(plugin):
-            self.display_plugin(plugin)
+            self.__display_plugin(plugin)
         urwid.connect_signal(self.__menu, 'changed', menu_item_changed)
 
     def __create_screen(self):
@@ -123,97 +167,122 @@
         screen = urwid.Frame(body, header, footer)
         return urwid.AttrMap(screen, "screen")
 
-    def display_plugin(self, plugin):
-        if self._check_outstanding_changes():
-            return
-        timer = timeit.Timer()
-        self._current_plugin = plugin
-        page = ovirt.node.ui.builder.page_from_plugin(self, plugin)
-        self.display_page(page)
-        LOGGER.debug("Build and displayed page in %ss" % timer.timeit())
-
     def _check_outstanding_changes(self):
-        has_outstanding_changes = False 
+        has_outstanding_changes = False
         if self._current_plugin:
             pending_changes = self._current_plugin.pending_changes()
             if pending_changes:
-                LOGGER.warning("Pending changes: %s" % pending_changes)
+                self.logger.warning("Pending changes: %s" % pending_changes)
                 msg = ""
                 widgets = dict(self._current_plugin.ui_content().children)
-                LOGGER.debug("Available widgets: %s" % widgets)
+                self.logger.debug("Available widgets: %s" % widgets)
                 for path, value in pending_changes.items():
                     if path in widgets:
                         field = widgets[path].name
                         msg += "- %s\n" % (field.strip(":"))
-                self.display_dialog(urwid.Filler(urwid.Text(
+                self.__display_as_dialog(urwid.Filler(urwid.Text(
                             "The following fields were changed:\n%s" % msg)),
                             "Pending changes")
                 has_outstanding_changes = True
         return has_outstanding_changes
 
-    def display_page(self, page):
-        LOGGER.debug("Displaying page %s" % page)
+    def __display_as_body(self, widget):
+        self.__main_frame.body = widget
+
+    def __display_as_page(self, page):
+        self.logger.debug("Displaying page %s" % page)
 #        filler = urwid.Filler(page, ("fixed top", 1), height=35)
         filler = urwid.Pile([page])
         self.__page_frame.body = filler
 
-    def display_dialog(self, body, title):
-        LOGGER.debug("Displaying dialog: %s / %s" % (body, title))
+    def __display_plugin(self, plugin):
+        if self._check_outstanding_changes():
+            return
+        timer = timeit.Timer()
+        self._current_plugin = plugin
+        plugin_page = ovirt.node.ui.builder.page_from_plugin(self, plugin)
+        self.__display_as_page(plugin_page)
+        self.logger.debug("Build and displayed plugin_page in %ss" %
+                          timer.timeit())
+
+    def __display_as_dialog(self, body, title):
+        self.logger.debug("Displaying dialog: %s / %s" % (body, title))
 #        filler = urwid.Filler(body, ("fixed top", 1), height=35)
         filler = urwid.Pile([body])
         dialog = ovirt.node.ui.widgets.ModalDialog(title, filler, "esc",
                                                    self.__loop.widget)
         urwid.connect_signal(dialog, "close",
-                             lambda: self.close_dialog(dialog))
+                             lambda: self.__close_dialog(dialog))
         self.__loop.widget = dialog
         self.__widget_stack.append(dialog)
+        self._draw_screen()
         return dialog
 
-    def close_dialog(self, dialog):
-        # FIXME stack to allow more than one dialog
+    def __close_dialog(self, dialog):
         if type(self.__loop.widget) is ovirt.node.ui.widgets.ModalDialog:
             if dialog == self.__widget_stack[-1]:
                 self.__widget_stack.pop()
                 if len(self.__widget_stack) > 0:
                     self.__loop.widget = self.__widget_stack[:-1]
                 else:
-                    LOGGER.debug("No more dialog, main frame " + \
+                    self.logger.debug("No more dialog, main frame " + \
                                  "%s" % self.__main_frame)
                     self.__loop.widget = self.__main_frame
-            self.draw_screen()
-            LOGGER.debug("Dialog closed")
-
-    def popup(self, title, msg, buttons=None):
-        LOGGER.debug("Launching popup")
-        body = urwid.Filler(urwid.Text(msg))
-        self.display_dialog(body)
+            self.logger.debug("Loop widget: %s" % self.__loop.widget)
+            self._draw_screen()
+            self.logger.debug("Dialog closed")
 
     def __filter_hotkeys(self, keys, raw):
         key = str(keys)
 
         if type(self.__loop.widget) is ovirt.node.ui.widgets.ModalDialog:
-            LOGGER.debug("Modal dialog escape: %s" % key)
+            self.logger.debug("Modal dialog escape: %s" % key)
             if self.__loop.widget.escape_key in keys:
-                self.close_dialog(self.__widget_stack[-1])
+                self.__close_dialog(self.__widget_stack[-1])
                 return
 
         if key in self._hotkeys.keys():
-            LOGGER.debug("Running hotkeys: %s" % key)
+            self.logger.debug("Running hotkeys: %s" % key)
             self._hotkeys[key]()
 
-        LOGGER.debug("Keypress: %s" % key)
+        self.logger.debug("Keypress: %s" % key)
 
         return keys
 
     def __register_default_hotkeys(self):
         self.register_hotkey(["esc"], self.quit)
         self.register_hotkey(["q"], self.quit)
+        self.register_hotkey(["window resize"], self._check_min_size_cb)
 
-    def draw_screen(self):
+    def _draw_screen(self):
         self.__loop.draw_screen()
 
     def size(self):
+        if not self.__loop.screen:
+            # FIXME sometimes screen is None, but why?
+            return (0, 0)
         return self.__loop.screen.get_cols_rows()
+
+    def _min_size(self):
+            return (80, 23)
+
+    def _check_min_size_cb(self):
+        size = self.size()
+        msize = self._min_size()
+        width, height = size
+        min_width, min_height = msize
+        if width < min_width or height < min_height:
+            msg = ("The current window size %s is smaller " +
+                                 "than the minimum size %s") % (size, msize)
+            self.logger.warning(msg)
+            if not hasattr(self, "_error_dialog") or not self._error_dialog:
+                d = ui.Dialog("Error", [("dialog.error", ui.Label(msg))])
+                d.has_save_button = False
+                self._error_dialog = self.show_dialog(d)
+        else:
+            if hasattr(self, "_error_dialog") and self._error_dialog:
+                self._error_dialog.close()
+                self._error_dialog = None
 
     def watch_pipe(self, cb):
         """Return a fd to be used as stdout, cb called for each line
@@ -221,7 +290,7 @@
         return self.__loop.watch_pipe(cb)
 
     def notify(self, category, msg):
-        LOGGER.info("UI notification (%s): %s" % (category, msg))
+        self.logger.info("UI notification (%s): %s" % (category, msg))
         # FIXME do notification
 
     def suspended(self):
@@ -237,12 +306,6 @@
             def __exit__(self, a, b, c):
                 self.__loop.screen.start()
         return SuspendedScreen(self.__loop)
-
-    def quit(self):
-        """Quit the UI
-        """
-        LOGGER.info("Quitting, exitting mainloop")
-        raise urwid.ExitMainLoop()
 
     def _convert_palette(self):
         """Convert our palette to the format urwid understands.
@@ -264,14 +327,3 @@
             rest = default[len(colors):]
             palette.append(tuple([k] + colors + rest))
         return palette
-
-    def run(self):
-        """Run the UI
-        """
-        self.__main_frame = self.__create_screen()
-        self.__register_default_hotkeys()
-
-        self.__loop = urwid.MainLoop(self.__main_frame,
-                              self._convert_palette(),
-                              input_filter=self.__filter_hotkeys)
-        self.__loop.run()
diff --git a/scripts/tui/src/ovirt/node/ui/widgets.py b/scripts/tui/src/ovirt/node/ui/widgets.py
index 05ddbcf..f32c47e 100644
--- a/scripts/tui/src/ovirt/node/ui/widgets.py
+++ b/scripts/tui/src/ovirt/node/ui/widgets.py
@@ -292,16 +292,21 @@
 
     def enable(self, is_enabled):
         self._selectable = is_enabled
-        attr_map = {None: "plugin.widget.entry"}
+        edit_attr_map = {None: "plugin.widget.entry"}
+        linebox_attr_map = {None: "plugin.widget.entry.frame"}
         if not is_enabled:
-            attr_map = {None: "plugin.widget.entry.disabled"}
-        self._edit_attrmap.set_attr_map(attr_map)
+            edit_attr_map = {None: "plugin.widget.entry.disabled"}
+            linebox_attr_map = {None: "plugin.widget.entry.frame.disabled"}
+        self._edit_attrmap.set_attr_map(edit_attr_map)
+        self._linebox_attrmap.set_attr_map(linebox_attr_map)
 
     def valid(self, is_valid):
         attr_map = {None: "plugin.widget.entry.frame"}
         if not is_valid:
             attr_map = {None: "plugin.widget.entry.frame.invalid"}
-        self._linebox_attrmap.set_attr_map(attr_map)
+        if self._selectable:
+            # Only update style if it is selectable/enabled
+            self._linebox_attrmap.set_attr_map(attr_map)
 
     def set_text(self, txt):
         self._edit.set_edit_text(txt)
diff --git a/scripts/tui/src/ovirt/node/utils/network.py b/scripts/tui/src/ovirt/node/utils/network.py
index 4e134e8..17c0ef3 100644
--- a/scripts/tui/src/ovirt/node/utils/network.py
+++ b/scripts/tui/src/ovirt/node/utils/network.py
@@ -182,6 +182,7 @@
             n.startswith("vnet") or \
             n.startswith("tun") or \
             n.startswith("wlan") or \
+            n.startswith("virbr") or \
             (filter_vlans and ("." in n)))
 # FIXME!!!
 #    valid_props = lambda i, p: (filter_bridges and (p["type"] != "bridge"))
@@ -382,11 +383,15 @@
     status = "Not connected"
 
     iface = iface or node_bridge()
-    addresses = nic_ip_addresses(iface)
-    has_address = any([a != None for a in addresses.values()])
+    addresses = []
+    if iface:
+        addresses = nic_ip_addresses(iface)
+        has_address = any([a != None for a in addresses.values()])
 
-    if nic_link_detected(iface) and has_address:
-        status = "Connected"
+        if nic_link_detected(iface):
+            status = "Connected (Link only, no IP)"
+        if has_address:
+            status = "Connected"
 
     summary = (status, iface, addresses)
     LOGGER.debug(summary)
diff --git a/scripts/tui/src/ovirt/node/valid.py b/scripts/tui/src/ovirt/node/valid.py
index e47a58e..af00158 100644
--- a/scripts/tui/src/ovirt/node/valid.py
+++ b/scripts/tui/src/ovirt/node/valid.py
@@ -127,25 +127,32 @@
     True
     >>> Number()("42")
     True
+    >>> Number(range=[0, None]).validate(-10)
+    False
+    >>> Number(range=[0, 10]).validate(11)
+    False
     >>> Number().validate("4 2")
     False
     """
 
     description = "a number"
-    pattern = "^[-]?\d+$"
-    minmax = (None, None)
+    pattern = "^[-+]?\d+$"
+    range = [None, None]
 
-    def __init__(self, min=None, max=None):
-        if min or max:
-            self.minmax = (min, max)
-            self.description = "%s (%s - %s)" % (self.description, min, max)
+    def __init__(self, range=None):
+        super(Number, self).__init__()
+        if range:
+            self.range = range
+            self.description = "%s in the range %s" % (self.description, range)
 
     def validate(self, value):
         valid = RegexValidator.validate(self, value)
         if valid:
-            min, max = self.minmax
+            self.logger.debug("Checking range: %s" % self.range)
+            vmin, vmax = self.range
             value = int(value)
-            if (min and value < min) or (max and value > max):
+            if (vmin != None and value < vmin) or \
+               (vmax != None and value > vmax):
                 valid = False
         return valid
 
@@ -164,7 +171,7 @@
     description = "a port number"
 
     def __init__(self):
-        super(Port, self).__init__(1, 65535)
+        super(Port, self).__init__(range=[1, 65535])
 
 
 class NoSpaces(RegexValidator):
@@ -302,8 +309,12 @@
 class Empty(Validator):
     description = "an empty string"
 
+    def __init__(self, or_none=False):
+        super(Empty, self).__init__()
+        self.or_none = or_none
+
     def validate(self, value):
-        return value == ""
+        return value == "" or (self.or_none and value == None)
 
 
 class URL(Validator):


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

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