[node-patches] Change in ovirt-node[master]: setup: Add more logic and fixes to pages

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


Fabian Deutsch has uploaded a new change for review.

Change subject: setup: Add more logic and fixes to pages
......................................................................

setup: Add more logic and fixes to pages

Change-Id: I1e8b50d71990a3df3e69f0ff00b0ae52d17a7cdc
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/src/ovirt/node/config/defaults.py
M scripts/tui/src/ovirt/node/exceptions.py
M scripts/tui/src/ovirt/node/plugins.py
M scripts/tui/src/ovirt/node/setup/keyboard_page.py
M scripts/tui/src/ovirt/node/setup/logging_page.py
M scripts/tui/src/ovirt/node/setup/monitoring_page.py
M scripts/tui/src/ovirt/node/setup/remote_storage_page.py
M scripts/tui/src/ovirt/node/setup/security_page.py
M scripts/tui/src/ovirt/node/setup/snmp_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/__init__.py
M scripts/tui/src/ovirt/node/utils/security.py
15 files changed, 307 insertions(+), 126 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/68/9968/1

diff --git a/scripts/tui/src/ovirt/node/config/defaults.py b/scripts/tui/src/ovirt/node/config/defaults.py
index 19a7069..5e90eae 100644
--- a/scripts/tui/src/ovirt/node/config/defaults.py
+++ b/scripts/tui/src/ovirt/node/config/defaults.py
@@ -203,7 +203,8 @@
             value = cfg[key] if key in cfg else self.none_value
             values += (value,)
         assert len(varnames) == len(values)
-        return dict(zip(varnames, values))
+        cfg = dict(zip(varnames, values))
+        return cfg
 
     def clear(self):
         """Remove the configuration for this item
@@ -354,6 +355,20 @@
         return cfg
 
     def transaction(self):
+        return self.__legacy_transaction()
+
+    def __legacy_transaction(self):
+        class ConfigureNameservers(utils.Transaction.Element):
+            def commit(self):
+                import ovirtnode.network as onet
+                net = onet.Network()
+                net.configure_dns()
+
+        tx = utils.Transaction("Configuring nameservers")
+        tx.append(ConfigureNameservers())
+        return tx
+
+    def __new_transaction(self):
         """Derives the nameserver config from OVIRT_DNS
 
         1. Parse nameservers from defaults
@@ -445,7 +460,18 @@
         return cfg
 
     def transaction(self):
-        return utils.Transaction("Configuring timeserver")
+        return self.__legacy_transaction()
+
+    def __legacy_transaction(self):
+        class ConfigureTimeservers(utils.Transaction.Element):
+            def commit(self):
+                import ovirtnode.network as onet
+                net = onet.Network()
+                net.configure_ntp()
+
+        tx = utils.Transaction("Configuring timeservers")
+        tx.append(ConfigureTimeservers())
+        return tx
 
 
 class Syslog(NodeConfigFileSection):
@@ -469,6 +495,9 @@
         valid.Port()(port)
 
     def transaction(self):
+        return self.__legacy_transaction()
+
+    def __legacy_transaction(self):
         cfg = dict(self.retrieve())
         server, port = (cfg["server"], cfg["port"])
 
@@ -501,6 +530,26 @@
     def update(self, server, port):
         valid.FQDNOrIPAddress()(server)
         valid.Port()(port)
+
+    def transaction(self):
+        self.__legacy_transaction()
+
+    def __legacy_transaction(self):
+        cfg = dict(self.retrieve())
+        server, port = (cfg["server"], cfg["port"])
+
+        class ConfigureCollectd(utils.Transaction.Element):
+            def commit(self):
+                import ovirt_config_setup.collectd as ocollectd
+                if ocollectd.write_collectd_config(server, port):
+                    self.logger.debug("Collectd was configured successfully")
+                else:
+                    raise exceptions.TransactionError("Failed to configure " +
+                                                       "collectd")
+
+        tx = utils.Transaction("Configuring collectd")
+        tx.append(ConfigureCollectd())
+        return tx
 
 
 class RHN(NodeConfigFileSection):
@@ -662,12 +711,13 @@
     >>> fn = "/tmp/cfg_dummy"
     >>> cfgfile = ConfigFile(fn, SimpleProvider)
     >>> n = iSCSI(cfgfile)
-    >>> n.update("node.example.com", "target.example.com", "10.0.0.8", "42")
+    >>> n.update("iqn.1992-01.com.example:node",
+    ...          "iqn.1992-01.com.example:target", "10.0.0.8", "42")
     >>> data = sorted(n.retrieve().items())
     >>> data[:2]
-    [('name', 'node.example.com'), ('target_host', '10.0.0.8')]
+    [('name', 'iqn.1992-01.com.example:node'), ('target_host', '10.0.0.8')]
     >>> data[2:]
-    [('target_name', 'target.example.com'), ('target_port', '42')]
+    [('target_name', 'iqn.1992-01.com.example:target'), ('target_port', '42')]
     """
     keys = ("OVIRT_ISCSI_NODE_NAME",
             "OVIRT_ISCSI_TARGET_NAME",
@@ -805,15 +855,43 @@
     >>> n = CIM(cfgfile)
     >>> n.update(True)
     >>> n.retrieve()
-    {'enabled': '1'}
+    {'enabled': True}
     """
     keys = ("OVIRT_CIM_ENABLED",)
 
     @NodeConfigFileSection.map_and_update_defaults_decorator
     def update(self, enabled):
         return {
-                "OVIRT_CIM_ENABLED": "1" if utils.parse_bool(enabled) else "0"
+                "OVIRT_CIM_ENABLED": "1" if utils.parse_bool(enabled) else None
                 }
+
+    def retrieve(self):
+        cfg = dict(NodeConfigFileSection.retrieve(self))
+        cfg.update({
+                "enabled": True if cfg["enabled"] == "1" else None
+                })
+        return cfg
+
+    def transaction(self):
+        cfg = dict(self.retrieve())
+        enabled = cfg["enabled"]
+
+        class ConfigureCIM(utils.Transaction.Element):
+            def commit(self):
+                # FIXME snmp plugin needs to be placed somewhere else (in src)
+                import ovirt_config_setup.cim as ocim
+                if enabled:
+                    if ocim.enable_cim():
+                        self.logger.debug("Configured CIM successfully")
+                    else:
+                        raise exceptions.TransactionError("CIM configuration" +
+                                                          " failed")
+
+        # FIXME setting password is missing
+
+        tx = utils.Transaction("Configuring SNMP")
+        tx.append(ConfigureCIM())
+        return tx
 
 
 class Keyboard(NodeConfigFileSection):
@@ -924,8 +1002,8 @@
 
     def transaction(self):
         cfg = dict(self.retrieve())
-        pwauth, num_bytes, aesni = (cfg["pwauth"], cfg["num_bytes"],
-                                    cfg["aesni"])
+        pwauth, num_bytes, disable_aesni = (cfg["pwauth"], cfg["num_bytes"],
+                                            cfg["disable_aesni"])
 
         ssh = utils.security.Ssh()
 
@@ -939,13 +1017,13 @@
 
         class ConfigureAESNI(utils.Transaction.Element):
             def commit(self):
-                ssh.aes_ni(aesni)
+                ssh.disable_aesni(disable_aesni)
 
         tx = utils.Transaction("Configuring SSH")
         if pwauth:
             tx.append(ConfigurePasswordAuthentication())
         if num_bytes:
             tx.append(ConfigureStrongRNG())
-        if aesni:
+        if disable_aesni:
             tx.append(ConfigureAESNI())
         return tx
diff --git a/scripts/tui/src/ovirt/node/exceptions.py b/scripts/tui/src/ovirt/node/exceptions.py
index 181c587..eae160d 100644
--- a/scripts/tui/src/ovirt/node/exceptions.py
+++ b/scripts/tui/src/ovirt/node/exceptions.py
@@ -24,30 +24,30 @@
 """
 
 
-class InvalidData(Exception):
-    """E.g. if a string contains characters which are not allowed
-    """
+class ExceptionWithMessage(Exception):
     def __init__(self, msg):
         self.message = msg
 
     def __str__(self):
         return repr(self.message)
+
+
+class InvalidData(ExceptionWithMessage):
+    """E.g. if a string contains characters which are not allowed
+    """
+    pass
 
 
 class Concern(InvalidData):
     """E.g. if a password is not secure enough
     FIXME very ... unspecific
     """
-    def __init__(self, msg):
-        self.message = msg
-
-    def __str__(self):
-        return repr(self.message)
+    pass
 
 
-class PreconditionFailed(Exception):
-    def __init__(self, msg):
-        self.message = msg
+class TransactionError(ExceptionWithMessage):
+    pass
 
-    def __str__(self):
-        return repr(self.message)
+
+class PreconditionError(TransactionError):
+    pass
diff --git a/scripts/tui/src/ovirt/node/plugins.py b/scripts/tui/src/ovirt/node/plugins.py
index 94f99d6..2ae6d3c 100644
--- a/scripts/tui/src/ovirt/node/plugins.py
+++ b/scripts/tui/src/ovirt/node/plugins.py
@@ -239,7 +239,7 @@
         """Called when data should be saved
         Calls merge_changes, but only with values that really changed
         """
-        effective_changes = self.pending_changes() or {}
+        effective_changes = self.pending_changes()
         is_valid = False
 
         self.logger.debug("Request to apply model changes: %s" %
@@ -254,7 +254,7 @@
         if self.only_merge_on_valid_changes and not is_valid:
             msg = "There are still fields with invalid values."
             self.logger.warning(msg)
-            raise exceptions.PreconditionFailed(msg)
+            raise exceptions.PreconditionError(msg)
 
         successfull_merge = self.on_merge(effective_changes)
 
@@ -309,13 +309,16 @@
                 else:
                     effective_changes[key] = value
         else:
+            self.logger.debug("No changes at all detected.")
+        if not effective_changes:
             self.logger.debug("No effective changes detected.")
-        return effective_changes if len(effective_changes) > 0 else None
+        return effective_changes
 
     def dry_or(self, func):
         if self.application.args.dry:
             self.logger.info("Running dry, otherwise: %s" % func)
         else:
+            self.logger.info("Running %s" % func)
             func()
 
 
diff --git a/scripts/tui/src/ovirt/node/setup/keyboard_page.py b/scripts/tui/src/ovirt/node/setup/keyboard_page.py
index 840aca6..89e8cfc 100644
--- a/scripts/tui/src/ovirt/node/setup/keyboard_page.py
+++ b/scripts/tui/src/ovirt/node/setup/keyboard_page.py
@@ -39,7 +39,6 @@
 
     def model(self):
         cfg = defaults.Keyboard().retrieve()
-        self.logger.debug(cfg)
         model = {}
         model["keyboard.layout"] = cfg["layout"] or ""
         return model
diff --git a/scripts/tui/src/ovirt/node/setup/logging_page.py b/scripts/tui/src/ovirt/node/setup/logging_page.py
index 65e9af6..ee25e88 100644
--- a/scripts/tui/src/ovirt/node/setup/logging_page.py
+++ b/scripts/tui/src/ovirt/node/setup/logging_page.py
@@ -52,10 +52,10 @@
         model["logrotate.max_size"] = logrotate["max_size"] or "1024"
 
         model["rsyslog.address"] = syslog["server"] or ""
-        model["rsyslog.port"] = syslog["port"] or ""
+        model["rsyslog.port"] = syslog["port"] or "514"
 
         model["netconsole.address"] = netconsole["server"] or ""
-        model["netconsole.port"] = netconsole["port"] or ""
+        model["netconsole.port"] = netconsole["port"] or "6666"
 
         return model
 
diff --git a/scripts/tui/src/ovirt/node/setup/monitoring_page.py b/scripts/tui/src/ovirt/node/setup/monitoring_page.py
index bbfb6d1..14f35e8 100644
--- a/scripts/tui/src/ovirt/node/setup/monitoring_page.py
+++ b/scripts/tui/src/ovirt/node/setup/monitoring_page.py
@@ -18,17 +18,16 @@
 # 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.
+from ovirt.node import plugins, ui, valid, utils
+from ovirt.node.config import defaults
+from ovirt.node.plugins import ChangesHelper
 
 """
 Configure Monitoring
 """
 
-import ovirt.node.plugins
-import ovirt.node.valid
-import ovirt.node.ui
 
-
-class Plugin(ovirt.node.plugins.NodePlugin):
+class Plugin(plugins.NodePlugin):
     _model = None
     _widgets = None
 
@@ -39,40 +38,58 @@
         return 90
 
     def model(self):
-        if not self._model:
-            self._model = {
-                "collectd.address": "",
-                "collectd.port": "7634",
-            }
-        return self._model
+        cfg = defaults.Collectd().retrieve()
+        model = {
+            "collectd.address": cfg["server"] or "",
+            "collectd.port": cfg["port"] or "7634",
+        }
+        return model
 
     def validators(self):
         return {
-                "collectd.address": ovirt.node.valid.FQDNOrIPAddress(),
-                "collectd.port": ovirt.node.valid.Port(),
+                "collectd.address": valid.Empty() | valid.FQDNOrIPAddress(),
+                "collectd.port": valid.Port(),
             }
 
     def ui_content(self):
         widgets = [
-            ("header", ovirt.node.ui.Header("Monitoring Configuration")),
+            ("header", ui.Header("Monitoring Configuration")),
 
-            ("label", ovirt.node.ui.Label("Collectd gathers statistics " +
+            ("label", 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:")),
+            ("collectd.address", ui.Entry("Server Address:")),
+            ("collectd.port", ui.Entry("Server Port:")),
         ]
         # Save it "locally" as a dict, for better accessability
         self._widgets = dict(widgets)
 
-        page = ovirt.node.ui.Page(widgets)
+        page = ui.Page(widgets)
         return page
 
     def on_change(self, changes):
         pass
-        self._model.update(changes)
 
     def on_merge(self, effective_changes):
-        pass
+        self.logger.debug("Saving monitoring page")
+        changes = ChangesHelper(self.pending_changes(False))
+        model = self.model()
+        model.update(effective_changes)
+        effective_model = ChangesHelper(model)
+
+        self.logger.debug("Saving monitoring page: %s" % changes.changes)
+        self.logger.debug("monitoring model: %s" % effective_model.changes)
+
+        collectd_keys = ["collectd.address", "collectd.port"]
+
+        txs = utils.Transaction("Updating monitoring configuration")
+
+        if changes.any_key_in_change(collectd_keys):
+            model = defaults.Collectd()
+            model.update(*effective_model.get_key_values(collectd_keys))
+            txs += model.transaction()
+
+        txs.prepare()
+        self.dry_or(lambda: txs())
diff --git a/scripts/tui/src/ovirt/node/setup/remote_storage_page.py b/scripts/tui/src/ovirt/node/setup/remote_storage_page.py
index 202e8f6..55801c0 100644
--- a/scripts/tui/src/ovirt/node/setup/remote_storage_page.py
+++ b/scripts/tui/src/ovirt/node/setup/remote_storage_page.py
@@ -18,18 +18,16 @@
 # 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.
-from ovirt.node import utils, valid
+from ovirt.node import utils, valid, plugins, ui
 from ovirt.node.config import defaults
 from ovirt.node.plugins import ChangesHelper
-import ovirt.node.plugins
-import ovirt.node.ui
 
 """
 Configure Remote Storage
 """
 
 
-class Plugin(ovirt.node.plugins.NodePlugin):
+class Plugin(plugins.NodePlugin):
     _model = None
     _widgets = None
 
@@ -55,23 +53,21 @@
 
     def ui_content(self):
         widgets = [
-            ("header", ovirt.node.ui.Header("Remote Storage")),
+            ("header", ui.Header("Remote Storage")),
 
-            ("iscsi.initiator_name", ovirt.node.ui.Entry("iSCSI Initiator " +
-                                                         "Name:",
-                                                         align_vertical=True)),
+            ("iscsi.initiator_name", ui.Entry("iSCSI Initiator Name:",
+                                              align_vertical=True)),
 
-            ("divider", ovirt.node.ui.Divider()),
+            ("divider", ui.Divider()),
 
-            ("nfsv4.domain", ovirt.node.ui.Entry("NFSv4 Domain " +
-                                                 "(example.redhat.com):",
-                                                 align_vertical=True)),
+            ("nfsv4.domain", ui.Entry("NFSv4 Domain (example.redhat.com):",
+                                      align_vertical=True)),
         ]
 
         # Save it "locally" as a dict, for better accessability
         self._widgets = dict(widgets)
 
-        page = ovirt.node.ui.Page(widgets)
+        page = ui.Page(widgets)
         return page
 
     def on_change(self, changes):
diff --git a/scripts/tui/src/ovirt/node/setup/security_page.py b/scripts/tui/src/ovirt/node/setup/security_page.py
index 65f89b3..47d88fd 100644
--- a/scripts/tui/src/ovirt/node/setup/security_page.py
+++ b/scripts/tui/src/ovirt/node/setup/security_page.py
@@ -38,15 +38,16 @@
         return 20
 
     def model(self):
-        if not self._model:
-            self._model = {
-                "ssh.enabled": "no",
-                "strongrng.aesni": "no",
-                "strongrng.bytes_used": "",
-                "passwd.admin.password": "",
-                "passwd.admin.password_confirmation": "",
-            }
-        return self._model
+        cfg = defaults.SSH().retrieve()
+        self.logger.debug(cfg)
+        model = {
+            "ssh.pwauth": cfg["pwauth"] or False,
+            "strongrng.aesni": cfg["disable_aesni"] or False,
+            "strongrng.num_bytes": cfg["num_bytes"] or "",
+            "passwd.admin.password": "",
+            "passwd.admin.password_confirmation": "",
+        }
+        return model
 
     def validators(self):
         number_or_empty = valid.Number(range=[0, None]) | \
@@ -60,7 +61,7 @@
     def ui_content(self):
         widgets = [
             ("header[0]", ui.Header("Remote Access")),
-            ("ssh.enabled", ui.Checkbox("Enable ssh password authentication")),
+            ("ssh.pwauth", ui.Checkbox("Enable SSH password authentication")),
 
             ("header[1]", ui.Header("Strong Random Number Generator")),
             ("strongrng.aesni", ui.Checkbox("Enable AES-NI")),
@@ -78,11 +79,16 @@
         return page
 
     def on_change(self, changes):
-        self._model.update(changes)
+        m = self.model()
+        m.update(self.pending_changes() or {})
+        effective_model = ChangesHelper(m)
 
-        if self._model["passwd.admin.password"] != \
-           self._model["passwd.admin.password_confirmation"]:
-            raise exceptions.InvalidData("Passwords do not match.")
+        passwd_keys = ["passwd.admin.password",
+                       "passwd.admin.password_confirmation"]
+        if effective_model.any_key_in_change(passwd_keys):
+            passwd, passwdc = effective_model.get_key_values(passwd_keys)
+            if passwd != passwdc:
+                raise exceptions.InvalidData("Passwords do not match.")
 
     def on_merge(self, effective_changes):
         self.logger.debug("Saving security page")
@@ -95,7 +101,7 @@
         self.logger.debug("Remote security model: %s" %
                           effective_model.changes)
 
-        ssh_keys = ["ssh.enabled", "strongrng.num_bytes", "strongrng.aesni"]
+        ssh_keys = ["ssh.pwauth", "strongrng.num_bytes", "strongrng.aesni"]
         passwd_keys = ["passwd.admin.password",
                        "passwd.admin.password_confirmation"]
 
@@ -112,8 +118,11 @@
                 raise exceptions.InvalidData("Passwords do not match")
             passwd = utils.security.Passwd()
 
-            self.logger.debug("Setting admin password.")
-            self.dry_or(lambda: passwd.set_password("admin", pw))
+            class SetAdminPasswd(utils.Transaction.Element):
+                def commit(self):
+                    self.logger.debug("Setting admin password.")
+                    passwd.set_password("admin", pw)
+            txs += [SetAdminPasswd()]
 
         txs.prepare()  # Just to display something in dry mode
         self.dry_or(lambda: txs())
diff --git a/scripts/tui/src/ovirt/node/setup/snmp_page.py b/scripts/tui/src/ovirt/node/setup/snmp_page.py
index f25818b..d2fc9ab 100644
--- a/scripts/tui/src/ovirt/node/setup/snmp_page.py
+++ b/scripts/tui/src/ovirt/node/setup/snmp_page.py
@@ -18,12 +18,13 @@
 # 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.
+from ovirt.node import plugins, valid, ui, utils
+from ovirt.node.config import defaults
+from ovirt.node.plugins import ChangesHelper
 
 """
 Configure SNMP
 """
-
-from ovirt.node import plugins, valid, ui, exceptions
 
 
 class Plugin(plugins.NodePlugin):
@@ -37,13 +38,14 @@
         return 40
 
     def model(self):
-        if not self._model:
-            self._model = {
-                "snmp.enabled": "no",
-                "snmp.password": "",
-                "snmp.password_confirmation": "",
-            }
-        return self._model
+        cfg = defaults.SNMP().retrieve()
+        self.logger.debug(cfg)
+        model = {
+            "snmp.enabled": True if cfg["password"] else False,
+            "snmp.password": "",
+            "snmp.password_confirmation": "",
+        }
+        return model
 
     def validators(self):
         return {
@@ -53,15 +55,14 @@
 
     def ui_content(self):
         widgets = [
-            ("snmp._header", ui.Header("SNMP")),
+            ("header[0]", ui.Header("SNMP")),
             ("snmp.enabled", ui.Checkbox("Enable SNMP")),
-            ("ssh._divider", ui.Divider()),
+            ("divider[0]", ui.Divider()),
 
-
-            ("snmp.password._header", ui.Header("SNMP Password")),
+            ("header[1]", ui.Header("SNMP Password")),
             ("snmp.password", ui.PasswordEntry("Password:")),
-            ("snmp.password_confirmation", ui.PasswordEntry(
-                                                        "Confirm Password:")),
+            ("snmp.password_confirmation",
+             ui.PasswordEntry("Confirm Password:")),
         ]
         # Save it "locally" as a dict, for better accessability
         self._widgets = dict(widgets)
@@ -70,11 +71,39 @@
         return page
 
     def on_change(self, changes):
-        self._model.update(changes)
+        m = self.model()
+        m.update(self.pending_changes() or {})
+        effective_model = ChangesHelper(m)
 
-        if self._model["snmp.password"] != \
-           self._model["snmp.password_confirmation"]:
-            raise exceptions.InvalidData("Passwords do not match.")
+        snmp_keys = ["snmp.password",
+                     "snmp.password_confirmation"]
+        if effective_model.any_key_in_change(snmp_keys):
+            passwd, passwdc = effective_model.get_key_values(snmp_keys)
+            #if passwd != passwdc:
+            #    raise exceptions.InvalidData("Passwords do not match.")
 
     def on_merge(self, effective_changes):
-        pass
+        self.logger.debug("Saving SNMP page")
+        changes = ChangesHelper(self.pending_changes(False))
+        model = self.model()
+        model.update(effective_changes)
+        effective_model = ChangesHelper(model)
+
+        self.logger.debug("Saving SNMP page: %s" % changes.changes)
+        self.logger.debug("SNMP model: %s" % effective_model.changes)
+
+        snmp_keys = ["snmp.password", "snmp.enabled"]
+
+        txs = utils.Transaction("Updating SNMP configuration")
+
+        if changes.any_key_in_change(snmp_keys):
+            values = effective_model.get_key_values(snmp_keys)
+            args = [values[0]]
+            if values[1] is False:  # If set to disabled, set password to None
+                args[0] = None
+            model = defaults.SNMP()
+            model.update(*args)
+            txs += model.transaction()
+
+        txs.prepare()
+        self.dry_or(lambda: txs())
diff --git a/scripts/tui/src/ovirt/node/ui/__init__.py b/scripts/tui/src/ovirt/node/ui/__init__.py
index dc3f086..0bc5b86 100644
--- a/scripts/tui/src/ovirt/node/ui/__init__.py
+++ b/scripts/tui/src/ovirt/node/ui/__init__.py
@@ -94,7 +94,7 @@
 
     def __init__(self, children):
         super(Page, self).__init__(children)
-        self.buttons = [
+        self.buttons = self.buttons or [
                         (None, SaveButton()),
                         (None, ResetButton())
                         ]
@@ -112,6 +112,12 @@
     @Element.signal_change
     def close(self, v=True):
         self._close = v
+
+
+class InfoDialog(Dialog):
+    def __init__(self, title, children):
+        super(InfoDialog, self).__init__(title, children)
+        self.buttons = [(None, CloseButton())]
 
 
 class Row(ContainerElement):
@@ -196,6 +202,11 @@
         super(ResetButton, self).__init__("Reset", enabled)
 
 
+class CloseButton(Button):
+    def __init__(self, enabled=True):
+        super(CloseButton, self).__init__("Close", enabled)
+
+
 class Divider(Element):
     def __init__(self, char=u" "):
         super(Divider, self).__init__()
diff --git a/scripts/tui/src/ovirt/node/ui/builder.py b/scripts/tui/src/ovirt/node/ui/builder.py
index bca8423..4710d51 100644
--- a/scripts/tui/src/ovirt/node/ui/builder.py
+++ b/scripts/tui/src/ovirt/node/ui/builder.py
@@ -57,7 +57,8 @@
     # Add buttons
     button_widgets = []
     for path, item in container.buttons:
-        assert type(item) in [ui.SaveButton, ui.ResetButton, ui.Button]
+        assert type(item) in [ui.SaveButton, ui.ResetButton, ui.CloseButton,
+                              ui.Button]
         button_widgets.append(build_button(path, item, tui, plugin))
 
     if button_widgets:
@@ -144,7 +145,8 @@
     item.connect_signal("enabled", on_item_enabled_change_cb)
 
     def on_widget_value_change(widget, new_value):
-        LOGGER.debug("Entry changed, calling callback: '%s'" % path)
+        LOGGER.debug("Entry %s changed, calling callback: '%s'" % (widget,
+                                                                   path))
 
         try:
             change = {path: new_value}
@@ -195,11 +197,13 @@
         plugin.sig_valid.connect(lambda w, v: widget.enable(v))
 
     def on_widget_click_cb(widget, data=None):
-        LOGGER.debug("Button click: %s %s" % (path, widget))
+        LOGGER.debug("Button click: %s" % {"path": path, "widget": widget})
         if itemtype is ui.Button:
             plugin._on_ui_change({path: True})
         if itemtype in [ui.Button, ui.SaveButton]:
             r = plugin._on_ui_save()
+        if itemtype in [ui.CloseButton]:
+            r = tui.close_topmost_dialog()
         if itemtype in [ui.ResetButton]:
             r = plugin._on_ui_reset()
             tui._display_plugin(plugin)
@@ -233,6 +237,13 @@
 
 def build_checkbox(path, item, tui, plugin):
     widget = ui.widgets.Checkbox(item.label, item.state())
+
+    def on_widget_change_cb(widget, data=None):
+        item.state(data)
+        LOGGER.debug("Checkbox changed, calling callback: %s" % data)
+        plugin._on_ui_change({path: data})
+
+    urwid.connect_signal(widget, "change", on_widget_change_cb)
     return widget
 
 
diff --git a/scripts/tui/src/ovirt/node/ui/tui.py b/scripts/tui/src/ovirt/node/ui/tui.py
index 3756d5a..669ce69 100644
--- a/scripts/tui/src/ovirt/node/ui/tui.py
+++ b/scripts/tui/src/ovirt/node/ui/tui.py
@@ -128,6 +128,12 @@
         widget = ui.builder.build_page(self, self._current_plugin, dialog)
         return self.__display_as_dialog(widget, dialog.title)
 
+    def close_topmost_dialog(self):
+        dialog = [w for w in self.__widget_stack
+                  if type(w) is ovirt.node.ui.widgets.ModalDialog][-1]
+        assert len(dialog) == 1
+        self.__close_dialog(dialog)
+
     def quit(self):
         """Quit the UI
         """
@@ -192,7 +198,7 @@
                         msg += "- %s\n" % (field.strip(":"))
                 if msg:
                     self.__display_as_dialog(urwid.Filler(urwid.Text(
-                                "The following fields were changed:\n%s" %
+                                "The following fields have changed:\n%s" %
                                 msg)),
                                 "Pending changes")
                     has_outstanding_changes = True
diff --git a/scripts/tui/src/ovirt/node/ui/widgets.py b/scripts/tui/src/ovirt/node/ui/widgets.py
index 5967f8a..7564ce7 100644
--- a/scripts/tui/src/ovirt/node/ui/widgets.py
+++ b/scripts/tui/src/ovirt/node/ui/widgets.py
@@ -429,6 +429,8 @@
 
 
 class Checkbox(urwid.WidgetWrap):
+    signals = ['change']
+
     def __init__(self, label, state):
         self._label = urwid.Text(label)
         self._label_attrmap = urwid.AttrMap(self._label,
@@ -437,12 +439,19 @@
         self._divider = urwid.Divider()
         self._container = urwid.Columns([self._label_attrmap,
                                          self._checkbox])
+
+        def on_change_cb(widget, new_value):
+            urwid.emit_signal(self, 'change', self, new_value)
+        urwid.connect_signal(self._checkbox, 'change', on_change_cb)
+
         super(Checkbox, self).__init__(urwid.Pile([self._container,
                                                    self._divider]))
 
     def set_text(self, s):
         if s in [True, False]:
             self._checkbox.set_state(s)
+        else:
+            raise Exception("Invalid value: %s" % s)
 
 
 class PageWidget(urwid.WidgetWrap):
@@ -488,10 +497,21 @@
         (maxcol,) = size
         self._shift_view_to_cursor = bool(focus)
 
-        txt = self.get_edit_text().ljust(maxcol, self.char)[:maxcol]
+        txt = self.get_edit_text()
+        txt = u"".join([self._mask] * len(txt)) if self._mask else txt
+        txt = txt.ljust(maxcol, self.char)[:maxcol]
         canv = urwid.Text(txt).render((maxcol,))
         if focus:
             canv = urwid.CompositeCanvas(canv)
             canv.cursor = self.get_cursor_coords((maxcol,))
 
         return canv
+
+
+class TabablePile(urwid.Pile):
+    def keypress(self, size, key):
+        #if "tab" in key:
+        #    self.focus_position += 1
+        #elif "shift tab" in key:
+        #    self.focus_position -= 1
+        return (size, key)
diff --git a/scripts/tui/src/ovirt/node/utils/__init__.py b/scripts/tui/src/ovirt/node/utils/__init__.py
index 2562030..c42342b 100644
--- a/scripts/tui/src/ovirt/node/utils/__init__.py
+++ b/scripts/tui/src/ovirt/node/utils/__init__.py
@@ -27,11 +27,10 @@
 And use the model.py module for oVirt Node's defaults file.
 """
 
-import hashlib
+from ovirt.node import base, exceptions
 import augeas as _augeas
+import hashlib
 import system_config_keyboard.keyboard
-
-from ovirt.node import base
 
 
 class AugeasWrapper(base.Base):
@@ -216,7 +215,7 @@
     >>> tx()
     Traceback (most recent call last):
         ...
-    RuntimeError: Transaction failed: Step C
+    TransactionError: 'Transaction failed: Step C'
     """
     def __init__(self, title, elements=[]):
         super(Transaction, self).__init__()
@@ -229,7 +228,8 @@
         for element in self:
             self.logger.debug("Preparing element '%s'" % element)
             if Transaction.Element not in element.__class__.mro():
-                raise Exception("%s is no Transaction.Element" % element)
+                raise exceptions.PreconditionError(("%s is no Transaction." +
+                                                     "Element") % element)
             self._prepared_elements.append(element)
             element.prepare()
         return True
@@ -255,7 +255,8 @@
         except Exception as e:
             self.logger.warning("Transaction failed: %s" % e.message)
             self.abort()
-            raise RuntimeError("Transaction failed: %s" % e.message)
+            raise exceptions.TransactionError("Transaction failed: " +
+                                               "%s" % e.message)
         self.logger.info("Transaction '%s' succeeded" % self)
         return True
 
diff --git a/scripts/tui/src/ovirt/node/utils/security.py b/scripts/tui/src/ovirt/node/utils/security.py
index e236ca7..7d2f200 100644
--- a/scripts/tui/src/ovirt/node/utils/security.py
+++ b/scripts/tui/src/ovirt/node/utils/security.py
@@ -50,15 +50,14 @@
 class Ssh(base.Base):
     def __init__(self):
         super(Ssh, self).__init__()
-        import ovirtnode.ovirtfunctions as ofunc
-        self.ofunc = ofunc
 
-    def __update_profile(self, rng_num_bytes, enable_aes):
+    def __update_profile(self, rng_num_bytes, disable_aes):
+        import ovirtnode.ovirtfunctions as ofunc
         additional_lines = []
-        self.ofunc.unmount_config("/etc/profile")
+        ofunc.unmount_config("/etc/profile")
 
         process.system("sed -i '/OPENSSL_DISABLE_AES_NI/d' /etc/profile")
-        if not enable_aes:
+        if disable_aes:
             additional_lines += ["export OPENSSL_DISABLE_AES_NI=1"]
 
         process.system("sed -i '/SSH_USE_STRONG_RNG/d' /etc/profile")
@@ -71,27 +70,29 @@
             with open("/etc/profile", "a") as f:
                 lines = "\n" + "\n".join(additional_lines)
                 f.write(lines)
-            self.ofunc.ovirt_store_config("/etc/profile")
+            ofunc.ovirt_store_config("/etc/profile")
 
             self.restart()
 
-    def aes_ni(self, enable=None):
+    def disable_aesni(self, disable=None):
         """Set/Get AES NI for OpenSSL
         Args:
             enable: True or False
         Returns:
             The status of aes_ni
         """
-        rng, aes = self.ofunc.rng_status()
-        if enable in [True, False]:
-            self.__update_profile(rng, enable)
+        import ovirtnode.ovirtfunctions as ofunc
+        rng, aes = ofunc.rng_status()
+        if disable in [True, False]:
+            self.__update_profile(rng, disable)
         else:
-            self.logger.warning("Unknown value for AES NI: %s" % enable)
-        return self.ofunc.rng_status()[1]  # FIXME should rurn bool
+            self.logger.warning("Unknown value for AES NI: %s" % disable)
+        return ofunc.rng_status()[1]  # FIXME should rurn bool
         # and does it return disable_aes_ni?
 
     def strong_rng(self, num_bytes=None):
-        rng, aes = self.ofunc.rng_status()
+        import ovirtnode.ovirtfunctions as ofunc
+        rng, aes = ofunc.rng_status()
         if valid.Number(range=[0, None]).validate(num_bytes):
             self.__update_profile(num_bytes, aes)
         elif num_bytes is None:
@@ -99,7 +100,7 @@
         else:
             self.logger.warning("Unknown value for RNG num bytes: " +
                                 "%s" % num_bytes)
-        return self.ofunc.rng_status()[0]
+        return ofunc.rng_status()[0]
 
     def restart(self):
         self.logger.debug("Restarting SSH")


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

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