diff options
author | root <root@mdehaan.rdu.redhat.com> | 2007-09-05 16:31:30 -0400 |
---|---|---|
committer | root <root@mdehaan.rdu.redhat.com> | 2007-09-05 16:31:30 -0400 |
commit | 69cd6adec522e73a52a2c0f3aac70401f92adc9f (patch) | |
tree | 6f22eab2a8848a455fc5c1c6faf13f239ced0eaf | |
parent | 26263db30e0ad51f0cabfcc6f7dde92d04cdcfbb (diff) | |
download | third_party-cobbler-69cd6adec522e73a52a2c0f3aac70401f92adc9f.tar.gz third_party-cobbler-69cd6adec522e73a52a2c0f3aac70401f92adc9f.tar.xz third_party-cobbler-69cd6adec522e73a52a2c0f3aac70401f92adc9f.zip |
Working on pluggable serializers.
-rw-r--r-- | Makefile | 2 | ||||
-rwxr-xr-x | cobbler/cobbler.py | 12 | ||||
-rw-r--r-- | cobbler/config.py | 44 | ||||
-rw-r--r-- | cobbler/module_loader.py | 68 | ||||
-rw-r--r-- | cobbler/modules/__init__.py | 0 | ||||
-rw-r--r-- | cobbler/modules/serializer_yaml.py | 101 | ||||
-rw-r--r-- | cobbler/serializer.py | 91 | ||||
-rw-r--r-- | cobbler/settings.py | 8 | ||||
-rw-r--r-- | setup.py | 3 | ||||
-rw-r--r-- | tests/tests.py | 22 |
10 files changed, 247 insertions, 104 deletions
@@ -13,7 +13,7 @@ manpage: pod2man --center="cobbler" --release="" ./docs/cobbler.pod | gzip -c > ./docs/cobbler.1.gz pod2html ./docs/cobbler.pod > ./docs/cobbler.html -test: +test: install python tests/tests.py -rm -rf /tmp/_cobbler-* diff --git a/cobbler/cobbler.py b/cobbler/cobbler.py index 7ecb2d6..aeb8962 100755 --- a/cobbler/cobbler.py +++ b/cobbler/cobbler.py @@ -294,13 +294,13 @@ class BootCLI: raise CX(_("at least one required parameter is missing. See 'man cobbler'.")) self.__generic_copy(args,collection_fn,control_fn,exc_msg) if objname != objname2: - collection_fn().remove(objname, with_delete=self.api.sync_flag) + collection_fn().remove(objname, with_delete=True) self.api.serialize() def __generic_remove(self,args,alias1,alias2,collection_fn): commands = { - "--%s" % alias1 : lambda(a): collection_fn().remove(a, with_delete=self.api.sync_flag), - "--%s" % alias2 : lambda(a): collection_fn().remove(a, with_delete=self.api.sync_flag) + "--%s" % alias1 : lambda(a): collection_fn().remove(a, with_delete=True), + "--%s" % alias2 : lambda(a): collection_fn().remove(a, with_delete=True) } on_ok = lambda: True return self.apply_args(args,commands,on_ok) @@ -447,7 +447,7 @@ class BootCLI: def on_ok(): if newname is not None: profile.set_name(newname) - self.api.profiles().add(profile, with_copy=self.api.sync_flag) + self.api.profiles().add(profile, with_copy=True) return self.apply_args(args,commands,on_ok) def __repo_control(self,args,repo,newname=None): @@ -486,7 +486,7 @@ class BootCLI: '--breed' : lambda(a) : distro.set_breed(a) } def on_ok(): - self.api.distros().add(distro, with_copy=self.api.sync_flag) + self.api.distros().add(distro, with_copy=True) return self.apply_args(args,commands,on_ok) def __system_control(self,args,sys): @@ -513,7 +513,7 @@ class BootCLI: '--virt-type' : lambda(a) : sys.set_virt_type(a) } def on_ok(): - self.api.systems().add(sys, with_copy=self.api.sync_flag) + self.api.systems().add(sys, with_copy=True) return self.apply_args(args,commands,on_ok) ################################################################################### diff --git a/cobbler/config.py b/cobbler/config.py index 88d04da..91bf1fd 100644 --- a/cobbler/config.py +++ b/cobbler/config.py @@ -24,6 +24,9 @@ import collection_distros as distros import collection_profiles as profiles import collection_systems as systems import collection_repos as repos +import modules.serializer_yaml as serializer_yaml + +import module_loader as loader import settings import serializer @@ -52,26 +55,23 @@ class Config: Config.has_loaded = True + self.modules = loader.load_modules() + + print "DEBUG: You've got modules!: %s" % self.modules + self.api = api self._distros = distros.Distros(weakref.proxy(self)) self._repos = repos.Repos(weakref.proxy(self)) self._profiles = profiles.Profiles(weakref.proxy(self)) self._systems = systems.Systems(weakref.proxy(self)) self._settings = settings.Settings() # not a true collection - self._classes = [ - self._settings, - self._distros, - self._repos, - self._profiles, - self._systems - ] self._graph_classes = [ self._distros, self._repos, self._profiles, self._systems ] - self.file_check() + # self.file_check() def __cmp(self,a,b): return cmp(a.name,b.name) @@ -134,27 +134,29 @@ class Config: """ Forget about all loaded configuration data """ - for x in self._classes: + for x in self._graph_classes: x.clear() return True - def file_check(self): - """ - Serialize any files that do not yet exist. This is useful for bringing the - app up to a working state on first run or if files are deleted. See api.py - """ - for x in self._classes: - if not os.path.exists(x.filename()): - if not serializer.serialize(x): - return False - return True + #def file_check(self): + # """ + # Serialize any files that do not yet exist. This is useful for bringing the + # app up to a working state on first run or if files are deleted. See api.py + # """ + # for x in self._classes: + # if not os.path.exists(x.filename()): + # if not serializer.serialize(x): + # return False + # return True def serialize(self): """ Save the object hierarchy to disk, using the filenames referenced in each object. """ - for x in self._classes: + if not serializer_yaml.serialize(self._settings): + return False + for x in self._graph_classes: if not serializer.serialize(x): return False return True @@ -163,7 +165,7 @@ class Config: """ Load the object hierachy from disk, using the filenames referenced in each object. """ - if not serializer.deserialize(self._settings,topological=False): + if not serializer_yaml.deserialize(self._settings,topological=False): return False for x in self._graph_classes: if not serializer.deserialize(x,topological=True): diff --git a/cobbler/module_loader.py b/cobbler/module_loader.py new file mode 100644 index 0000000..98ca487 --- /dev/null +++ b/cobbler/module_loader.py @@ -0,0 +1,68 @@ +#!/usr/bin/python + +""" +Module loader, adapted for cobbler usage + +Copyright 2006-2007, Red Hat, Inc +Adrian Likins <alikins@redhat.com> +Michael DeHaan <mdehaan@redhat.com> + +This software may be freely redistributed under the terms of the GNU +general public license. + +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., 675 Mass Ave, Cambridge, MA 02139, USA. +""" + + +import distutils.sysconfig +import os +import sys +import glob +from rhpl.translate import _, N_, textdomain, utf8 + +plib = distutils.sysconfig.get_python_lib() +mod_path="%s/cobbler/modules" % plib +print "DEBUG: adding to python path: %s" % mod_path +sys.path.insert(0, mod_path) +sys.path.insert(1, "%s/cobbler" % plib) + +def load_modules(module_path=mod_path, blacklist=None): + filenames = glob.glob("%s/*.py" % module_path) + filenames = filenames + glob.glob("%s/*.pyc" % module_path) + filesnames = filenames + glob.glob("%s/*.pyo" % module_path) + + mods = {} + + print sys.path + + for fn in filenames: + basename = os.path.basename(fn) + if basename == "__init__.py": + continue + if basename[-3:] == ".py": + modname = basename[:-3] + elif basename[-4:] in [".pyc", ".pyo"]: + modname = basename[:-4] + + + try: + blip = __import__("modules.%s" % ( modname), globals(), locals(), [modname]) + if not hasattr(blip, "register"): + errmsg = _("%(module_path)s/%(modname)s is not a proper module") + print errmsg % {'module_path': module_path, 'modname':modname} + continue + mods[modname] = blip + except ImportError, e: + print e + raise + + return mods + + + + +if __name__ == "__main__": + print load_modules(module_path) + diff --git a/cobbler/modules/__init__.py b/cobbler/modules/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cobbler/modules/__init__.py diff --git a/cobbler/modules/serializer_yaml.py b/cobbler/modules/serializer_yaml.py new file mode 100644 index 0000000..6aa308b --- /dev/null +++ b/cobbler/modules/serializer_yaml.py @@ -0,0 +1,101 @@ +""" +Serializer code for cobbler + +Copyright 2006, Red Hat, Inc +Michael DeHaan <mdehaan@redhat.com> + +This software may be freely redistributed under the terms of the GNU +general public license. + +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., 675 Mass Ave, Cambridge, MA 02139, USA. +""" + +import distutils.sysconfig +import os +import sys +import glob + +plib = distutils.sysconfig.get_python_lib() +mod_path="%s/cobbler" % plib +sys.path.insert(0, mod_path) + + +from rhpl.translate import _, N_, textdomain, utf8 +import yaml # Howell-Clark version +from cexceptions import * +import os + +def register(obj): + """ + The mandatory cobbler module registration hook. + """ + pass + +def serialize(obj): + """ + Save an object to disk. Object must "implement" Serializable. + Will create intermediate paths if it can. Returns True on Success, + False on permission errors. + """ + filename = obj.filename() + try: + fd = open(filename,"w+") + except IOError, ioe: + dirname = os.path.dirname(filename) + if not os.path.exists(dirname): + try: + os.makedirs(dirname) + # evidentally this doesn't throw exceptions. + except OSError, ose: + pass + try: + fd = open(filename,"w+") + except IOError, ioe3: + raise CX(_("Need permissions to write to %s") % filename) + return False + datastruct = obj.to_datastruct() + encoded = yaml.dump(datastruct) + fd.write(encoded) + fd.close() + return True + +def deserialize(obj,topological=False): + """ + Populate an existing object with the contents of datastruct. + Object must "implement" Serializable. Returns True assuming + files could be read and contained decent YAML. Otherwise returns + False. + """ + filename = obj.filename() + try: + fd = open(filename,"r") + except IOError, ioe: + # if it doesn't exist, that's cool -- it's not a bug until we try + # to write the file and can't create it. + if not os.path.exists(filename): + return True + else: + raise CX(_("Need permissions to read %s") % obj.filename()) + data = fd.read() + datastruct = yaml.load(data).next() # first record + fd.close() + + if topological: + # in order to build the graph links from the flat list, sort by the + # depth of items in the graph. If an object doesn't have a depth, sort it as + # if the depth were 0. It will be assigned a proper depth at serialization + # time. This is a bit cleaner implementation wise than a topological sort, + # though that would make a shiny upgrade. + datastruct.sort(__depth_cmp) + obj.from_datastruct(datastruct) + return True + +def __depth_cmp(item1, item2): + if not item1.has_key("depth"): + return 1 + if not item2.has_key("depth"): + return -1 + return cmp(item1["depth"],item2["depth"]) + diff --git a/cobbler/serializer.py b/cobbler/serializer.py index b1161d1..15dcd10 100644 --- a/cobbler/serializer.py +++ b/cobbler/serializer.py @@ -1,7 +1,8 @@ """ Serializer code for cobbler +Now adapted to support different storage backends -Copyright 2006, Red Hat, Inc +Copyright 2006-2007, Red Hat, Inc Michael DeHaan <mdehaan@redhat.com> This software may be freely redistributed under the terms of the GNU @@ -12,78 +13,46 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. """ -import yaml # Howell-Clark version import errno import os +from rhpl.translate import _, N_, textdomain, utf8 + +import yaml # Howell-Clark version from cexceptions import * import utils +import api as cobbler_api -from rhpl.translate import _, N_, textdomain, utf8 +MODULE_CACHE = {} def serialize(obj): """ - Save an object to disk. Object must "implement" Serializable. - Will create intermediate paths if it can. Returns True on Success, - False on permission errors. + Save a collection to disk or other storage. """ - filename = obj.filename() - try: - fd = open(filename,"w+") - except IOError, ioe: - dirname = os.path.dirname(filename) - if not os.path.exists(dirname): - try: - os.makedirs(dirname) - # evidentally this doesn't throw exceptions. - except OSError, ose: - pass - try: - fd = open(filename,"w+") - except IOError, ioe3: - raise CX(_("Need permissions to write to %s") % filename) - return False - datastruct = obj.to_datastruct() - encoded = yaml.dump(datastruct) - fd.write(encoded) - fd.close() + storage_module = __get_storage_module(obj.collection_type()) + storage_module.serialize(obj) return True def deserialize(obj,topological=False): """ - Populate an existing object with the contents of datastruct. - Object must "implement" Serializable. Returns True assuming - files could be read and contained decent YAML. Otherwise returns - False. + Fill in an empty collection from disk or other storage """ - filename = obj.filename() - try: - fd = open(filename,"r") - except IOError, ioe: - # if it doesn't exist, that's cool -- it's not a bug until we try - # to write the file and can't create it. - if not os.path.exists(filename): - return True - else: - raise CX(_("Need permissions to read %s") % obj.filename()) - data = fd.read() - datastruct = yaml.load(data).next() # first record - fd.close() - - if topological: - # in order to build the graph links from the flat list, sort by the - # depth of items in the graph. If an object doesn't have a depth, sort it as - # if the depth were 0. It will be assigned a proper depth at serialization - # time. This is a bit cleaner implementation wise than a topological sort, - # though that would make a shiny upgrade. - datastruct.sort(__depth_cmp) - obj.from_datastruct(datastruct) - return True - -def __depth_cmp(item1, item2): - if not item1.has_key("depth"): - return 1 - if not item2.has_key("depth"): - return -1 - return cmp(item1["depth"],item2["depth"]) - + storage_module = __get_storage_module(obj.collection_type()) + return storage_module.deserialize(obj,topological) + + +def __get_storage_module(collection_type): + + if MODULE_CACHE.has_key(collection_type): + return MODULE_CACHE[collection_type] + config = cobbler_api.BootAPI()._config + settings = config.settings() + storage_module_name = settings.storage_modules.get(collection_type, None) + if not storage_module_name: + raise CX(_("Storage module not set for objects of type %s") % collection_type) + storage_module = config.modules.get(storage_module_name, None) + if not storage_module: + raise CX(_("Storage module %s not present") % storage_module_name) + MODULE_CACHE[collection_type] = storage_module + return storage_module + diff --git a/cobbler/settings.py b/cobbler/settings.py index f8b32e6..1c607cc 100644 --- a/cobbler/settings.py +++ b/cobbler/settings.py @@ -42,10 +42,10 @@ DEFAULTS = { "server" : "127.0.0.1", "snippetsdir" : "/var/lib/cobbler/snippets", "storage_modules" : { - "distros" : [ 'yaml' ], - "profiles" : [ 'yaml' ], - "systems" : [ 'yaml' ], - "repos" : [ 'yaml' ], + "distro" : 'serializer_yaml', + "profile" : 'serializer_yaml', + "system" : 'serializer_yaml', + "repo" : 'serializer_yaml', }, "syslog_port" : 25150, "tftpboot" : "/tftpboot", @@ -46,7 +46,8 @@ if __name__ == "__main__": license = "GPL", packages = [ "cobbler", - "cobbler/yaml" + "cobbler/yaml", + "cobbler/modules", ], scripts = ["scripts/cobbler", "scripts/cobblerd"], data_files = [ diff --git a/tests/tests.py b/tests/tests.py index 5368f69..e62eaaf 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -19,23 +19,25 @@ import subprocess import tempfile import shutil -sys.path.append('../cobbler') -sys.path.append('./cobbler') +#sys.path.append('../cobbler') +#sys.path.append('../cobbler/modules') +#sys.path.append('./cobbler') +#sys.path.append('./cobbler/modules') -import settings -import collection_distros -import collection_profiles -import collection_systems +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 -import api -import config -import utils -from cexceptions import CobblerException +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" |