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 <[email protected]> --- 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 <[email protected]> _______________________________________________ node-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/node-patches
