[node-patches] Change in ovirt-node[master]: utils: Add and integrate transaction mechanism

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


Fabian Deutsch has uploaded a new change for review.

Change subject: utils: Add and integrate transaction mechanism
......................................................................

utils: Add and integrate transaction mechanism

Change-Id: Ifda1b1ab62e717f5602e6be5e733ec5768772f77
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/src/ovirt/node/config/defaults.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/network_page.py
M scripts/tui/src/ovirt/node/utils/__init__.py
M scripts/tui/src/ovirt/node/utils/network.py
6 files changed, 187 insertions(+), 46 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/42/9942/1

diff --git a/scripts/tui/src/ovirt/node/config/defaults.py b/scripts/tui/src/ovirt/node/config/defaults.py
index 2121cb5..6bf73a2 100644
--- a/scripts/tui/src/ovirt/node/config/defaults.py
+++ b/scripts/tui/src/ovirt/node/config/defaults.py
@@ -169,11 +169,25 @@
         """
         raise NotImplementedError
 
-    def apply(self, *args, **kwargs):
-        """This method updates the to this subclass specific configuration
-        files according to the config keys set with configure.
+    def transaction(self):
+        """This method returns a transaction which needs to be performed
+        to activate the defaults config (so e.g. update cfg files and restart
+        services).
+
+        This can be used to update the UI when the transaction has many steps
         """
         raise NotImplementedError
+
+    def commit(self, *args, **kwargs):
+        """This method updates the to this subclass specific configuration
+        files according to the config keys set with configure.
+
+        A shortcut for:
+        tx = obj.ransaction()
+        tx()
+        """
+        tx = self.transaction()
+        tx()
 
     def retrieve(self):
         """Returns the config keys of the current component
@@ -305,7 +319,7 @@
                 "servers": cfg["servers"].split(",")
                 }
 
-    def apply(self):
+    def transaction(self):
         """Derives the nameserver config from OVIRT_DNS
 
         1. Parse nameservers from defaults
@@ -316,6 +330,7 @@
         Args:
             servers: List of servers (str)
         """
+        aug = utils.AugeasWrapper()
         ovirt_config = self.defaults.get_dict()
         if "OVIRT_DNS" not in ovirt_config:
             self.logger.debug("No DNS server entry in default config")
@@ -323,31 +338,40 @@
 
         servers = ovirt_config["OVIRT_DNS"]
         if servers is None or servers == "":
-            self.logger.debug("No DNS servers configured in default config")
-
+            self.logger.debug("No DNS servers configured " +
+                              "in default config")
         servers = servers.split(",")
 
-        aug = utils.AugeasWrapper()
-        # Write resolv.conf any way, sometimes without servers
-        comment = ("Please make changes through the TUI. " + \
-                   "Manual edits to this file will be " + \
-                   "lost on reboot")
-        aug.set("/files/etc/resolv.conf/#comment[1]", comment)
+        class UpdateResolvConf(utils.Transaction.Element):
+            title = "Updateing resolv.conf"
 
-        # Now set the nameservers
-        ovirt.node.config.network.nameservers(servers)
+            def commit(self):
+                # Write resolv.conf any way, sometimes without servers
+                comment = ("Please make changes through the TUI. " + \
+                           "Manual edits to this file will be " + \
+                           "lost on reboot")
+                aug.set("/files/etc/resolv.conf/#comment[1]", comment)
+                # Now set the nameservers
+                ovirt.node.config.network.nameservers(servers)
+                utils.fs.persist_config("/etc/resolv.conf")
 
-        # Set or remove PEERDNS for all ifcfg-*
-        for nic in glob.glob("/etc/sysconfig/network-scripts/ifcfg-*"):
-            if "ifcfg-lo" in nic:
-                continue
-            path = "/files%s/PEERDNS" % nic
-            if len(servers) > 0:
-                aug.set(path, "no")
-            else:
-                aug.remove(path)
+        class UpdatePeerDNS(utils.Transaction.Element):
+            title = "Update PEERDNS statement in ifcfg-* files"
 
-        utils.fs.persist_config("/etc/resolv.conf")
+            def commit(self):
+                # Set or remove PEERDNS for all ifcfg-*
+                for nic in glob.glob("/etc/sysconfig/network-scripts/ifcfg-*"):
+                    if "ifcfg-lo" in nic:
+                        continue
+                    path = "/files%s/PEERDNS" % nic
+                    if len(servers) > 0:
+                        aug.set(path, "no")
+                    else:
+                        aug.remove(path)
+
+        return utils.Transaction("Configuring DNS", [
+                               UpdateResolvConf(),
+                               UpdatePeerDNS()])
 
 
 class Timeservers(NodeConfigFileSection):
@@ -379,6 +403,9 @@
                 "servers": cfg["servers"].split(",")
                 }
 
+    def transaction(self):
+        return utils.Transaction("Configuring timeserver")
+
 
 class Syslog(NodeConfigFileSection):
     """Configure rsyslog
diff --git a/scripts/tui/src/ovirt/node/plugins.py b/scripts/tui/src/ovirt/node/plugins.py
index a1d01f2..66e949e 100644
--- a/scripts/tui/src/ovirt/node/plugins.py
+++ b/scripts/tui/src/ovirt/node/plugins.py
@@ -253,7 +253,6 @@
         self.logger.debug("Request to discard model changes: %s" % changes)
         self.__changes = {}
 
-
     def pending_changes(self, only_effective_changes=True):
         """Return all changes which happened since the last on_merge call
 
@@ -365,4 +364,4 @@
             """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
+            map(lambda w: w.enabled(is_enable), self.widgethelper.subset(self))
diff --git a/scripts/tui/src/ovirt/node/setup/keyboard_page.py b/scripts/tui/src/ovirt/node/setup/keyboard_page.py
index df6ffcf..66099cd 100644
--- a/scripts/tui/src/ovirt/node/setup/keyboard_page.py
+++ b/scripts/tui/src/ovirt/node/setup/keyboard_page.py
@@ -25,6 +25,7 @@
 
 from ovirt.node import plugins, ui, utils
 
+
 class Plugin(plugins.NodePlugin):
     _model = None
     _widgets = None
diff --git a/scripts/tui/src/ovirt/node/setup/network_page.py b/scripts/tui/src/ovirt/node/setup/network_page.py
index 3e44f89..19ebae5 100644
--- a/scripts/tui/src/ovirt/node/setup/network_page.py
+++ b/scripts/tui/src/ovirt/node/setup/network_page.py
@@ -22,13 +22,10 @@
 Network page plugin
 """
 
-import time
-
-from ovirt.node import ui
+from ovirt.node import plugins, ui, valid, utils
 from ovirt.node.config import defaults
-from ovirt.node import plugins
 import ovirt.node.utils.network
-from ovirt.node import valid
+import time
 
 
 class Plugin(ovirt.node.plugins.NodePlugin):
@@ -262,31 +259,45 @@
             ("dialog.dia.text[0]", progress),
             ]))
 
+        # This object will contain all transaction elements to be executed
+        txs = utils.Transaction("DNS and NTP configuration")
+
+        e_changes_h = plugins.ChangesHelper(effective_changes)
+        e_model_h = plugins.ChangesHelper(effective_model)
+
         nameservers = []
-        for key in ["dns[0]", "dns[1]"]:
-            if key in effective_changes:
-                nameservers.append(effective_model[key])
+        dns_keys = ["dns[0]", "dns[1]"]
+        if e_changes_h.any_key_in_change(dns_keys):
+            nameservers += e_model_h.get_key_values(dns_keys)
         if nameservers:
-            set_progress("Applying DNS changes.")
             self.logger.info("Setting new nameservers: %s" % nameservers)
             model = defaults.Nameservers()
             model.update(nameservers)
+            txs += model.transaction()
 
         timeservers = []
-        for key in ["ntp[0]", "ntp[1]"]:
-            if key in effective_changes:
-                timeservers.append(effective_model[key])
+        ntp_keys = ["ntp[0]", "ntp[1]"]
+        if e_changes_h.any_key_in_change(ntp_keys):
+            timeservers += e_model_h.get_key_values(ntp_keys)
         if timeservers:
-            set_progress("Applying NTP changes.")
             self.logger.info("Setting new timeservers: %s" % timeservers)
             model = defaults.Timeservers()
             model.update(timeservers)
+            txs += model.transaction()
 
-        change_helper = plugins.ChangesHelper(effective_changes)
-        if change_helper.any_key_in_change(self._nic_details_group):
+        # For the NIC details dialog:
+        if e_changes_h.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))
+            # Fetch the values for the nic keys, they are used as arguments
+            args = helper.get_key_values(self._nic_details_group)
+            txs += self._configure_nic(*args)
+
+        # Commit all outstanding transactions
+        txs.prepare()
+        for e in txs:
+            set_progress(e.title)
+            #e.commit()
 
         set_progress("All changes were applied.")
         time.sleep(3)
@@ -306,3 +317,5 @@
             model.update(iface, "none", ipaddr, netmask, gateway, vlanid)
         else:
             self.logger.debug("No interface configuration found")
+        # Return the resulting transaction
+        return model.transaction()
diff --git a/scripts/tui/src/ovirt/node/utils/__init__.py b/scripts/tui/src/ovirt/node/utils/__init__.py
index 5c7304f..9ce9c8d 100644
--- a/scripts/tui/src/ovirt/node/utils/__init__.py
+++ b/scripts/tui/src/ovirt/node/utils/__init__.py
@@ -166,4 +166,108 @@
         layoutgen = ((details[0], kid)
                      for kid, details in kbd.modelDict.items())
         layouts = [(kid, name) for name, kid in sorted(layoutgen)]
-        return layouts
\ No newline at end of file
+        return layouts
+
+
+class Transaction(list, base.Base):
+    """A very simple transaction mechanism.
+
+    >>> class StepA(Transaction.Element):
+    ...     def commit(self):
+    ...         print "Step A"
+    ...         return "Stepped A"
+
+    >>> class StepB(Transaction.Element):
+    ...     def commit(self):
+    ...         print "Step B"
+    ...         return "Stepped B"
+
+    >>> class StepC(Transaction.Element):
+    ...     def commit(self):
+    ...         raise Exception("Step C")
+
+    >>> tx = Transaction("Steps", [StepA(), StepB()])
+    >>> tx()
+    Step A
+    Step B
+    True
+
+    >>> len(tx)
+    2
+
+    >>> tx.prepare()
+    True
+    >>> for e in tx:
+    ...     e.commit()
+    Step A
+    'Stepped A'
+    Step B
+    'Stepped B'
+
+>>> tx = Transaction("Steps", [StepA(), StepB(), StepC()])
+    >>> tx()
+    Traceback (most recent call last):
+        ...
+    RuntimeError: Transaction failed: Step C
+    """
+    def __init__(self, title, elements=[]):
+        super(Transaction, self).__init__()
+        base.Base.__init__(self)
+        self.title = title
+        self._prepared_elements = []
+        self.extend(elements)
+
+    def prepare(self):
+        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)
+            self._prepared_elements.append(element)
+            element.prepare()
+        return True
+
+    def commit(self):
+        for element in self:
+            self.logger.debug("Committing element '%s'" % element)
+            element.commit()
+        return True
+
+    def abort(self):
+        for element in self._prepared_elements:
+            self.logger.debug("Aborting element '%s'" % element)
+            element.abort()
+        self._prepared_elements = []
+        return True
+
+    def __call__(self):
+        self.logger.debug("Running transaction '%s'" % self)
+        try:
+            self.prepare()
+            self.commit()
+        except Exception as e:
+            self.logger.warning("Transaction failed: %s" % e.message)
+            self.abort()
+            raise RuntimeError("Transaction failed: %s" % e.message)
+        self.logger.info("Transaction '%s' succeeded" % self)
+        return True
+
+    class Element(base.Base):
+        title = None
+
+        def __repr__(self):
+            return "<%s '%s'>" % (self.__class__.__name__, self.title)
+
+        def prepare(self):
+            """Is expected to be short running and not changing anything
+            """
+            pass
+
+        def commit(self):
+            """Is expected to run and change stuff
+            """
+            pass
+
+        def abort(self):
+            """Is run in case that one commit of a transaction fails.
+            """
+            pass
diff --git a/scripts/tui/src/ovirt/node/utils/network.py b/scripts/tui/src/ovirt/node/utils/network.py
index 17c0ef3..72bc868 100644
--- a/scripts/tui/src/ovirt/node/utils/network.py
+++ b/scripts/tui/src/ovirt/node/utils/network.py
@@ -273,9 +273,6 @@
 def node_bridge():
     """Returns the main bridge of this node
 
-    >>> node_bridge() is not None
-    True
-
     Returns:
         Bridge of this node
     """


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

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