Revision 45f62156

b/lib/config.py
34 34
# pylint: disable=R0904
35 35
# R0904: Too many public methods
36 36

  
37
import copy
37 38
import os
38 39
import random
39 40
import logging
......
2033 2034
             (data.cluster.master_node, self._my_hostname))
2034 2035
      raise errors.ConfigurationError(msg)
2035 2036

  
2036
    # Upgrade configuration if needed
2037
    data.UpgradeConfig()
2038

  
2039 2037
    self._config_data = data
2040 2038
    # reset the last serial as -1 so that the next write will cause
2041 2039
    # ssconf update
2042 2040
    self._last_cluster_serial = -1
2043 2041

  
2044
    # And finally run our (custom) config upgrade sequence
2042
    # Upgrade configuration if needed
2045 2043
    self._UpgradeConfig()
2046 2044

  
2047 2045
    self._cfg_id = utils.GetFileID(path=self._cfg_file)
2048 2046

  
2049 2047
  def _UpgradeConfig(self):
2050
    """Run upgrade steps that cannot be done purely in the objects.
2048
    """Run any upgrade steps.
2051 2049

  
2052
    This is because some data elements need uniqueness across the
2053
    whole configuration, etc.
2050
    This method performs both in-object upgrades and also update some data
2051
    elements that need uniqueness across the whole configuration or interact
2052
    with other objects.
2054 2053

  
2055 2054
    @warning: this function will call L{_WriteConfig()}, but also
2056 2055
        L{DropECReservations} so it needs to be called only from a
......
2059 2058
        created first, to avoid causing deadlock.
2060 2059

  
2061 2060
    """
2062
    modified = False
2061
    # Keep a copy of the persistent part of _config_data to check for changes
2062
    # Serialization doesn't guarantee order in dictionaries
2063
    oldconf = copy.deepcopy(self._config_data.ToDict())
2064

  
2065
    # In-object upgrades
2066
    self._config_data.UpgradeConfig()
2067

  
2063 2068
    for item in self._AllUUIDObjects():
2064 2069
      if item.uuid is None:
2065 2070
        item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2066
        modified = True
2067 2071
    if not self._config_data.nodegroups:
2068 2072
      default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2069 2073
      default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2070 2074
                                            members=[])
2071 2075
      self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2072
      modified = True
2073 2076
    for node in self._config_data.nodes.values():
2074 2077
      if not node.group:
2075 2078
        node.group = self.LookupNodeGroup(None)
2076
        modified = True
2077 2079
      # This is technically *not* an upgrade, but needs to be done both when
2078 2080
      # nodegroups are being added, and upon normally loading the config,
2079 2081
      # because the members list of a node group is discarded upon
2080 2082
      # serializing/deserializing the object.
2081 2083
      self._UnlockedAddNodeToGroup(node.name, node.group)
2084

  
2085
    modified = (oldconf != self._config_data.ToDict())
2082 2086
    if modified:
2083 2087
      self._WriteConfig()
2084 2088
      # This is ok even if it acquires the internal lock, as _UpgradeConfig is
b/test/py/ganeti.config_unittest.py
174 174
    self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance,
175 175
                          None)
176 176

  
177
  def testUpgradeSave(self):
178
    """Test that any modification done during upgrading is saved back"""
179
    cfg = self._get_object()
180

  
181
    # Remove an element, run upgrade, and check if the element is
182
    # back and the file upgraded
183
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
184
    # For a ConfigObject, None is the same as a missing field
185
    node.ndparams = None
186
    oldsaved = utils.ReadFile(self.cfg_file)
187
    cfg._UpgradeConfig()
188
    self.assertTrue(node.ndparams is not None)
189
    newsaved = utils.ReadFile(self.cfg_file)
190
    # We rely on the fact that at least the serial number changes
191
    self.assertNotEqual(oldsaved, newsaved)
192

  
193
    # Add something that should not be there this time
194
    key = list(constants.NDC_GLOBALS)[0]
195
    node.ndparams[key] = constants.NDC_DEFAULTS[key]
196
    cfg._WriteConfig(None)
197
    oldsaved = utils.ReadFile(self.cfg_file)
198
    cfg._UpgradeConfig()
199
    self.assertTrue(node.ndparams.get(key) is None)
200
    newsaved = utils.ReadFile(self.cfg_file)
201
    self.assertNotEqual(oldsaved, newsaved)
202

  
203
    # Do the upgrade again, this time there should be no update
204
    oldsaved = newsaved
205
    cfg._UpgradeConfig()
206
    newsaved = utils.ReadFile(self.cfg_file)
207
    self.assertEqual(oldsaved, newsaved)
208

  
209
    # Reload the configuration again: it shouldn't change the file
210
    oldsaved = newsaved
211
    self._get_object()
212
    newsaved = utils.ReadFile(self.cfg_file)
213
    self.assertEqual(oldsaved, newsaved)
214

  
177 215
  def testNICParameterSyntaxCheck(self):
178 216
    """Test the NIC's CheckParameterSyntax function"""
179 217
    mode = constants.NIC_MODE

Also available in: Unified diff