[node-patches] Change in ovirt-node[master]: setup: Add several pages

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


Fabian Deutsch has uploaded a new change for review.

Change subject: setup: Add several pages
......................................................................

setup: Add several pages

Change-Id: Ieed7880a31c250b03030cbaa6fbb3686776b4b19
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/.gitignore
A scripts/tui/src/ovirt/node/plugins/engine_page.py
M scripts/tui/src/ovirt/node/plugins/example.py
M scripts/tui/src/ovirt/node/plugins/features.py
R scripts/tui/src/ovirt/node/plugins/kdump_page.py
A scripts/tui/src/ovirt/node/plugins/keyboard_page.py
A scripts/tui/src/ovirt/node/plugins/logging_page.py
A scripts/tui/src/ovirt/node/plugins/monitoring_page.py
A scripts/tui/src/ovirt/node/plugins/network_page.py
M scripts/tui/src/ovirt/node/plugins/ping.py
A scripts/tui/src/ovirt/node/plugins/remote_storage_page.py
A scripts/tui/src/ovirt/node/plugins/security_page.py
A scripts/tui/src/ovirt/node/plugins/snmp_page.py
R scripts/tui/src/ovirt/node/plugins/status_page.py
M scripts/tui/src/ovirt/node/plugins/usage.py
M scripts/tui/src/ovirt/node/tui.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/widgets.py
M scripts/tui/src/ovirt/node/utils/__init__.py
A scripts/tui/src/ovirt/node/utils/network.py
M scripts/tui/src/ovirt/node/valid.py
22 files changed, 1,189 insertions(+), 26 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/98/9898/1

diff --git a/scripts/tui/.gitignore b/scripts/tui/.gitignore
index 171bbb9..a9d8d73 100644
--- a/scripts/tui/.gitignore
+++ b/scripts/tui/.gitignore
@@ -2,3 +2,7 @@
 app.log
 *.pyc
 *.pyo
+MANIFEST
+scm_hash.txt
+dist
+build
diff --git a/scripts/tui/src/ovirt/node/plugins/engine_page.py b/scripts/tui/src/ovirt/node/plugins/engine_page.py
new file mode 100644
index 0000000..39b1ee9
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/engine_page.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python
+#
+# engine_page.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Configure Engine
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "oVirt Engine"
+
+    def rank(self):
+        return 100
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                "vdsm.address": "",
+                "vdsm.port": "7634",
+                "vdsm.connect_and_validate": True,
+                "vdsm.password": "",
+                "vdsm.password_confirmation": "",
+            }
+        return self._model
+
+    def validators(self):
+        return {
+                "vdsm.address": ovirt.node.valid.FQDNOrIPAddress(),
+                "vdsm.port": ovirt.node.valid.Port(),
+                "vdsm.password": ovirt.node.valid.Text(),
+                "vdsm.password_confirmation": ovirt.node.valid.Text(),
+            }
+
+    def ui_content(self):
+        widgets = [
+            ("header", ovirt.node.ui.Header("oVirt Engine Configuration")),
+
+            ("vdsm.address", ovirt.node.ui.Entry("Server Address")),
+            ("vdsm.port", ovirt.node.ui.Entry("Server Port")),
+            ("vdsm.connect_and_validate", ovirt.node.ui.Options(
+                    "Connect to oVirt Engine and Validate Certificate",
+                    [("yes", "Yes"), ("no", "No")])),
+
+            ("vdsm.password._label", ovirt.node.ui.Label(
+                    "Optional password for adding Node through oVirt " +
+                    "Engine UI")),
+
+            ("vdsm.password", ovirt.node.ui.PasswordEntry("Password")),
+            ("vdsm.password_confirmation", ovirt.node.ui.PasswordEntry(
+                    "Confirm Password")),
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def on_change(self, changes):
+        self._model.update(changes)
+
+        if self._model["vdsm.password"] != \
+           self._model["vdsm.password_confirmation"]:
+            raise ovirt.node.exceptions.InvalidData("Passwords do not match.")
+
+    def on_merge(self, effective_changes):
+        pass
diff --git a/scripts/tui/src/ovirt/node/plugins/example.py b/scripts/tui/src/ovirt/node/plugins/example.py
index 7436c10..ffe6f87 100644
--- a/scripts/tui/src/ovirt/node/plugins/example.py
+++ b/scripts/tui/src/ovirt/node/plugins/example.py
@@ -38,6 +38,9 @@
     def name(self):
         return "Example Page"
 
+    def rank(self):
+        return 999
+
     def model(self):
         """Returns the model of this plugin
         This is expected to parse files and all stuff to build up the model.
diff --git a/scripts/tui/src/ovirt/node/plugins/features.py b/scripts/tui/src/ovirt/node/plugins/features.py
index 5816579..a022a9f 100644
--- a/scripts/tui/src/ovirt/node/plugins/features.py
+++ b/scripts/tui/src/ovirt/node/plugins/features.py
@@ -41,7 +41,7 @@
     def name(self):
         return "Features"
 
-    rank = lambda self: 10
+    rank = lambda self: 999
 
     def ui_content(self):
         widgets = [
diff --git a/scripts/tui/src/ovirt/node/plugins/kdump.py b/scripts/tui/src/ovirt/node/plugins/kdump_page.py
similarity index 96%
rename from scripts/tui/src/ovirt/node/plugins/kdump.py
rename to scripts/tui/src/ovirt/node/plugins/kdump_page.py
index 88ca080..1cc8771 100644
--- a/scripts/tui/src/ovirt/node/plugins/kdump.py
+++ b/scripts/tui/src/ovirt/node/plugins/kdump_page.py
@@ -45,7 +45,7 @@
         return "Kdump"
 
     def rank(self):
-        return 70
+        return 60
 
     def model(self):
         """Returns the model of this plugin
@@ -63,9 +63,8 @@
     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(options),
+                "kdump.type": ovirt.node.valid.Options(self._types),
                 "kdump.ssh_location": ovirt.node.valid.NoSpaces(),
                 "kdump.nfs_location": ovirt.node.valid.NoSpaces(),
             }
diff --git a/scripts/tui/src/ovirt/node/plugins/keyboard_page.py b/scripts/tui/src/ovirt/node/plugins/keyboard_page.py
new file mode 100644
index 0000000..20cfc0a
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/keyboard_page.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+#
+# keyboard_page.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Configure Keyboard Layout
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+import ovirt.node.utils.network
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "Keyboard"
+
+    def rank(self):
+        return 30
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                "layout": "en_US",
+            }
+
+        return self._model
+
+    def validators(self):
+        return {}
+
+    def ui_content(self):
+        """Describes the UI this plugin requires
+        This is an ordered list of (path, widget) tuples.
+        """
+        widgets = [
+            ("layout.label",
+                ovirt.node.ui.Label("Keyboard Layout Selection")),
+            ("layout", ovirt.node.ui.Table("", self._get_layouts())),
+
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def _get_layouts(self):
+        # FIXME load from somewhere
+        return [
+                ("en_US", "U.S. English"),
+                ("de_DE", "German"),
+                ]
+
+    def on_change(self, changes):
+        pass
+
+    def on_merge(self, effective_changes):
+        pass
diff --git a/scripts/tui/src/ovirt/node/plugins/logging_page.py b/scripts/tui/src/ovirt/node/plugins/logging_page.py
new file mode 100644
index 0000000..71e6b22
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/logging_page.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+#
+# logging.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Configure Logging
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "Logging"
+
+    def rank(self):
+        return 50
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                # The target address
+                "max_log_size": "1024",
+                "rsyslog.address": "",
+                "rsyslog.port": "514",
+                "netconsole.address": "",
+                "netconsole.port": "6666",
+            }
+        return self._model
+
+    def validators(self):
+        """Validators validate the input on change and give UI feedback
+        """
+        return {
+                "max_log_size": ovirt.node.valid.Number(min=0),
+                "rsyslog.address": ovirt.node.valid.FQDNOrIPAddress(),
+                "rsyslog.port": ovirt.node.valid.Port(),
+                "netconsole.address": ovirt.node.valid.FQDNOrIPAddress(),
+                "netconsole.port": ovirt.node.valid.Port(),
+            }
+
+    def ui_content(self):
+        widgets = [
+            ("header", ovirt.node.ui.Header("Logging")),
+
+            ("max_log_size", ovirt.node.ui.Entry("Logrotate Max Log " +
+                                                 "Size (KB)")),
+
+            ("rsyslog.header", ovirt.node.ui.Label(
+                                    "RSyslog is an enhanced multi-threaded " +
+                                    "syslogd")),
+            ("rsyslog.address", ovirt.node.ui.Entry("Server Address")),
+            ("rsyslog.port", ovirt.node.ui.Entry("Server Port")),
+
+            ("netconsole.header", ovirt.node.ui.Label(
+                                    "Netconsole service allows a remote sys" +
+                                    "log daemon to record printk() messages")),
+            ("netconsole.address", ovirt.node.ui.Entry("Server Address")),
+            ("netconsole.port", ovirt.node.ui.Entry("Server Port")),
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def on_change(self, changes):
+        pass
+        self._model.update(changes)
+
+    def on_merge(self, effective_changes):
+        pass
diff --git a/scripts/tui/src/ovirt/node/plugins/monitoring_page.py b/scripts/tui/src/ovirt/node/plugins/monitoring_page.py
new file mode 100644
index 0000000..00f427c
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/monitoring_page.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+#
+# monitoring_page.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Configure Monitoring
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "Monitoring"
+
+    def rank(self):
+        return 90
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                "collectd.address": "",
+                "collectd.port": "7634",
+            }
+        return self._model
+
+    def validators(self):
+        return {
+                "collectd.address": ovirt.node.valid.FQDNOrIPAddress(),
+                "collectd.port": ovirt.node.valid.Port(),
+            }
+
+    def ui_content(self):
+        widgets = [
+            ("header", ovirt.node.ui.Header("Monitoring Configuration")),
+
+            ("label", ovirt.node.ui.Label("Collectd gathers statistics " +
+                            "about the system and can be used to find " +
+                            "performance bottlenecks and predict future " +
+                            "system load.")),
+
+            ("collectd.address", ovirt.node.ui.Entry("Server Address")),
+            ("collectd.port", ovirt.node.ui.Entry("Server Port")),
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def on_change(self, changes):
+        pass
+        self._model.update(changes)
+
+    def on_merge(self, effective_changes):
+        pass
diff --git a/scripts/tui/src/ovirt/node/plugins/network_page.py b/scripts/tui/src/ovirt/node/plugins/network_page.py
new file mode 100644
index 0000000..aee4fa5
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/network_page.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+#
+# status.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+import ovirt.node.utils.network
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    """This is the network page
+    """
+
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "Network"
+
+    def rank(self):
+        return 10
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                "hostname": "localhost.example.com",
+                "dns[0]": "192.168.122.1",
+                "dns[1]": "",
+                "ntp[0]": "fedora.pool.ntp.org",
+                "ntp[1]": "",
+            }
+
+        nameservers = ovirt.node.utils.network.nameservers()
+        for idx, nameserver in enumerate(nameservers):
+            self._model["dns[%d]" % idx] = nameserver
+
+        timeservers = ovirt.node.utils.network.timeservers()
+        for idx, timeserver in enumerate(timeservers):
+            self._model["ntp[%d]" % idx] = timeserver
+
+        return self._model
+
+    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()
+        return {
+                "hostname": ovirt.node.valid.FQDNOrIPAddress(),
+                "dns[0]": ovirt.node.valid.IPAddress(),
+                "dns[1]": ip_or_empty,
+                "ntp[0]": ovirt.node.valid.FQDNOrIPAddress(),
+                "ntp[1]": fqdn_ip_or_empty,
+            }
+
+    def ui_content(self):
+        """Describes the UI this plugin requires
+        This is an ordered list of (path, widget) tuples.
+        """
+        widgets = [
+            ("hostname",
+                ovirt.node.ui.Entry("Hostname")),
+            ("hostname._space", ovirt.node.ui.Divider()),
+
+            ("nics", ovirt.node.ui.Table(
+                        "Device   Status         Model    MAC Address",
+                        self._get_nics())),
+            ("nics._space", ovirt.node.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()),
+
+            ("ntp[0]", ovirt.node.ui.Entry("NTP Server 1")),
+            ("ntp[1]", ovirt.node.ui.Entry("NTP Server 2")),
+            ("ntp._space", ovirt.node.ui.Divider()),
+
+#            ("action", ovirt.node.ui.Buttons(["Lock", "Log Off", "Restart",
+#                                              "Power Off"])),
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def _get_nics(self):
+        justify = lambda txt, l: txt.ljust(l)[0:l]
+        node_nics = [
+                ("em1",
+                    "em1      Configured     e1000    00:11:22:33:44:55"),
+                ("p1p6",
+                    "p1p6     Unconfigured   bnx2     10:21:32:43:54:65"),
+                ]
+        node_nics = []
+        for name, nic in ovirt.node.utils.network.node_nics().items():
+            bootproto = "Configured" if nic["bootproto"] else "Unconfigured"
+            description = " ".join([
+                justify(nic["name"], 8),
+                justify(bootproto, 14),
+                justify(nic["driver"], 8),
+                justify(nic["hwaddr"], 17)
+                ])
+            node_nics.append((name, description))
+        return node_nics
+
+    def on_change(self, changes):
+        pass
+
+    def on_merge(self, effective_changes):
+        effective_model = self._model.update(changes)
+
+        if "dns[0]" in effective_changes or \
+           "dns[1]" in effective_changes:
+            new_servers = [v for k, v in effective_model \
+                             if k.startswith("dns[")]
+            LOGGER.info("Setting new nameservers: %s" % new_servers)
+            ovirt.node.utils.network.nameservers(new_servers)
+
+        if "ntp[0]" in effective_changes or \
+           "ntp[1]" in effective_changes:
+            new_servers = [v for k, v in effective_model \
+                             if k.startswith("ntp[")]
+            LOGGER.info("Setting new timeservers: %s" % new_servers)
+            ovirt.node.utils.network.timeservers(new_servers)
diff --git a/scripts/tui/src/ovirt/node/plugins/ping.py b/scripts/tui/src/ovirt/node/plugins/ping.py
index 855a11a..f40e7eb 100644
--- a/scripts/tui/src/ovirt/node/plugins/ping.py
+++ b/scripts/tui/src/ovirt/node/plugins/ping.py
@@ -39,7 +39,7 @@
         return "Tools (ping)"
 
     def rank(self):
-        return 70
+        return 999
 
     def model(self):
         """Returns the model of this plugin
diff --git a/scripts/tui/src/ovirt/node/plugins/remote_storage_page.py b/scripts/tui/src/ovirt/node/plugins/remote_storage_page.py
new file mode 100644
index 0000000..87fe0ef
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/remote_storage_page.py
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+#
+# remote_storage_page.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Configure Remote Storage
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "Remote Storage"
+
+    def rank(self):
+        return 70
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                "iscsi.initiator_name": "",
+            }
+        return self._model
+
+    def validators(self):
+        is_initiator_name = lambda v: (None if len(v.split(":")) == 2
+                                            else "Invalid IQN.")
+        return {
+                "iscsi.initiator_name": is_initiator_name,
+            }
+
+    def ui_content(self):
+        widgets = [
+            ("header", ovirt.node.ui.Header("Remote Storage")),
+
+            ("iscsi.initiator_name", ovirt.node.ui.Entry("iSCSI Initiator " +
+                                                         "Name")),
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def on_change(self, changes):
+        pass
+        self._model.update(changes)
+
+    def on_merge(self, effective_changes):
+        pass
diff --git a/scripts/tui/src/ovirt/node/plugins/security_page.py b/scripts/tui/src/ovirt/node/plugins/security_page.py
new file mode 100644
index 0000000..7e7ba7e
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/security_page.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+#
+# security_page.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Configure Security
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "Security"
+
+    def rank(self):
+        return 20
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                "ssh.enabled": "no",
+                "passwd.admin.password": "",
+                "passwd.admin.password_confirmation": "",
+            }
+        return self._model
+
+    def validators(self):
+        return {
+                "passwd.admin.password": ovirt.node.valid.Text(),
+                "passwd.admin.password_confirmation": ovirt.node.valid.Text(),
+            }
+
+    def ui_content(self):
+        widgets = [
+            ("ssh.address", ovirt.node.ui.Header("Remote Access")),
+            ("ssh.enabled", ovirt.node.ui.Options(
+                "Enable ssh password authentication",
+                [("yes", "Yes"), ("no", "No")])),
+            ("ssh._divider", ovirt.node.ui.Divider()),
+
+
+            ("passwd._label", ovirt.node.ui.Label("Local Access")),
+            ("passwd.admin.password", ovirt.node.ui.PasswordEntry("Password")),
+            ("passwd.admin.password_confirmation", ovirt.node.ui.PasswordEntry(
+                "Confirm Password")),
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def on_change(self, changes):
+        self._model.update(changes)
+
+        if self._model["passwd.admin.password"] != \
+           self._model["passwd.admin.password_confirmation"]:
+            raise ovirt.node.exceptions.InvalidData("Passwords do not match.")
+
+    def on_merge(self, effective_changes):
+        pass
diff --git a/scripts/tui/src/ovirt/node/plugins/snmp_page.py b/scripts/tui/src/ovirt/node/plugins/snmp_page.py
new file mode 100644
index 0000000..29d15d4
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/plugins/snmp_page.py
@@ -0,0 +1,86 @@
+#!/usr/bin/python
+#
+# snmp_page.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Configure SNMP
+"""
+import logging
+
+import ovirt.node.plugins
+import ovirt.node.valid
+import ovirt.node.ui
+import ovirt.node.utils
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Plugin(ovirt.node.plugins.NodePlugin):
+    _model = None
+    _widgets = None
+
+    def name(self):
+        return "SNMP"
+
+    def rank(self):
+        return 40
+
+    def model(self):
+        if not self._model:
+            self._model = {
+                "snmp.enabled": "no",
+                "snmp.password": "",
+                "snmp.password_confirmation": "",
+            }
+        return self._model
+
+    def validators(self):
+        return {
+                "passwd.admin.password": ovirt.node.valid.Text(),
+                "passwd.admin.password_confirmation": ovirt.node.valid.Text(),
+            }
+
+    def ui_content(self):
+        widgets = [
+            ("snmp._header", ovirt.node.ui.Header("SNMP")),
+            ("snmp.enabled", ovirt.node.ui.Options("Enable SNMP",
+                [("yes", "Yes"), ("no", "No")])),
+            ("ssh._divider", ovirt.node.ui.Divider()),
+
+
+            ("snmp.password._label", ovirt.node.ui.Label("SNMP Password")),
+            ("snmp.password", ovirt.node.ui.PasswordEntry("Password")),
+            ("snmp.password_confirmation", ovirt.node.ui.PasswordEntry(
+                "Confirm Password")),
+        ]
+        # Save it "locally" as a dict, for better accessability
+        self._widgets = dict(widgets)
+
+        page = ovirt.node.ui.Page(widgets)
+        return page
+
+    def on_change(self, changes):
+        self._model.update(changes)
+
+        if self._model["snmp.password"] != \
+           self._model["snmp.password_confirmation"]:
+            raise ovirt.node.exceptions.InvalidData("Passwords do not match.")
+
+    def on_merge(self, effective_changes):
+        pass
diff --git a/scripts/tui/src/ovirt/node/plugins/status.py b/scripts/tui/src/ovirt/node/plugins/status_page.py
similarity index 99%
rename from scripts/tui/src/ovirt/node/plugins/status.py
rename to scripts/tui/src/ovirt/node/plugins/status_page.py
index e1c425b..550d503 100644
--- a/scripts/tui/src/ovirt/node/plugins/status.py
+++ b/scripts/tui/src/ovirt/node/plugins/status_page.py
@@ -44,7 +44,7 @@
         return "Status"
 
     def rank(self):
-        return 10
+        return 0
 
     def model(self):
         if not self._model:
diff --git a/scripts/tui/src/ovirt/node/plugins/usage.py b/scripts/tui/src/ovirt/node/plugins/usage.py
index c3549f0..3da8cdd 100644
--- a/scripts/tui/src/ovirt/node/plugins/usage.py
+++ b/scripts/tui/src/ovirt/node/plugins/usage.py
@@ -43,7 +43,7 @@
     def name(self):
         return "Usage"
 
-    rank = lambda self: 10
+    rank = lambda self: 999
 
     def ui_content(self):
         widgets = [
diff --git a/scripts/tui/src/ovirt/node/tui.py b/scripts/tui/src/ovirt/node/tui.py
index ff06a85..8198b7b 100644
--- a/scripts/tui/src/ovirt/node/tui.py
+++ b/scripts/tui/src/ovirt/node/tui.py
@@ -54,8 +54,10 @@
     footer = u"Press ctrl+c to exit"
 
     palette = [('header', 'white', 'dark blue'),
-               ('menu.entry', '', ''),
-               ('menu.entry:focus', 'white', 'light blue', 'standout'),
+               ('table', 'dark gray', ''),
+               ('table.header', 'bold', ''),
+               ('table.entry', 'dark gray', ''),
+               ('table.entry:focus', 'white', 'light blue', 'standout'),
                ('main.menu', 'black', ''),
                ('main.menu.frame', 'light gray', ''),
                ('plugin.widget.entry', 'dark gray', ''),
diff --git a/scripts/tui/src/ovirt/node/ui/__init__.py b/scripts/tui/src/ovirt/node/ui/__init__.py
index fc948fa..618a3e8 100644
--- a/scripts/tui/src/ovirt/node/ui/__init__.py
+++ b/scripts/tui/src/ovirt/node/ui/__init__.py
@@ -132,7 +132,7 @@
         return self.children
 
     @widgets.setter
-    def widgets(self, v):
+    def set_widgets(self, v):
         self.children = v
 
 
@@ -148,7 +148,7 @@
 
     def __init__(self, title, children):
         self.title = title
-        self.close(False) # FIXME hack
+        self.close(False)
         super(Dialog, self).__init__(children)
 
     @Element.signal_change
@@ -236,6 +236,12 @@
 
 
 class Options(Element):
+    """A selection of options
+
+    Args:
+        label: The caption of the options
+        options:
+    """
     def __init__(self, label, options):
         self.label = label
         self.options = options
@@ -253,6 +259,12 @@
 
 
 class ProgressBar(Element):
+    """A abstract progress bar.
+
+    Args:
+        current: The initial value
+        done: The maximum value
+    """
     def __init__(self, current=0, done=100):
         self.current(current)
         self.done = done
@@ -260,6 +272,29 @@
 
     @Element.signal_change
     def current(self, current=None):
+        """Get/Set the current status
+
+        Args:
+            current: New value or None
+
+        Returns:
+            The current progress
+        """
         if current is not None:
             self._current = current
         return self._current
+
+
+class Table(Element):
+    """Represents a simple Table with one column
+
+    Args:
+        header: A string
+        items: A list of tuples (key, label)
+        height: The height of the Table
+    """
+
+    def __init__(self, header, items, height=3):
+        self.header = header
+        self.items = items
+        self.height = height
diff --git a/scripts/tui/src/ovirt/node/ui/builder.py b/scripts/tui/src/ovirt/node/ui/builder.py
index 96aee29..7b4161c 100644
--- a/scripts/tui/src/ovirt/node/ui/builder.py
+++ b/scripts/tui/src/ovirt/node/ui/builder.py
@@ -93,6 +93,7 @@
         ovirt.node.ui.Options: build_options,
         ovirt.node.ui.Row: build_row,
         ovirt.node.ui.ProgressBar: build_progressbar,
+        ovirt.node.ui.Table: build_table,
     }
 
     # Check if builder is available for UI Element
@@ -200,8 +201,10 @@
             elif type(r) in [ovirt.node.ui.Dialog]:
                 w = build_page(tui, plugin, r)
                 dialog = tui.display_dialog(w, r.title)
+
                 def on_item_close_changed_cb(i, v):
                     dialog.close()
+
                 r.connect_signal("close", on_item_close_changed_cb)
 
         else:
@@ -250,3 +253,20 @@
     item.connect_signal("current", on_item_current_change_cb)
 
     return widget
+
+
+def build_table(path, item, tui, plugin):
+    children = []
+    for key, label in item.items:
+        c = _build_tableitem(path, plugin, key, label)
+        children.append(c)
+    widget = ovirt.node.ui.widgets.TableWidget(item.header, children,
+                                               item.height)
+
+    return widget
+
+def _build_tableitem(path, plugin, key, label):
+    c = ovirt.node.ui.widgets.TableEntryWidget(label)
+    urwid.connect_signal(c, "click",
+                         lambda w, d: plugin._on_ui_change(d), {path: key})
+    return c
diff --git a/scripts/tui/src/ovirt/node/ui/widgets.py b/scripts/tui/src/ovirt/node/ui/widgets.py
index fb358f6..bb6d4fa 100644
--- a/scripts/tui/src/ovirt/node/ui/widgets.py
+++ b/scripts/tui/src/ovirt/node/ui/widgets.py
@@ -37,20 +37,81 @@
         return key
 
 
-class PluginMenuEntry(urwid.AttrMap):
-    """An entry in the main menu
+class TableEntryWidget(urwid.AttrMap):
+    """An entry in a table
     """
-    __text = None
+    _text = None
 
+    signals = ["click"]
+
+    def __init__(self, title):
+        self._text = SelectableText(title)
+#        self._text = Button(title)
+#        self._text.button_left = ""
+#        self._text.button_right = ""
+        super(TableEntryWidget, self).__init__(self._text, 'table.entry',
+                                                           'table.entry:focus')
+
+    def keypress(self, size, key):
+        if self._command_map[key] != 'activate':
+            return key
+        self._emit('click')
+
+    def mouse_event(self, size, event, button, x, y, focus):
+        if button != 1 or not urwid.util.is_mouse_press(event):
+            return False
+
+        self._emit('click')
+        return True
+
+
+class TableWidget(urwid.WidgetWrap):
+    """A table, with a single column
+    """
+    __walker = None
+    __list = None
+    __list_attrmap = None
+    __linebox = None
+    __linebox_attrmap = None
+
+    signals = ['changed']
+
+    _table_attr = "table"
+    _header_attr = "table.header"
+
+    def __init__(self, header, items, height):
+        self.__label = urwid.Text(header)
+        self.__label_attrmap = urwid.AttrMap(self.__label, self._header_attr)
+        self.__items = items
+        self.__walker = urwid.SimpleListWalker(self.__items)
+        self.__list = urwid.ListBox(self.__walker)
+#        self.__list_linebox = urwid.LineBox(self.__list)
+
+        def __on_item_change():
+            widget, position = self.__list.get_focus()
+            urwid.emit_signal(self, "changed", widget)
+        urwid.connect_signal(self.__walker, 'modified', __on_item_change)
+
+        self.__box = urwid.BoxAdapter(self.__list, height)
+        self.__box_attrmap = urwid.AttrMap(self.__box, self._table_attr)
+
+        self.__pile = urwid.Pile([self.__label_attrmap, self.__box])
+
+        super(TableWidget, self).__init__(self.__pile)
+
+    def set_focus(self, n):
+        self.__list.set_focus(n)
+
+
+class PluginMenuEntry(TableEntryWidget):
     def __init__(self, title, plugin):
-        self.__text = SelectableText(title)
-        self.__text.plugin = plugin
-        super(PluginMenuEntry, self).__init__(self.__text, 'menu.entry',
-                                              'menu.entry:focus')
+        super(PluginMenuEntry, self).__init__(title)
+        self._text.plugin = plugin
 
 
 class PluginMenu(urwid.WidgetWrap):
     """The main menu listing all available plugins (which have a UI)
+    FIXME Use TableWidget
     """
     __pages = None
     __walker = None
@@ -236,6 +297,9 @@
 
     selectable = lambda self: True
 
+    _button_attr = "plugin.widget.button"
+    _button_disabled_attr = "plugin.widget.button.disabled"
+
     def __init__(self, label):
         self._button = urwid.Button(label)
 
@@ -243,8 +307,7 @@
             urwid.emit_signal(self, 'click', self)
         urwid.connect_signal(self._button, 'click', on_click_cb)
 
-        self._button_attrmap = urwid.AttrMap(self._button,
-                                              "plugin.widget.button")
+        self._button_attrmap = urwid.AttrMap(self._button, self._button_attr)
 
         self._padding = urwid.Padding(self._button_attrmap,
                                       width=len(label) + 4)
@@ -254,10 +317,10 @@
     def enable(self, is_enabled):
         self.selectable = lambda: is_enabled
         if is_enabled:
-            self._button_attrmap.set_attr_map({None: ""})
+            self._button_attrmap.set_attr_map({None: self._button_attr})
         else:
             self._button_attrmap.set_attr_map({
-                None: "plugin.widget.button.disabled"
+                None: self._button_disabled_attr
                 })
 
 
diff --git a/scripts/tui/src/ovirt/node/utils/__init__.py b/scripts/tui/src/ovirt/node/utils/__init__.py
index a1d2ecb..6c932cf 100644
--- a/scripts/tui/src/ovirt/node/utils/__init__.py
+++ b/scripts/tui/src/ovirt/node/utils/__init__.py
@@ -21,3 +21,33 @@
 """
 Utility functions
 """
+
+import augeas as _augeas
+import logging
+
+LOGGER = logging.getLogger(__name__)
+
+
+class AugeasWrapper(object):
+    _aug = _augeas.Augeas()
+
+    def __init__(self):
+#        self._aug = _augeas.Augeas() # Is broken
+        self._aug.set("/augeas/save/copy_if_rename_fails", "")
+
+    def get(self, p):
+        return self._aug.get(p)
+
+    def set(self, p, v):
+        self._aug.set(p, v)
+        self.save()
+
+    def remove(self, p):
+        self._aug.remove(p)
+        self.save()
+
+    def save(self):
+        return self._aug.save()
+
+    def match(self, p):
+        return self._aug.match(p)
diff --git a/scripts/tui/src/ovirt/node/utils/network.py b/scripts/tui/src/ovirt/node/utils/network.py
new file mode 100644
index 0000000..42d9a45
--- /dev/null
+++ b/scripts/tui/src/ovirt/node/utils/network.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+#
+# network.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# 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.
+
+"""
+Some convenience functions related to networking
+"""
+
+import gudev
+import os.path
+import logging
+from ovirt.node.utils import AugeasWrapper as Augeas
+
+LOGGER = logging.getLogger(__name__)
+
+
+def _query_udev_nics():
+    client = gudev.Client(['net'])
+    devices = client.query_by_subsystem("net")
+    return [d.get_property("INTERFACE") for d in devices]
+
+
+def all_nics():
+    """Retuns all system NICs (via udev)
+
+    >>> "lo" in all_nics()
+    True
+
+    Returns:
+        Dict with NIC (name, info) mappings for all known NICS
+    """
+    return _collect_nic_informations(_query_udev_nics())
+
+
+def _collect_nic_informations(nics):
+    """Collects all NIC relevant informations for a list of NICs
+
+    >>> infos = _collect_nic_informations(_query_udev_nics())
+    >>> "lo" in infos
+    True
+    >>> "driver" in infos["lo"]
+    True
+    >>> infos["lo"]["driver"]
+    'unknown'
+
+    Args:
+        nics: List of NIC names
+    Returns:
+        A dict of (nic-name, nic-infos-dict)
+    """
+    infos = {}
+
+    client = gudev.Client(['net'])
+    for d in client.query_by_subsystem("net"):
+#        assert d.has_property("ID_VENDOR_FROM_DATABASE"), \
+#                "udev informations are incomplete (udevadm re-trigger?)"
+
+        info = {"name": d.get_property("INTERFACE"),
+                "vendor": d.get_property("ID_VENDOR_FROM_DATABASE") or "unkown",
+                "devtype": d.get_property("DEVTYPE") or "unknown",
+                "devpath": d.get_property("DEVPATH")
+               }
+
+        infos[info["name"]] = info
+
+    # Check if we cover all req. NICs
+    unknown_nics = (set(nics) - set(infos))
+    if unknown_nics != set():
+        raise Exception("Couldn't gather informations for unknown NICs: %s" %
+                        unknown_nics)
+
+    for name, info in infos.items():
+        LOGGER.debug("Getting additional information for '%s'" % name)
+
+        # Driver
+        driver_symlink = "/sys/class/net/%s/device/driver" % name
+        driver = "unknown"
+        if os.path.islink(driver_symlink):
+            try:
+                driver = os.path.basename(os.readlink(driver_symlink))
+            except Exception as e:
+                LOGGER.warning("Exception while reading driver " +
+                               "of '%s' from '%s'" % (name, driver_symlink))
+        infos[name]["driver"] = driver
+
+
+        hwaddr = "unkown"
+        with open("/sys/class/net/%s/address" % name) as macfile:
+            hwaddr = macfile.read().strip()
+        infos[name]["hwaddr"] = hwaddr
+
+        aug = Augeas()
+        augbasepath = "/files/etc/sysconfig/network-scripts/ifcfg-%s"
+        augdevicepath = augbasepath % name
+
+        # Bootprotocol
+        info["type"] = aug.get(augdevicepath + "/TYPE")
+        if os.path.exists("/sys/class/net/%s/bridge" % name):
+            info["type"] = "bridge"
+
+        # Bootprotocol
+        info["bootproto"] = aug.get(augdevicepath + "/BOOTPROTO")
+
+        # Parent bridge
+        info["bridge"] = aug.get(augdevicepath + "/BRIDGE")
+
+        # VLAN
+        info["is_vlan"] = aug.get(augdevicepath + "/VLAN") is not None
+        if info["is_vlan"] != "." in name:
+            LOGGER.warning("NIC config says VLAN, name doesn't reflect " + \
+                           "that: %s" % name)
+        if info["is_vlan"]:
+            parts = name.split(".")
+            info["vlanid"] = parts[-1:]
+            info["parent"] = ".".join(parts[:-1])
+
+            info["type"] = "vlan"
+
+
+    return infos
+
+
+def relevant_nics(filter_bridges=True, filter_vlans=True):
+    """Retuns relevant system NICs (via udev)
+
+    Filters out
+    - loop
+    - bonds
+    - vnets
+    - bridges
+    - sit
+    - vlans
+
+    >>> "lo" in relevant_nics()
+    False
+
+    >>> "eth0" in relevant_nics() or "em1" in relevant_nics()
+    True
+
+    Args:
+        filter_bridges: If bridges shall be filtered out too
+        filter_vlans: If vlans shall be filtered out too
+    Returns:
+        List of strings, the NIC names
+    """
+    is_irrelevant = lambda n, p: ( \
+            n == "lo" or \
+            n.startswith("bond") or \
+            n.startswith("sit") or \
+            n.startswith("vnet") or \
+            n.startswith("tun") or \
+            n.startswith("wlan") or \
+            (("." in n) and filter_vlans) or \
+            ((p["type"] == "bridge") and filter_bridges))
+
+    relevant_nics = {n: p for n, p in all_nics().items() \
+                          if not is_irrelevant(n, p)}
+
+    irrelevant_names = set(all_nics().keys()) - set(relevant_nics.keys())
+    LOGGER.debug("Irrelevant interfaces: %s" % irrelevant_names)
+
+    return relevant_nics
+
+
+def node_nics():
+    """Returns Node's NIC model.
+    This squashes nic, bridge and vlan informations.
+
+    >>> node_nics() != None
+    True
+    """
+    all_nics = relevant_nics(filter_bridges=False, filter_vlans=False)
+
+    bridges = [nic for nic, info in all_nics.items() \
+               if info["type"] == "bridge"]
+    vlans = [nic for nic, info in all_nics.items() \
+             if info["type"] == "vlan"]
+    nics = [nic for nic, info in all_nics.items() \
+            if info["name"] not in bridges + vlans]
+
+    LOGGER.debug("Bridges: %s" % bridges)
+    LOGGER.debug("VLANs: %s" % vlans)
+    LOGGER.debug("NICs: %s" % nics)
+
+    node_nics = {}
+    for name in nics:
+        info = all_nics[name]
+        if info["bridge"]:
+            bridge = all_nics[info["bridge"]]
+            info["bootproto"] = bridge["bootproto"]
+        node_nics[name] = info
+
+    for name in vlans:
+        info = all_nics[name]
+        if info["vlanid"]:
+            node_nics[info["parent"]]["vlanid"] = info["vlanid"][0]
+
+    LOGGER.debug("Node NICs: %s" % node_nics)
+
+    return node_nics
+
+
+def _aug_get_or_set(augpath, new_servers=None):
+    """Get or set some servers
+    """
+    aug = Augeas()
+
+    servers = []
+    for path in aug.match(augpath):
+        servers.append(aug.get(path))
+
+    if new_servers:
+        itempath = lambda idx: "%s[%d]" % (augpath, idx+1)
+        for idx, server in enumerate(new_servers):
+            aug.set(itempath(idx), server)
+        if len(servers) > len(new_servers):
+            for idx in range(len(servers) + 1, len(new_servers)):
+                aug.remove(itempath(idx))
+    return servers
+
+
+def nameservers(new_servers=None):
+    """Get or set DNS servers
+    >>> len(nameservers()) > 0
+    True
+    """
+    augpath = "/files/etc/resolv.conf/nameserver"
+    return _aug_get_or_set(augpath, new_servers)
+
+
+def timeservers(new_servers=None):
+    """Get or set TIME servers
+    >>> len(nameservers()) > 0
+    True
+    """
+    augpath = "/files/etc/ntp.conf/server"
+    return _aug_get_or_set(augpath, new_servers)
diff --git a/scripts/tui/src/ovirt/node/valid.py b/scripts/tui/src/ovirt/node/valid.py
index 5965987..460ce6a 100644
--- a/scripts/tui/src/ovirt/node/valid.py
+++ b/scripts/tui/src/ovirt/node/valid.py
@@ -248,13 +248,13 @@
 class IPAddress(Validator):
     """Allows any IPv4 or IPv6 address
 
-    >>> FQDNOrIPAddress()("127.0.0.1")
+    >>> IPAddress()("127.0.0.1")
     True
-    >>> FQDNOrIPAddress()("::1")
+    >>> IPAddress()("::1")
     True
-    >>> FQDNOrIPAddress().validate("example.com")
+    >>> IPAddress().validate("example.com")
     False
-    >>> FQDNOrIPAddress().validate("")
+    >>> IPAddress().validate("")
     False
     """
 


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

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