diff --git a/lib/config.py b/lib/config.py index fcfac9ecc870337213db26bac6714c1e6d2c2a1d..c5fa6bc734821bf5ea7442dad7bfd563a8278f89 100644 --- a/lib/config.py +++ b/lib/config.py @@ -297,8 +297,9 @@ class ConfigWriter: if not isinstance(instance, objects.Instance): raise errors.ProgrammerError("Invalid type passed to AddInstance") - all_lvs = instance.MapLVsByNode() - logger.Info("Instance '%s' DISK_LAYOUT: %s" % (instance.name, all_lvs)) + if instance.disk_template != constants.DT_DISKLESS: + all_lvs = instance.MapLVsByNode() + logger.Info("Instance '%s' DISK_LAYOUT: %s" % (instance.name, all_lvs)) self._OpenConfig() self._config_data.instances[instance.name] = instance @@ -624,3 +625,30 @@ class ConfigWriter: self._ReleaseLock() return self._config_data.cluster + + def Update(self, target): + """Notify function to be called after updates. + + This function must be called when an object (as returned by + GetInstanceInfo, GetNodeInfo, GetCluster) has been updated and the + caller wants the modifications saved to the backing store. Note + that all modified objects will be saved, but the target argument + is the one the caller wants to ensure that it's saved. + + """ + if self._config_data is None: + raise errors.ProgrammerError, ("Configuration file not read," + " cannot save.") + if isinstance(target, objects.Cluster): + test = target == self._config_data.cluster + elif isinstance(target, objects.Node): + test = target in self._config_data.nodes.values() + elif isinstance(target, objects.Instance): + test = target in self._config_data.instances.values() + else: + raise errors.ProgrammerError, ("Invalid object type (%s) passed to" + " ConfigWriter.Update" % type(target)) + if not test: + raise errors.ConfigurationError, ("Configuration updated since object" + " has been read or unknown object") + self._WriteConfig() diff --git a/testing/ganeti.config_unittest.py b/testing/ganeti.config_unittest.py new file mode 100755 index 0000000000000000000000000000000000000000..f2bc1e880005bf6f902b98401a1a4553cb884eb8 --- /dev/null +++ b/testing/ganeti.config_unittest.py @@ -0,0 +1,132 @@ +#!/usr/bin/python +# + +# Copyright (C) 2006, 2007 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for unittesting the config module""" + + +import unittest +import os +import time +import tempfile +import os.path +import socket + +from ganeti import errors +from ganeti import constants +from ganeti import config +from ganeti import objects + + +class TestConfigRunner(unittest.TestCase): + """Testing case for HooksRunner""" + def setUp(self): + fd, self.cfg_file = tempfile.mkstemp() + os.close(fd) + + def tearDown(self): + try: + os.unlink(self.cfg_file) + except OSError: + pass + + def _get_object(self): + """Returns a instance of ConfigWriter""" + cfg = config.ConfigWriter(cfg_file=self.cfg_file, offline=True) + return cfg + + def _init_cluster(self, cfg): + """Initializes the cfg object""" + cfg.InitConfig(socket.gethostname(), '127.0.0.1', None, '', 'aa:00:00', + 'xenvg', constants.DEFAULT_BRIDGE) + + def _create_instance(self): + """Create and return an instance object""" + inst = objects.Instance(name="test.example.com", disks=[], + disk_template=constants.DT_DISKLESS) + return inst + + def testEmpty(self): + """Test instantiate config object""" + self._get_object() + + def testInit(self): + """Test initialize the config file""" + cfg = self._get_object() + self._init_cluster(cfg) + self.failUnlessEqual(1, len(cfg.GetNodeList())) + self.failUnlessEqual(0, len(cfg.GetInstanceList())) + + def testUpdateCluster(self): + """Test updates on the cluster object""" + cfg = self._get_object() + # construct a fake cluster object + fake_cl = objects.Cluster() + # fail if we didn't read the config + self.failUnlessRaises(errors.ProgrammerError, cfg.Update, fake_cl) + + self._init_cluster(cfg) + cl = cfg.GetClusterInfo() + # first pass, must not fail + cfg.Update(cl) + # second pass, also must not fail (after the config has been written) + cfg.Update(cl) + # but the fake_cl update should still fail + self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl) + + def testUpdateNode(self): + """Test updates on one node object""" + cfg = self._get_object() + # construct a fake node + fake_node = objects.Node() + # fail if we didn't read the config + self.failUnlessRaises(errors.ProgrammerError, cfg.Update, fake_node) + + self._init_cluster(cfg) + node = cfg.GetNodeInfo(cfg.GetNodeList()[0]) + # first pass, must not fail + cfg.Update(node) + # second pass, also must not fail (after the config has been written) + cfg.Update(node) + # but the fake_node update should still fail + self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_node) + + def testUpdateInstance(self): + """Test updates on one instance object""" + cfg = self._get_object() + # construct a fake instance + inst = self._create_instance() + fake_instance = objects.Instance() + # fail if we didn't read the config + self.failUnlessRaises(errors.ProgrammerError, cfg.Update, fake_instance) + + self._init_cluster(cfg) + cfg.AddInstance(inst) + instance = cfg.GetInstanceInfo(cfg.GetInstanceList()[0]) + # first pass, must not fail + cfg.Update(instance) + # second pass, also must not fail (after the config has been written) + cfg.Update(instance) + # but the fake_instance update should still fail + self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance) + + +if __name__ == '__main__': + unittest.main()