summaryrefslogtreecommitdiffstats
path: root/cobbler
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@redhat.com>2006-07-12 17:00:49 -0400
committerJim Meyering <jim@meyering.net>2006-07-12 17:00:49 -0400
commitbea30de4beb585b5c79d10c6164e65713f1611bc (patch)
tree435799b37baa464c21e42e622914cbdd8b33276f /cobbler
parentf061199e4f8a3ea0e769325e72029ca7d791b206 (diff)
downloadcobbler-bea30de4beb585b5c79d10c6164e65713f1611bc.tar.gz
cobbler-bea30de4beb585b5c79d10c6164e65713f1611bc.tar.xz
cobbler-bea30de4beb585b5c79d10c6164e65713f1611bc.zip
Added templating support through new --ks_meta option which works like --kopts. The parameter is a space delimited list of key=value pairs, which allows the variables entered to be evaluated through Cheetah. Thus kickstarts are now Cheetah templates. All templating errors are ignored so usage of a $ in a template is still legal where it doesn't reference a variable. Error ignoring should be finer grained and this does need some tests. Currently this only works for kickstarts on filesystems, and I'm not sure what the behavior for http and nfs should be. Anyhow, fairly useful stuff.
Diffstat (limited to 'cobbler')
-rw-r--r--cobbler/action_sync.py135
-rwxr-xr-xcobbler/cobbler.py9
-rw-r--r--cobbler/cobbler_msg.py5
-rw-r--r--cobbler/collection.py8
-rw-r--r--cobbler/collection_distros.py3
-rw-r--r--cobbler/collection_profiles.py3
-rw-r--r--cobbler/collection_systems.py3
-rw-r--r--cobbler/item.py14
-rw-r--r--cobbler/item_distro.py6
-rw-r--r--cobbler/item_profile.py14
-rw-r--r--cobbler/item_system.py6
11 files changed, 168 insertions, 38 deletions
diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py
index 399a8494..92f84913 100644
--- a/cobbler/action_sync.py
+++ b/cobbler/action_sync.py
@@ -16,16 +16,16 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import os
import shutil
import yaml
+from Cheetah.Template import Template
import utils
import cobbler_msg
import cexceptions
-
-"""
-Handles conversion of internal state to the tftpboot tree layout
-"""
-
+
class BootSync:
+ """
+ Handles conversion of internal state to the tftpboot tree layout
+ """
def __init__(self,config):
"""
@@ -92,7 +92,7 @@ class BootSync:
"""
Delete any previously built pxelinux.cfg tree and xen tree info.
"""
- for x in ["pxelinux.cfg","images","systems","distros","profiles","kickstarts"]:
+ for x in ["pxelinux.cfg","images","systems","distros","profiles","kickstarts","kickstarts_sys"]:
path = os.path.join(self.settings.tftpboot,x)
self.rmtree(path)
self.mkdir(path)
@@ -129,23 +129,98 @@ class BootSync:
(http or ftp), can stay as is. kickstarts referenced by absolute
path (i.e. are files path) will be mirrored over http.
"""
- # ensure all referenced kickstarts exist
- # these are served by either NFS, Apache, or some ftpd, so we don't need to copy them
- # it's up to the user to make sure they are nicely served by their URLs
+
+ self.validate_kickstarts_per_profile()
+ self.validate_kickstarts_per_system()
+ return True
+
+ def validate_kickstarts_per_profile(self):
+ """
+ Koan provisioning (Xen + auto-ks) needs kickstarts
+ per profile. Validate them as needed. Local kickstarts
+ get template substitution. Since http:// kickstarts might
+ get generated via magic URLs, those are *not* substituted.
+ NFS kickstarts are also not substituted when referenced
+ by NFS URL's as we don't copy those files over to the cobbler
+ directories. They are supposed to be live such that an
+ admin can update those without needing to run 'sync' again.
+ """
for g in self.profiles:
+ distro = self.distros.find(g.distro)
self.sync_log(cobbler_msg.lookup("sync_mirror_ks"))
kickstart_path = utils.find_kickstart(g.kickstart)
if kickstart_path and os.path.exists(kickstart_path):
# the input is an *actual* file, hence we have to copy it
- copy_path = os.path.join(self.settings.tftpboot, "kickstarts", g.name)
+ copy_path = os.path.join(
+ self.settings.tftpboot,
+ "kickstarts", # profile kickstarts go here
+ g.name
+ )
self.mkdir(copy_path)
dest = os.path.join(copy_path, "ks.cfg")
- try:
- self.copyfile(g.kickstart, dest)
- except:
- raise cexceptions.CobblerException("err_kickstart2")
-
+ # FIXME -- uncomment try for now
+ #try:
+ meta = self.blend_options(False, (
+ distro.ks_meta,
+ g.ks_meta,
+ ))
+ self.apply_template(kickstart_path, meta, dest)
+ #except:
+ #msg = "err_kickstart2" % (g.kickstart,dest)
+ #raise cexceptions.CobblerException(msg)
+
+ def validate_kickstarts_per_system(self):
+ """
+ PXE provisioning needs kickstarts evaluated per system.
+ Profiles would normally be sufficient, but not in cases
+ such as static IP, where we want to be able to do templating
+ on a system basis.
+
+ FIXME: be sure PXE configs reference the new kickstarts_sys path
+ instead.
+ """
+
+ for s in self.systems:
+ profile = self.profiles.find(s.profile)
+ distro = self.distros.find(profile.distro)
+ kickstart_path = utils.find_kickstart(profile.kickstart)
+ if kickstart_path and os.path.exists(kickstart_path):
+ copy_path = os.path.join(self.settings.tftpboot,
+ "kickstarts_sys", # system kickstarts go here
+ s.name
+ )
+ self.mkdir(copy_path)
+ dest = os.path.join(copy_path, "ks.cfg")
+ try:
+ meta = self.blend_options(False,(
+ distro.ks_meta,
+ profile.ks_meta,
+ s.ks_meta
+ ))
+ self.apply_template(kickstart_path, meta, dest)
+ except:
+ msg = "err_kickstart2" % (g.kickstart, dest)
+ raise cexpcetions.CobblerException(msg)
+
+ def apply_template(self, kickstart_input, metadata, out_path):
+ """
+ Take filesystem file kickstart_input, apply metadata using
+ Cheetah and save as out_path.
+ """
+ fd = open(kickstart_input)
+ data = fd.read()
+ fd.close()
+ print metadata # FIXME: temporary
+ t = Template(
+ "#errorCatcher Echo\n%s" % data,
+ searchList=[metadata],
+ )
+ computed = str(t)
+ fd = open(out_path, "w+")
+ fd.write(computed)
+ fd.close()
+
def build_trees(self):
"""
Now that kernels and initrds are copied and kickstarts are all valid,
@@ -161,7 +236,7 @@ class BootSync:
self.sync_log(cobbler_msg.lookup("sync_processing") % d.name)
# TODO: add check to ensure all distros have profiles (=warning)
filename = os.path.join(self.settings.tftpboot,"distros",d.name)
- d.kernel_options = self.blend_kernel_options((
+ d.kernel_options = self.blend_options(True,(
self.settings.kernel_options,
d.kernel_options
))
@@ -174,7 +249,7 @@ class BootSync:
filename = os.path.join(self.settings.tftpboot,"profiles",p.name)
distro = self.distros.find(p.distro)
if distro is not None:
- p.kernel_options = self.blend_kernel_options((
+ p.kernel_options = self.blend_options(True,(
self.settings.kernel_options,
distro.kernel_options,
p.kernel_options
@@ -230,7 +305,7 @@ class BootSync:
self.tee(fd,"timeout 1\n")
self.tee(fd,"label linux\n")
self.tee(fd," kernel %s\n" % kernel_path)
- kopts = self.blend_kernel_options((
+ kopts = self.blend_options(True,(
self.settings.kernel_options,
profile.kernel_options,
distro.kernel_options,
@@ -238,10 +313,10 @@ class BootSync:
))
nextline = " append %s initrd=%s" % (kopts,initrd_path)
if kickstart_path is not None and kickstart_path != "":
- # if kickstart path is local, we've already copied it into
+ # if kickstart path is on disk, we've already copied it into
# the HTTP mirror, so make it something anaconda can get at
if kickstart_path.startswith("/"):
- kickstart_path = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (self.settings.server, profile.name)
+ kickstart_path = "http://%s/cobbler/kickstarts_sys/%s/ks.cfg" % (self.settings.server, system.name)
nextline = nextline + " ks=%s" % kickstart_path
self.tee(fd, nextline)
self.close_file(fd)
@@ -367,15 +442,19 @@ class BootSync:
else:
print message
- def blend_kernel_options(self, list_of_opts):
+ def blend_options(self, is_for_kernel, list_of_opts):
"""
- Given a list of kernel options, take the values used by the
+ Given a list of options, take the values used by the
first argument in the list unless overridden by those in the
second (or further on), according to --key=value formats.
This is used such that we can have default kernel options
in /etc and then distro, profile, and system options with various
- levels of configurability.
+ levels of configurability overriding them. This also works
+ for template metadata (--ksopts)
+
+ The output when is_for_kernel is true is a space delimited list.
+ When is_for_kernel is false, it's just a hash (which Cheetah requires).
"""
internal = {}
results = []
@@ -390,17 +469,21 @@ class BootSync:
internal[key_value[0]] = ""
else:
internal[key_value[0]] = key_value[1]
- # now go back through the final list and render the single
+ if not is_for_kernel:
+ return internal
+ # the kernel requires a flat string for options, and we want
+ # to remove certain invalid options.
+ # go back through the final list and render the single
# items AND key/value items
for key in internal.keys():
data = internal[key]
- if key == "ks" or key == "initrd" or key == "append":
+ if (key == "ks" or key == "initrd" or key == "append"):
# the user REALLY doesn't want to do this...
continue
if data == "":
results.append(key)
else:
results.append("%s=%s" % (key,internal[key]))
- # end result is a new fragment of a kernel options string
+ # end result is a new fragment of an options string
return " ".join(results)
diff --git a/cobbler/cobbler.py b/cobbler/cobbler.py
index 22cb8d31..99498823 100755
--- a/cobbler/cobbler.py
+++ b/cobbler/cobbler.py
@@ -128,7 +128,8 @@ class BootCLI:
'--name' : lambda(a) : sys.set_name(a),
'--system' : lambda(a) : sys.set_name(a),
'--profile' : lambda(a) : sys.set_profile(a),
- '--kopts' : lambda(a) : sys.set_kernel_options(a)
+ '--kopts' : lambda(a) : sys.set_kernel_options(a),
+ '--ksmeta' : lambda(a) : sys.set_ksmeta(a)
}
on_ok = lambda: self.api.systems().add(sys)
return self.apply_args(args,commands,on_ok)
@@ -147,7 +148,8 @@ class BootCLI:
'--kopts' : lambda(a) : profile.set_kernel_options(a),
'--xen-name' : lambda(a) : profile.set_xen_name(a),
'--xen-file-size' : lambda(a) : profile.set_xen_file_size(a),
- '--xen-ram' : lambda(a) : profile.set_xen_ram(a)
+ '--xen-ram' : lambda(a) : profile.set_xen_ram(a),
+ '--ksmeta' : lambda(a) : profile.set_ksmeta(a)
# the following options are most likely not useful for profiles (yet)
# primarily due to not being implemented in koan.
# '--xen-mac' : lambda(a) : profile.set_xen_mac(a),
@@ -167,7 +169,8 @@ class BootCLI:
'--distro' : lambda(a) : distro.set_name(a),
'--kernel' : lambda(a) : distro.set_kernel(a),
'--initrd' : lambda(a) : distro.set_initrd(a),
- '--kopts' : lambda(a) : distro.set_kernel_options(a)
+ '--kopts' : lambda(a) : distro.set_kernel_options(a),
+ '--ksmeta' : lambda(a) : distro.set_ksmeta(a)
}
on_ok = lambda: self.api.distros().add(distro)
return self.apply_args(args,commands,on_ok)
diff --git a/cobbler/cobbler_msg.py b/cobbler/cobbler_msg.py
index f686f794..550cca74 100644
--- a/cobbler/cobbler_msg.py
+++ b/cobbler/cobbler_msg.py
@@ -15,6 +15,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""
_msg_table = {
+ "system" : "System",
+ "profile" : "Profile",
+ "distribution" : "Distribution",
"bad_server" : "The 'server' field in /var/lib/cobbler/settings must be set to something other than localhost, or kickstarting features will not work",
"parse_error" : "cobbler could not read %s, replacing...",
"no_create" : "cobbler could not create: %s",
@@ -39,7 +42,7 @@ _msg_table = {
"no_line" : "file '%s' should have a line '%s' somewhere",
"no_dir2" : "can't find %s for %s as referenced in /var/lib/cobbler/settings",
"bad_param" : "at least one parameter is missing for this function",
- "empty_list" : "There are no configured items.",
+ "empty_list" : "There are no configured %s records.",
"err_resolv" : "The system name (%s) did not resolve",
"err_kickstart" : "The kickstart (%s) for item (%s) is not valid",
"err_kickstart2" : "Error while mirroring kickstart file (%s) to (%s)",
diff --git a/cobbler/collection.py b/cobbler/collection.py
index ad1ed275..2525d668 100644
--- a/cobbler/collection.py
+++ b/cobbler/collection.py
@@ -96,7 +96,7 @@ class Collection(serializable.Serializable):
if len(values) > 0:
return "\n\n".join(results)
else:
- return cobbler_msg.lookup("empty_list")
+ return cobbler_msg.lookup("empty_list" % cobbler_msg.lookup(self.collection_type()))
def __iter__(self):
"""
@@ -111,4 +111,10 @@ class Collection(serializable.Serializable):
"""
return len(self.listing.values())
+ def collection_type(self):
+ """
+ Returns the string key for the name of the collection (for use in messages for humans)
+ """
+ return exceptions.NotImplementedError
+
diff --git a/cobbler/collection_distros.py b/cobbler/collection_distros.py
index 9625b58c..a687a6bc 100644
--- a/cobbler/collection_distros.py
+++ b/cobbler/collection_distros.py
@@ -20,6 +20,9 @@ import cexceptions
class Distros(collection.Collection):
+ def collection_type(self):
+ return "distribution"
+
def factory_produce(self,config,seed_data):
"""
Return a Distro forged from seed_data
diff --git a/cobbler/collection_profiles.py b/cobbler/collection_profiles.py
index 152e1f8c..93fb1c75 100644
--- a/cobbler/collection_profiles.py
+++ b/cobbler/collection_profiles.py
@@ -24,6 +24,9 @@ import cexceptions
class Profiles(collection.Collection):
+ def collection_type(self):
+ return "profile"
+
def factory_produce(self,config,seed_data):
return profile.Profile(config).from_datastruct(seed_data)
diff --git a/cobbler/collection_systems.py b/cobbler/collection_systems.py
index 39b31190..368c3f64 100644
--- a/cobbler/collection_systems.py
+++ b/cobbler/collection_systems.py
@@ -22,6 +22,9 @@ import cexceptions
class Systems(collection.Collection):
+ def collection_type(self):
+ return "system"
+
def factory_produce(self,config,seed_data):
"""
Return a system forged from seed_data
diff --git a/cobbler/item.py b/cobbler/item.py
index 9e8a5ba2..6c55225c 100644
--- a/cobbler/item.py
+++ b/cobbler/item.py
@@ -34,6 +34,20 @@ class Item(serializable.Serializable):
self.kernel_options = options_string
return True
+ def set_ksmeta(self,options_string):
+ """
+ A comma delimited list of key value pairs, like 'a=b,c=d,e=f'.
+ The meta tags are used as input to the Cheetah templating system
+ to preprocess kickstart files
+ """
+ self.ks_meta = options_string
+ tokens = self.ks_meta.split(",")
+ for t in tokens:
+ tokens2 = t.split("=")
+ if len(tokens2) != 2:
+ return False
+ return True
+
def to_datastruct(self):
"""
Returns an easily-marshalable representation of the collection.
diff --git a/cobbler/item_distro.py b/cobbler/item_distro.py
index 7491a98e..e15c2b1c 100644
--- a/cobbler/item_distro.py
+++ b/cobbler/item_distro.py
@@ -37,6 +37,7 @@ class Distro(item.Item):
self.kernel = None
self.initrd = None
self.kernel_options = ""
+ self.ks_meta = ""
def from_datastruct(self,seed_data):
"""
@@ -46,6 +47,7 @@ class Distro(item.Item):
self.kernel = seed_data['kernel']
self.initrd = seed_data['initrd']
self.kernel_options = seed_data['kernel_options']
+ self.ks_meta = seed_data['ks_meta']
return self
def set_kernel(self,kernel):
@@ -88,7 +90,8 @@ class Distro(item.Item):
'name': self.name,
'kernel': self.kernel,
'initrd' : self.initrd,
- 'kernel_options' : self.kernel_options
+ 'kernel_options' : self.kernel_options,
+ 'ks_meta' : self.ks_meta
}
def printable(self, id):
@@ -109,5 +112,6 @@ class Distro(item.Item):
buf = buf + "kernel : %s\n" % kstr
buf = buf + "initrd : %s\n" % istr
buf = buf + "kernel options : %s\n" % self.kernel_options
+ buf = buf + "ks metadata : %s\n" % self.ks_meta
return buf
diff --git a/cobbler/item_profile.py b/cobbler/item_profile.py
index 46c79294..e3cecfec 100644
--- a/cobbler/item_profile.py
+++ b/cobbler/item_profile.py
@@ -33,11 +33,12 @@ class Profile(item.Item):
self.distro = None # a name, not a reference
self.kickstart = None
self.kernel_options = ''
+ self.ks_meta = ''
self.xen_name = 'xenguest'
- self.xen_file_size = 5 # GB
- self.xen_ram = 2048 # MB
- self.xen_mac = ''
- self.xen_paravirt = True
+ self.xen_file_size = 5 # GB. 5 = Decent _minimum_ default for FC5.
+ self.xen_ram = 512 # MB. Install with 256 not likely to pass
+ self.xen_mac = '' # allow random generation as default
+ self.xen_paravirt = True # hvm support is *NOT* in Koan (now)
def from_datastruct(self,seed_data):
"""
@@ -47,6 +48,7 @@ class Profile(item.Item):
self.distro = seed_data['distro']
self.kickstart = seed_data['kickstart']
self.kernel_options = seed_data['kernel_options']
+ self.ks_meta = seed_data['ks_meta']
self.xen_name = seed_data['xen_name']
if not self.xen_name or self.xen_name == '':
self.xen_name = self.name
@@ -176,7 +178,8 @@ class Profile(item.Item):
'xen_file_size' : self.xen_file_size,
'xen_ram' : self.xen_ram,
'xen_mac' : self.xen_mac,
- 'xen_paravirt' : self.xen_paravirt
+ 'xen_paravirt' : self.xen_paravirt,
+ 'ks_meta' : self.ks_meta
}
def printable(self,id):
@@ -187,6 +190,7 @@ class Profile(item.Item):
buf = buf + "distro : %s\n" % self.distro
buf = buf + "kickstart : %s\n" % self.kickstart
buf = buf + "kernel options : %s\n" % self.kernel_options
+ buf = buf + "ks metadata : %s\n" % self.ks_meta
buf = buf + "xen name : %s\n" % self.xen_name
buf = buf + "xen file size : %s\n" % self.xen_file_size
buf = buf + "xen ram : %s\n" % self.xen_ram
diff --git a/cobbler/item_system.py b/cobbler/item_system.py
index ab4b5203..68ba1564 100644
--- a/cobbler/item_system.py
+++ b/cobbler/item_system.py
@@ -26,11 +26,13 @@ class System(item.Item):
self.name = None
self.profile = None # a name, not a reference
self.kernel_options = ""
+ self.ks_meta = ""
def from_datastruct(self,seed_data):
self.name = seed_data['name']
self.profile = seed_data['profile']
self.kernel_options = seed_data['kernel_options']
+ self.ks_meta = seed_data['ks_meta']
return self
def set_name(self,name):
@@ -69,12 +71,14 @@ class System(item.Item):
return {
'name' : self.name,
'profile' : self.profile,
- 'kernel_options' : self.kernel_options
+ 'kernel_options' : self.kernel_options,
+ 'ks_meta' : self.ks_meta
}
def printable(self,id):
buf = "system %-4s : %s\n" % (id, self.name)
buf = buf + "profile : %s\n" % self.profile
buf = buf + "kernel options : %s" % self.kernel_options
+ buf = buf + "ks metadata : %s" % self.ks_meta
return buf