# Test cases for Cobbler # # Michael DeHaan TRY_GRAPH = False HAS_GRAPH = False if TRY_GRAPH: try: import pycallgraph_mod as pycallgraph HAS_GRAPH = True except: pass import sys import unittest import os import subprocess import tempfile import shutil #sys.path.append('../cobbler') #sys.path.append('../cobbler/modules') #sys.path.append('./cobbler') #sys.path.append('./cobbler/modules') from cobbler import settings from cobbler import collection_distros from cobbler import collection_profiles from cobbler import collection_systems settings.TESTMODE = True collection_distros.TESTMODE = True collection_profiles.TESTMODE = True collection_systems.TESTMODE = True from cobbler import api from cobbler import config from cobbler import utils from cobbler.cexceptions import CobblerException FAKE_INITRD="initrd-2.6.15-1.2054_FAKE.img" FAKE_INITRD2="initrd-2.5.16-2.2055_FAKE.img" FAKE_INITRD3="initrd-1.8.18-3.9999_FAKE.img" FAKE_KERNEL="vmlinuz-2.6.15-1.2054_FAKE" FAKE_KERNEL2="vmlinuz-2.5.16-2.2055_FAKE" FAKE_KERNEL3="vmlinuz-1.8.18-3.9999_FAKE" FAKE_KICKSTART="http://127.0.0.1/fake.ks" cleanup_dirs = [] class BootTest(unittest.TestCase): def setUp(self): # Create temp dir self.topdir = tempfile.mkdtemp(prefix="_cobbler-",dir="/tmp") #self.topdir = "/tmp" # only for refactoring, fix later print "using dir = %s" % self.topdir self.fk_initrd = os.path.join(self.topdir, FAKE_INITRD) self.fk_initrd2 = os.path.join(self.topdir, FAKE_INITRD2) self.fk_initrd3 = os.path.join(self.topdir, FAKE_INITRD3) self.fk_kernel = os.path.join(self.topdir, FAKE_KERNEL) self.fk_kernel2 = os.path.join(self.topdir, FAKE_KERNEL2) self.fk_kernel3 = os.path.join(self.topdir, FAKE_KERNEL3) self.api = api.BootAPI() self.hostname = os.uname()[1] create = [ self.fk_initrd, self.fk_initrd2, self.fk_initrd3, self.fk_kernel, self.fk_kernel2, self.fk_kernel3 ] for fn in create: f = open(fn,"w+") self.make_basic_config() def tearDown(self): # only off during refactoring, fix later shutil.rmtree(self.topdir,ignore_errors=True) self.api = None if HAS_GRAPH: pycallgraph.save_dot("%s.dot" % self.__class__.__name__) def make_basic_config(self): distro = self.api.new_distro() self.assertTrue(distro.set_name("testdistro0")) self.assertTrue(distro.set_kernel(self.fk_kernel)) self.assertTrue(distro.set_initrd(self.fk_initrd)) self.assertTrue(self.api.distros().add(distro)) self.assertTrue(self.api.distros().find(name="testdistro0")) profile = self.api.new_profile() self.assertTrue(profile.set_name("testprofile0")) self.assertTrue(profile.set_distro("testdistro0")) self.assertTrue(profile.set_kickstart(FAKE_KICKSTART)) self.assertTrue(self.api.profiles().add(profile)) self.assertTrue(self.api.profiles().find(name="testprofile0")) system = self.api.new_system() self.assertTrue(system.set_name(self.hostname)) self.assertTrue(system.set_profile("testprofile0")) self.assertTrue(self.api.systems().add(system)) self.assertTrue(self.api.systems().find(name=self.hostname)) class Utilities(BootTest): def _expeq(self, expected, actual): try: self.failUnlessEqual(expected, actual, "Expected: %s; actual: %s" % (expected, actual)) except: self.fail("exception during failUnlessEqual") def test_kernel_scan(self): self.assertTrue(utils.find_kernel(self.fk_kernel)) self.assertFalse(utils.find_kernel("filedoesnotexist")) self._expeq(self.fk_kernel, utils.find_kernel(self.topdir)) def test_initrd_scan(self): self.assertTrue(utils.find_initrd(self.fk_initrd)) self.assertFalse(utils.find_initrd("filedoesnotexist")) self._expeq(self.fk_initrd, utils.find_initrd(self.topdir)) def test_kickstart_scan(self): # we don't check to see if kickstart files look like anything # so this will pass self.assertTrue(utils.find_kickstart("filedoesnotexist") is None) self.assertTrue(utils.find_kickstart(self.topdir) == None) self.assertTrue(utils.find_kickstart("http://bar")) self.assertTrue(utils.find_kickstart("ftp://bar")) self.assertTrue(utils.find_kickstart("nfs://bar")) self.assertFalse(utils.find_kickstart("gopher://bar")) def test_matching(self): self.assertTrue(utils.is_mac("00:C0:B7:7E:55:50")) self.assertTrue(utils.is_mac("00:c0:b7:7E:55:50")) self.assertFalse(utils.is_mac("00.D0.B7.7E.55.50")) self.assertFalse(utils.is_mac(self.hostname)) self.assertTrue(utils.is_ip("127.0.0.1")) self.assertTrue(utils.is_ip("192.168.1.1")) self.assertFalse(utils.is_ip("00:C0:B7:7E:55:50")) self.assertFalse(utils.is_ip(self.hostname)) class Additions(BootTest): def test_some_random_find_commands(self): # initial setup... self.test_system_name_is_a_MAC() # search for a parameter that isn't real, want an error self.failUnlessRaises(CobblerException,self.api.systems().find, pond="mcelligots") # search for a parameter with a bad value, want None self.assertFalse(self.api.systems().find(name="horton")) # one valid parameter another invalid is still an error self.failUnlessRaises(CobblerException,self.api.systems().find, name="onefish",pond="mcelligots") # searching with no args is ALSO an error self.failUnlessRaises(CobblerException, self.api.systems().find) # searching for a list returns a list of correct length self.assertTrue(len(self.api.systems().find(mac_address="00:16:41:14:B7:71",return_list=True))==1) # make sure we can still search without an explicit keyword arg self.assertTrue(len(self.api.systems().find("00:16:41:14:B7:71",return_list=True))==1) self.assertTrue(self.api.systems().find("00:16:41:14:B7:71")) def test_invalid_distro_non_referenced_kernel(self): distro = self.api.new_distro() self.assertTrue(distro.set_name("testdistro2")) self.failUnlessRaises(CobblerException,distro.set_kernel,"filedoesntexist") self.assertTrue(distro.set_initrd(self.fk_initrd)) self.failUnlessRaises(CobblerException, self.api.distros().add, distro) self.assertFalse(self.api.distros().find(name="testdistro2")) def test_invalid_distro_non_referenced_initrd(self): distro = self.api.new_distro() self.assertTrue(distro.set_name("testdistro3")) self.assertTrue(distro.set_kernel(self.fk_kernel)) self.failUnlessRaises(CobblerException, distro.set_initrd, "filedoesntexist") self.failUnlessRaises(CobblerException, self.api.distros().add, distro) self.assertFalse(self.api.distros().find(name="testdistro3")) def test_invalid_profile_non_referenced_distro(self): profile = self.api.new_profile() self.assertTrue(profile.set_name("testprofile11")) self.failUnlessRaises(CobblerException, profile.set_distro, "distrodoesntexist") self.assertTrue(profile.set_kickstart(FAKE_KICKSTART)) self.failUnlessRaises(CobblerException, self.api.profiles().add, profile) self.assertFalse(self.api.profiles().find(name="testprofile2")) def test_invalid_profile_kickstart_not_url(self): profile = self.api.new_profile() self.assertTrue(profile.set_name("testprofile12")) self.assertTrue(profile.set_distro("testdistro0")) self.failUnlessRaises(CobblerException, profile.set_kickstart, "kickstartdoesntexist") # since kickstarts are optional, you can still add it self.assertTrue(self.api.profiles().add(profile)) self.assertTrue(self.api.profiles().find(name="testprofile12")) # now verify the other kickstart forms would still work self.assertTrue(profile.set_kickstart("http://bar")) self.assertTrue(profile.set_kickstart("ftp://bar")) self.assertTrue(profile.set_kickstart("nfs://bar")) def test_profile_virt_parameter_checking(self): profile = self.api.new_profile() self.assertTrue(profile.set_name("testprofile12b")) self.assertTrue(profile.set_distro("testdistro0")) self.assertTrue(profile.set_kickstart("http://127.0.0.1/foo")) # no slashes or wildcards in name # sizes must be integers self.assertTrue(profile.set_virt_file_size("54321")) # temporarily commenting out failing test # self.failUnlessRaises(CobblerException, profile.set_virt_file_size, "huge") # self.failUnlessRaises(CobblerException, profile.set_virt_file_size, "54321.23") self.assertTrue(self.api.profiles().add(profile)) def test_inheritance_and_variable_propogation(self): # STEP ONE: verify that non-inherited objects behave # correctly with ks_meta (we picked this attribute # because it's a hash and it's a bit harder to handle # than strings). It should be passed down the render # tree to all subnodes repo = self.api.new_repo() try: os.path.makedirs("/tmp/test_cobbler_repo") except: pass self.assertTrue(repo.set_name("testrepo")) self.assertTrue(repo.set_mirror("/tmp")) self.assertTrue(self.api.repos().add(repo)) profile = self.api.new_profile() self.assertTrue(profile.set_name("testprofile12b2")) self.assertTrue(profile.set_distro("testdistro0")) self.assertTrue(profile.set_kickstart("http://127.0.0.1/foo")) self.assertTrue(profile.set_repos(["testrepo"])) self.assertTrue(self.api.profiles().add(profile)) self.api.sync() system = self.api.new_system() self.assertTrue(system.set_name("foo")) self.assertTrue(system.set_profile("testprofile12b2")) self.assertTrue(system.set_ksmeta({"asdf" : "jkl" })) self.assertTrue(self.api.systems().add(system)) profile = self.api.profiles().find("testprofile12b2") ksmeta = profile.ks_meta self.assertFalse(ksmeta.has_key("asdf")) # FIXME: do the same for inherited profiles # now verify the same for an inherited profile # and this time walk up the tree to verify it wasn't # applied to any other object except the base. profile2 = self.api.new_profile(is_subobject=True) profile2.set_name("testprofile12b3") profile2.set_parent("testprofile12b2") self.assertTrue(self.api.profiles().add(profile2)) self.api.sync() # FIXME: now add a system to the inherited profile # and set a attribute on it that we will later check for system2 = self.api.new_system() self.assertTrue(system2.set_name("foo2")) self.assertTrue(system2.set_profile("testprofile12b3")) self.assertTrue(system2.set_ksmeta({"narf" : "troz"})) self.assertTrue(self.api.systems().add(system2)) self.api.sync() # FIXME: now evaluate the system object and make sure # that it has inherited the repos value from the superprofile # above it's actual profile. This should NOT be present in the # actual object, which we have not modified yet. data = utils.blender(False, system2) self.assertTrue(data["repos"] == ["testrepo"]) self.assertTrue(self.api.profiles().find(system2.profile).repos == "<>") # now if we set the repos object of the system to an additional # repo we should verify it now contains two repos. # (FIXME) repo2 = self.api.new_repo() self.assertTrue(repo2.set_name("testrepo2")) self.assertTrue(repo2.set_mirror("/tmp")) self.assertTrue(self.api.repos().add(repo2)) profile2 = self.api.profiles().find("testprofile12b3") # note: side check to make sure we can also set to string values profile2.set_repos("testrepo2") self.api.profiles().add(profile2) # save it # random bug testing: run sync several times and ensure cardinality doesn't change self.api.sync() self.api.sync() self.api.sync() data = utils.blender(False, system2) self.assertTrue("testrepo" in data["repos"]) self.assertTrue("testrepo2" in data["repos"]) self.assertTrue(len(data["repos"]) == 2) self.assertTrue(self.api.profiles().find(system2.profile).repos == ["testrepo2"]) # now double check that the parent profile still only has one repo in it. # this is part of our test against upward propogation profile = self.api.profiles().find("testprofile12b2") print profile.repos self.assertTrue(len(profile.repos) == 1) self.assertTrue(profile.repos == ["testrepo"]) # now see if the subprofile does NOT have the ksmeta attribute # this is part of our test against upward propogation profile2 = self.api.profiles().find("testprofile12b3") self.assertTrue(type(profile2.ks_meta) == type("")) self.assertTrue(profile2.ks_meta == "<>") # now see if the profile above this profile still doesn't have it profile = self.api.profiles().find("testprofile12b2") self.assertTrue(type(profile.ks_meta) == type({})) self.api.sync() self.assertFalse(profile.ks_meta.has_key("narf"), "profile does not have the system ksmeta") self.api.sync() # verify that the distro did not acquire the property # we just set on the leaf system distro = self.api.distros().find("testdistro0") self.assertTrue(type(distro.ks_meta) == type({})) self.assertFalse(distro.ks_meta.has_key("narf"), "distro does not have the system ksmeta") # STEP THREE: verify that inheritance appears to work # by setting ks_meta on the subprofile and seeing # if it appears on the leaf system ... must use # blender functions profile2 = self.api.profiles().find("testprofile12b3") profile2.set_ksmeta({"canyouseethis" : "yes" }) self.assertTrue(self.api.profiles().add(profile2)) system2 = self.api.systems().find("foo2") data = utils.blender(False, system2) self.assertTrue(data.has_key("ks_meta")) self.assertTrue(data["ks_meta"].has_key("canyouseethis")) # STEP FOUR: do the same on the superprofile and see # if that propogates profile = self.api.profiles().find("testprofile12b2") profile.set_ksmeta({"canyouseethisalso" : "yes" }) self.assertTrue(self.api.profiles().add(profile)) system2 = self.api.systems().find("foo2") data = utils.blender(False, system2) self.assertTrue(data.has_key("ks_meta")) self.assertTrue(data["ks_meta"].has_key("canyouseethisalso")) # STEP FIVE: see if distro attributes propogate distro = self.api.distros().find("testdistro0") distro.set_ksmeta({"alsoalsowik" : "moose" }) self.assertTrue(self.api.distros().add(distro)) system2 = self.api.systems().find("foo2") data = utils.blender(False, system2) self.assertTrue(data.has_key("ks_meta")) self.assertTrue(data["ks_meta"].has_key("alsoalsowik")) # STEP SEVEN: see if settings changes also propogate # TBA def test_system_name_is_a_MAC(self): system = self.api.new_system() name = "00:16:41:14:B7:71" self.assertTrue(system.set_name(name)) self.assertTrue(system.set_profile("testprofile0")) self.assertTrue(self.api.systems().add(system)) self.assertTrue(self.api.systems().find(name=name)) self.assertTrue(self.api.systems().find(mac_address="00:16:41:14:B7:71")) self.assertFalse(self.api.systems().find(mac_address="thisisnotamac")) def test_system_name_is_an_IP(self): system = self.api.new_system() name = "192.168.1.54" self.assertTrue(system.set_name(name)) self.assertTrue(system.set_profile("testprofile0")) self.assertTrue(self.api.systems().add(system)) self.assertTrue(self.api.systems().find(name=name)) def test_invalid_system_non_referenced_profile(self): system = self.api.new_system() self.assertTrue(system.set_name(self.hostname)) self.failUnlessRaises(CobblerException, system.set_profile, "profiledoesntexist") self.failUnlessRaises(CobblerException, self.api.systems().add, system) class Deletions(BootTest): def test_invalid_delete_profile_doesnt_exist(self): self.failUnlessRaises(CobblerException, self.api.profiles().remove, "doesnotexist") def test_invalid_delete_profile_would_orphan_systems(self): self.make_basic_config() self.failUnlessRaises(CobblerException, self.api.profiles().remove, "testprofile0") def test_invalid_delete_system_doesnt_exist(self): self.failUnlessRaises(CobblerException, self.api.systems().remove, "doesnotexist") def test_invalid_delete_distro_doesnt_exist(self): self.failUnlessRaises(CobblerException, self.api.distros().remove, "doesnotexist") def test_invalid_delete_distro_would_orphan_profile(self): self.make_basic_config() self.failUnlessRaises(CobblerException, self.api.distros().remove, "testdistro0") def test_working_deletes(self): self.api.clear() self.make_basic_config() self.assertTrue(self.api.systems().remove(self.hostname)) self.api.serialize() self.assertTrue(self.api.profiles().remove("testprofile0")) self.assertTrue(self.api.distros().remove("testdistro0")) self.assertFalse(self.api.systems().find(name=self.hostname)) self.assertFalse(self.api.profiles().find(name="testprofile0")) self.assertFalse(self.api.distros().find(name="testdistro0")) class TestCheck(BootTest): def test_check(self): # we can't know if it's supposed to fail in advance # (ain't that the halting problem), but it shouldn't ever # throw exceptions. self.api.check() class TestSync(BootTest): def test_real_run(self): # syncing a real test run in an automated environment would # break a valid cobbler configuration, so we're not going to # test this here. pass class TestListings(BootTest): def test_listings(self): # check to see if the collection listings output something. # this is a minimal check, mainly for coverage, not validity self.make_basic_config() self.assertTrue(len(self.api.systems().printable()) > 0) self.assertTrue(len(self.api.profiles().printable()) > 0) self.assertTrue(len(self.api.distros().printable()) > 0) class TestCLIBasic(BootTest): def test_cli(self): # just invoke the CLI to increase coverage and ensure # nothing major is broke at top level. Full CLI command testing # is not included (yet) since the API tests hit that fairly throughly # and it would easily double the length of the tests. app = "/usr/bin/python" self.assertTrue(subprocess.call([app,"cobbler/cobbler.py","list"]) == 0) if __name__ == "__main__": if not os.path.exists("setup.py"): print "tests: must invoke from top level directory" sys.exit(1) if HAS_GRAPH: pycallgraph.start_trace() loader = unittest.defaultTestLoader test_module = __import__("tests") # self import considered harmful? tests = loader.loadTestsFromModule(test_module) runner = unittest.TextTestRunner() runner.run(tests) if HAS_GRAPH: pycallgraph.make_graph('cg_dot.png', tool='dot') sys.exit(0)