From f78ede4ef6f6e93f3541361518c0d2d73643e25c Mon Sep 17 00:00:00 2001 From: Guido Trotter <ultrotter@google.com> Date: Tue, 8 Jul 2008 08:40:51 +0000 Subject: [PATCH] ConfigWriter: synchronize access Since we share the ConfigWriter we need somehow to make sure that accessing it is properly synchronized. We'll do it using the locking.ssynchronized decorator and a module-private shared lock. This patch also renames a few functions, which were called inside the ConfigWriter, to a private version _UnlockedFunctionName, and exports the synchronized public ones. The internal callers, which are already synchronized, are then changed to use the _Unlocked version, to prevent double locking. Reviewed-by: iustinp --- lib/config.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/lib/config.py b/lib/config.py index d83839493..f823a004c 100644 --- a/lib/config.py +++ b/lib/config.py @@ -37,6 +37,7 @@ import random import re from ganeti import errors +from ganeti import locking from ganeti import logger from ganeti import utils from ganeti import constants @@ -46,6 +47,9 @@ from ganeti import serializer from ganeti import ssconf +_config_lock = locking.SharedLock() + + def ValidateConfig(): sstore = ssconf.SimpleStore() @@ -62,6 +66,7 @@ class ConfigWriter: """ def __init__(self, cfg_file=None, offline=False): self.write_count = 0 + self._lock = _config_lock self._config_data = None self._config_time = None self._config_size = None @@ -86,6 +91,7 @@ class ConfigWriter: """ return os.path.exists(constants.CLUSTER_CONF_FILE) + @locking.ssynchronized(_config_lock, shared=1) def GenerateMAC(self): """Generate a MAC for an instance. @@ -108,6 +114,7 @@ class ConfigWriter: raise errors.ConfigurationError("Can't generate unique MAC") return mac + @locking.ssynchronized(_config_lock, shared=1) def IsMacInUse(self, mac): """Predicate: check if the specified MAC is in use in the Ganeti cluster. @@ -131,6 +138,7 @@ class ConfigWriter: lvnames.update(lv_list) return lvnames + @locking.ssynchronized(_config_lock, shared=1) def GenerateUniqueID(self, exceptions=None): """Generate an unique disk name. @@ -177,6 +185,7 @@ class ConfigWriter: return result + @locking.ssynchronized(_config_lock, shared=1) def VerifyConfig(self): """Stub verify function. """ @@ -202,7 +211,7 @@ class ConfigWriter: seen_macs.append(nic.mac) return result - def SetDiskID(self, disk, node_name): + def _UnlockedSetDiskID(self, disk, node_name): """Convert the unique ID to the ID needed on the target nodes. This is used only for drbd, which needs ip/port configuration. @@ -211,10 +220,12 @@ class ConfigWriter: this helps when the only the top device is passed to the remote node. + This function is for internal use, when the config lock is already held. + """ if disk.children: for child in disk.children: - self.SetDiskID(child, node_name) + self._UnlockedSetDiskID(child, node_name) if disk.logical_id is None and disk.physical_id is not None: return @@ -223,8 +234,8 @@ class ConfigWriter: if node_name not in (pnode, snode): raise errors.ConfigurationError("DRBD device not knowing node %s" % node_name) - pnode_info = self.GetNodeInfo(pnode) - snode_info = self.GetNodeInfo(snode) + pnode_info = self._UnlockedGetNodeInfo(pnode) + snode_info = self._UnlockedGetNodeInfo(snode) if pnode_info is None or snode_info is None: raise errors.ConfigurationError("Can't find primary or secondary node" " for %s" % str(disk)) @@ -238,6 +249,20 @@ class ConfigWriter: disk.physical_id = disk.logical_id return + @locking.ssynchronized(_config_lock) + def SetDiskID(self, disk, node_name): + """Convert the unique ID to the ID needed on the target nodes. + + This is used only for drbd, which needs ip/port configuration. + + The routine descends down and updates its children also, because + this helps when the only the top device is passed to the remote + node. + + """ + return self._UnlockedSetDiskID(disk, node_name) + + @locking.ssynchronized(_config_lock) def AddTcpUdpPort(self, port): """Adds a new port to the available port pool. @@ -249,6 +274,7 @@ class ConfigWriter: self._config_data.cluster.tcpudp_port_pool.add(port) self._WriteConfig() + @locking.ssynchronized(_config_lock, shared=1) def GetPortList(self): """Returns a copy of the current port list. @@ -256,6 +282,7 @@ class ConfigWriter: self._OpenConfig() return self._config_data.cluster.tcpudp_port_pool.copy() + @locking.ssynchronized(_config_lock) def AllocatePort(self): """Allocate a port. @@ -280,6 +307,7 @@ class ConfigWriter: self._WriteConfig() return port + @locking.ssynchronized(_config_lock, shared=1) def GetHostKey(self): """Return the rsa hostkey from the config. @@ -290,6 +318,7 @@ class ConfigWriter: self._OpenConfig() return self._config_data.cluster.rsahostkeypub + @locking.ssynchronized(_config_lock) def AddInstance(self, instance): """Add an instance to the config. @@ -327,12 +356,14 @@ class ConfigWriter: instance.status = status self._WriteConfig() + @locking.ssynchronized(_config_lock) def MarkInstanceUp(self, instance_name): """Mark the instance status to up in the config. """ self._SetInstanceStatus(instance_name, "up") + @locking.ssynchronized(_config_lock) def RemoveInstance(self, instance_name): """Remove the instance from the configuration. @@ -344,6 +375,7 @@ class ConfigWriter: del self._config_data.instances[instance_name] self._WriteConfig() + @locking.ssynchronized(_config_lock) def RenameInstance(self, old_name, new_name): """Rename an instance. @@ -371,12 +403,14 @@ class ConfigWriter: self._config_data.instances[inst.name] = inst self._WriteConfig() + @locking.ssynchronized(_config_lock) def MarkInstanceDown(self, instance_name): """Mark the status of an instance to down in the configuration. """ self._SetInstanceStatus(instance_name, "down") + @locking.ssynchronized(_config_lock, shared=1) def GetInstanceList(self): """Get the list of instances. @@ -389,6 +423,7 @@ class ConfigWriter: return self._config_data.instances.keys() + @locking.ssynchronized(_config_lock, shared=1) def ExpandInstanceName(self, short_name): """Attempt to expand an incomplete instance name. @@ -398,6 +433,7 @@ class ConfigWriter: return utils.MatchNameComponent(short_name, self._config_data.instances.keys()) + @locking.ssynchronized(_config_lock, shared=1) def GetInstanceInfo(self, instance_name): """Returns informations about an instance. @@ -418,6 +454,7 @@ class ConfigWriter: return self._config_data.instances[instance_name] + @locking.ssynchronized(_config_lock) def AddNode(self, node): """Add a node to the configuration. @@ -429,6 +466,7 @@ class ConfigWriter: self._config_data.nodes[node.name] = node self._WriteConfig() + @locking.ssynchronized(_config_lock) def RemoveNode(self, node_name): """Remove a node from the configuration. @@ -440,6 +478,7 @@ class ConfigWriter: del self._config_data.nodes[node_name] self._WriteConfig() + @locking.ssynchronized(_config_lock, shared=1) def ExpandNodeName(self, short_name): """Attempt to expand an incomplete instance name. @@ -449,9 +488,11 @@ class ConfigWriter: return utils.MatchNameComponent(short_name, self._config_data.nodes.keys()) - def GetNodeInfo(self, node_name): + def _UnlockedGetNodeInfo(self, node_name): """Get the configuration of a node, as stored in the config. + This function is for internal use, when the config lock is already held. + Args: node: nodename (tuple) of the node Returns: the node object @@ -464,13 +505,36 @@ class ConfigWriter: return self._config_data.nodes[node_name] - def GetNodeList(self): + + @locking.ssynchronized(_config_lock, shared=1) + def GetNodeInfo(self, node_name): + """Get the configuration of a node, as stored in the config. + + Args: node: nodename (tuple) of the node + + Returns: the node object + + """ + return self._UnlockedGetNodeInfo(node_name) + + def _UnlockedGetNodeList(self): """Return the list of nodes which are in the configuration. + This function is for internal use, when the config lock is already held. + """ self._OpenConfig() return self._config_data.nodes.keys() + + @locking.ssynchronized(_config_lock, shared=1) + def GetNodeList(self): + """Return the list of nodes which are in the configuration. + + """ + return self._UnlockedGetNodeList() + + @locking.ssynchronized(_config_lock, shared=1) def DumpConfig(self): """Return the entire configuration of the cluster. """ @@ -533,7 +597,7 @@ class ConfigWriter: if self._offline: return True bad = False - nodelist = self.GetNodeList() + nodelist = self._UnlockedGetNodeList() myhostname = self._my_hostname try: @@ -579,6 +643,7 @@ class ConfigWriter: # and redistribute the config file self._DistributeConfig() + @locking.ssynchronized(_config_lock) def InitConfig(self, node, primary_ip, secondary_ip, hostkeypub, mac_prefix, vg_name, def_bridge): """Create the initial cluster configuration. @@ -611,6 +676,7 @@ class ConfigWriter: cluster=globalconfig) self._WriteConfig() + @locking.ssynchronized(_config_lock, shared=1) def GetVGName(self): """Return the volume group name. @@ -618,6 +684,7 @@ class ConfigWriter: self._OpenConfig() return self._config_data.cluster.volume_group_name + @locking.ssynchronized(_config_lock) def SetVGName(self, vg_name): """Set the volume group name. @@ -626,6 +693,7 @@ class ConfigWriter: self._config_data.cluster.volume_group_name = vg_name self._WriteConfig() + @locking.ssynchronized(_config_lock, shared=1) def GetDefBridge(self): """Return the default bridge. @@ -633,6 +701,7 @@ class ConfigWriter: self._OpenConfig() return self._config_data.cluster.default_bridge + @locking.ssynchronized(_config_lock, shared=1) def GetMACPrefix(self): """Return the mac prefix. @@ -640,6 +709,7 @@ class ConfigWriter: self._OpenConfig() return self._config_data.cluster.mac_prefix + @locking.ssynchronized(_config_lock, shared=1) def GetClusterInfo(self): """Returns informations about the cluster @@ -651,6 +721,7 @@ class ConfigWriter: return self._config_data.cluster + @locking.ssynchronized(_config_lock) def Update(self, target): """Notify function to be called after updates. -- GitLab