summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorroot <root@mdehaan.rdu.redhat.com>2007-09-05 16:31:30 -0400
committerroot <root@mdehaan.rdu.redhat.com>2007-09-05 16:31:30 -0400
commit69cd6adec522e73a52a2c0f3aac70401f92adc9f (patch)
tree6f22eab2a8848a455fc5c1c6faf13f239ced0eaf
parent26263db30e0ad51f0cabfcc6f7dde92d04cdcfbb (diff)
downloadthird_party-cobbler-69cd6adec522e73a52a2c0f3aac70401f92adc9f.tar.gz
third_party-cobbler-69cd6adec522e73a52a2c0f3aac70401f92adc9f.tar.xz
third_party-cobbler-69cd6adec522e73a52a2c0f3aac70401f92adc9f.zip
Working on pluggable serializers.
-rw-r--r--Makefile2
-rwxr-xr-xcobbler/cobbler.py12
-rw-r--r--cobbler/config.py44
-rw-r--r--cobbler/module_loader.py68
-rw-r--r--cobbler/modules/__init__.py0
-rw-r--r--cobbler/modules/serializer_yaml.py101
-rw-r--r--cobbler/serializer.py91
-rw-r--r--cobbler/settings.py8
-rw-r--r--setup.py3
-rw-r--r--tests/tests.py22
10 files changed, 247 insertions, 104 deletions
diff --git a/Makefile b/Makefile
index dcfa274..aa14401 100644
--- a/Makefile
+++ b/Makefile
@@ -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",
diff --git a/setup.py b/setup.py
index d3cb36b..6e9a89b 100644
--- a/setup.py
+++ b/setup.py
@@ -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"