summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@redhat.com>2008-04-18 18:25:14 -0400
committerMichael DeHaan <mdehaan@redhat.com>2008-04-18 18:25:14 -0400
commitb3d798bd603d4c1fe6bb6740b9f95630ba4ee483 (patch)
tree6a74857c654dd41e63f8708076fcfb34de4e7a26
parentaae7d2bb2133fcd171207e0c2a70ed009136d447 (diff)
downloadthird_party-cobbler-b3d798bd603d4c1fe6bb6740b9f95630ba4ee483.zip
third_party-cobbler-b3d798bd603d4c1fe6bb6740b9f95630ba4ee483.tar.gz
third_party-cobbler-b3d798bd603d4c1fe6bb6740b9f95630ba4ee483.tar.xz
Abstract out sync code into it's constituent parts, plus some packaging changes that I left out earlier.
-rw-r--r--MANIFEST.in1
-rw-r--r--cobbler.spec1
-rw-r--r--cobbler/action_litesync.py14
-rw-r--r--cobbler/action_sync.py495
-rw-r--r--cobbler/dhcpgen.py196
-rw-r--r--cobbler/pxegen.py322
-rw-r--r--cobbler/webui/master.py4
-rw-r--r--cobbler/yumgen.py108
8 files changed, 655 insertions, 486 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
index 644c0c2..3216946 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -2,6 +2,7 @@ include loaders/COPYING_ELILO
include loaders/elilo-3.6-ia64.efi
include loaders/menu.c32
include config/cobbler.conf
+include config/cobbler_svc.conf
include config/rsync.exclude
include config/cobblerd
include config/cobblerd_rotate
diff --git a/cobbler.spec b/cobbler.spec
index 5ebe403..2d0cc15 100644
--- a/cobbler.spec
+++ b/cobbler.spec
@@ -134,6 +134,7 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT
%{_mandir}/man1/cobbler.1.gz
/etc/init.d/cobblerd
%config(noreplace) /etc/httpd/conf.d/cobbler.conf
+%config(noreplace) /etc/httpd/conf.d/cobbler_svc.conf
%dir /var/log/cobbler/syslog
%defattr(755,root,root)
diff --git a/cobbler/action_litesync.py b/cobbler/action_litesync.py
index 89e24b4..c2029d4 100644
--- a/cobbler/action_litesync.py
+++ b/cobbler/action_litesync.py
@@ -57,7 +57,7 @@ class BootLiteSync:
if distro is None:
raise CX(_("error in distro lookup: %s") % name)
# copy image files to images/$name in webdir & tftpboot:
- self.sync.copy_single_distro_files(distro)
+ self.sync.pxegen.copy_single_distro_files(distro)
# cascade sync
kids = distro.get_children()
for k in kids:
@@ -78,7 +78,7 @@ class BootLiteSync:
if profile is None:
raise CX(_("error in profile lookup"))
# rebuild the yum configuration files for any attached repos
- self.sync.retemplate_yum_repos(profile,True)
+ self.sync.yumgen.retemplate_yum_repos(profile,True)
# cascade sync
kids = profile.get_children()
for k in kids:
@@ -97,7 +97,7 @@ class BootLiteSync:
system = self.systems.find(name=name)
if system is None:
raise CX(_("error in system lookup for %s") % name)
- self.sync.write_all_system_files(system)
+ self.sync.pxegen.write_all_system_files(system)
def add_single_system(self, name):
# get the system object:
@@ -105,12 +105,12 @@ class BootLiteSync:
if system is None:
raise CX(_("error in system lookup for %s") % name)
# rebuild system_list file in webdir
- self.sync.regen_ethers() # /etc/ethers, for dnsmasq & rarpd
- self.sync.regen_hosts() # /var/lib/cobbler/cobbler_hosts, pretty much for dnsmasq
+ self.sync.dhcpgen.regen_ethers() # /etc/ethers, for dnsmasq & rarpd
+ self.sync.dhcpgen.regen_hosts() # /var/lib/cobbler/cobbler_hosts, pretty much for dnsmasq
# write the PXE files for the system
- self.sync.write_all_system_files(system)
+ self.sync.pxegen.write_all_system_files(system)
# per system kickstarts
- self.sync.retemplate_yum_repos(system,False)
+ self.sync.yumgen.retemplate_yum_repos(system,False)
def remove_single_system(self, name):
bootloc = utils.tftpboot_location()
diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py
index aa53d12..4156ea7 100644
--- a/cobbler/action_sync.py
+++ b/cobbler/action_sync.py
@@ -27,7 +27,9 @@ import errno
import utils
from cexceptions import *
import templar
-import kickgen
+import pxegen
+import dhcpgen
+import yumgen
import item_distro
import item_profile
@@ -57,7 +59,9 @@ class BootSync:
self.settings = config.settings()
self.repos = config.repos()
self.templar = templar.Templar(config)
- self.kickgen = kickgen.KickGen(config)
+ self.pxegen = pxegen.PXEGen(config)
+ self.dhcpgen = dhcpgen.DHCPGen(config)
+ self.yumgen = yumgen.YumGen(config)
self.bootloc = utils.tftpboot_location()
def run(self):
@@ -71,195 +75,35 @@ class BootSync:
# run pre-triggers...
utils.run_triggers(None, "/var/lib/cobbler/triggers/sync/pre/*")
- # in case the pre-trigger modified any objects...
+ # (paranoid) in case the pre-trigger modified any objects...
self.api.deserialize()
self.distros = self.config.distros()
self.profiles = self.config.profiles()
self.systems = self.config.systems()
self.settings = self.config.settings()
self.repos = self.config.repos()
+ self.pxegen = pxegen.PXEGen(self.config)
+ self.dhcpgen = dhcpgen.DHCPGen(self.config)
+ self.yumgen = yumgen.YumGen(self.config)
# execute the core of the sync operation
self.clean_trees()
- self.copy_bootloaders()
- self.copy_distros()
+ self.pxegen.copy_bootloaders()
+ self.pxegen.copy_distros()
for x in self.systems:
- self.write_all_system_files(x)
- self.retemplate_all_yum_repos()
+ self.pxegen.write_all_system_files(x)
+ self.yumgen.retemplate_all_yum_repos()
if self.settings.manage_dhcp:
# these functions DRT for ISC or dnsmasq
- self.write_dhcp_file()
- self.regen_ethers()
- self.regen_hosts()
- self.make_pxe_menu()
+ self.dhcpgen.write_dhcp_file()
+ self.dhcpgen.regen_ethers()
+ self.dhcpgen.regen_hosts()
+ self.pxegen.make_pxe_menu()
# run post-triggers
utils.run_triggers(None, "/var/lib/cobbler/triggers/sync/post/*")
return True
- def copy_bootloaders(self):
- """
- Copy bootloaders to the configured tftpboot directory
- NOTE: we support different arch's if defined in
- /var/lib/cobbler/settings.
- """
- for loader in self.settings.bootloaders.keys():
- path = self.settings.bootloaders[loader]
- newname = os.path.basename(path)
- destpath = os.path.join(self.bootloc, newname)
- utils.copyfile(path, destpath)
- utils.copyfile("/var/lib/cobbler/menu.c32", os.path.join(self.bootloc, "menu.c32"))
-
- # Copy memtest to tftpboot if package is installed on system
- for memtest in glob.glob('/boot/memtest*'):
- base = os.path.basename(memtest)
- utils.copyfile(memtest,os.path.join(self.bootloc,"images",base))
-
- def write_dhcp_file(self):
- """
- DHCP files are written when manage_dhcp is set in
- /var/lib/cobbler/settings.
- """
-
- settings_file = self.settings.dhcpd_conf
- template_file = "/etc/cobbler/dhcp.template"
- mode = self.settings.manage_dhcp_mode.lower()
- if mode == "dnsmasq":
- settings_file = self.settings.dnsmasq_conf
- template_file = "/etc/cobbler/dnsmasq.template"
-
- try:
- f2 = open(template_file,"r")
- except:
- raise CX(_("error writing template to file: %s") % template_file)
- template_data = ""
- template_data = f2.read()
- f2.close()
-
- # build each per-system definition
- # as configured, this only works for ISC, patches accepted
- # from those that care about Itanium. elilo seems to be unmaintained
- # so additional maintaince in other areas may be required to keep
- # this working.
-
- elilo = os.path.basename(self.settings.bootloaders["ia64"])
-
- system_definitions = {}
- counter = 0
-
- # we used to just loop through each system, but now we must loop
- # through each network interface of each system.
-
- for system in self.systems:
- profile = system.get_conceptual_parent()
- distro = profile.get_conceptual_parent()
- for (name, interface) in system.interfaces.iteritems():
-
- mac = interface["mac_address"]
- ip = interface["ip_address"]
- host = interface["hostname"]
-
- if mac is None or mac == "":
- # can't write a DHCP entry for this system
- continue
-
- counter = counter + 1
- systxt = ""
-
- if mode == "isc":
-
- # the label the entry after the hostname if possible
- if host is not None and host != "":
- systxt = "\nhost %s {\n" % host
- if self.settings.isc_set_host_name:
- systxt = systxt + " option host-name = %s;\n" % host
- else:
- systxt = "\nhost generic%d {\n" % counter
-
- if distro.arch == "ia64":
- # can't use pxelinux.0 anymore
- systxt = systxt + " filename \"/%s\";\n" % elilo
- systxt = systxt + " hardware ethernet %s;\n" % mac
- if ip is not None and ip != "":
- systxt = systxt + " fixed-address %s;\n" % ip
- systxt = systxt + "}\n"
-
- else:
- # dnsmasq. don't have to write IP and other info here, but we do tag
- # each MAC based on the arch of it's distro, if it needs something other
- # than pxelinux.0 -- for these arches, and these arches only, a dnsmasq
- # reload (full "cobbler sync") would be required after adding the system
- # to cobbler, just to tag this relationship.
-
- if ip is not None and ip != "":
- if distro.arch.lower() == "ia64":
- systxt = "dhcp-host=net:ia64," + ip + "\n"
- # support for other arches needs modifications here
- else:
- systxt = ""
-
- dhcp_tag = interface["dhcp_tag"]
- if dhcp_tag == "":
- dhcp_tag = "default"
-
- if not system_definitions.has_key(dhcp_tag):
- system_definitions[dhcp_tag] = ""
- system_definitions[dhcp_tag] = system_definitions[dhcp_tag] + systxt
-
- # we are now done with the looping through each interface of each system
-
- metadata = {
- "insert_cobbler_system_definitions" : system_definitions.get("default",""),
- "date" : time.asctime(time.gmtime()),
- "cobbler_server" : self.settings.server,
- "next_server" : self.settings.next_server,
- "elilo" : elilo
- }
-
- # now add in other DHCP expansions that are not tagged with "default"
- for x in system_definitions.keys():
- if x == "default":
- continue
- metadata["insert_cobbler_system_definitions_%s" % x] = system_definitions[x]
-
- self.templar.render(template_data, metadata, settings_file, None)
-
- def regen_ethers(self):
- # dnsmasq knows how to read this database of MACs -> IPs, so we'll keep it up to date
- # every time we add a system.
- # read 'man ethers' for format info
- fh = open("/etc/ethers","w+")
- for sys in self.systems:
- for (name, interface) in sys.interfaces.iteritems():
- mac = interface["mac_address"]
- ip = interface["ip_address"]
- if mac is None or mac == "":
- # can't write this w/o a MAC address
- continue
- if ip is not None and ip != "":
- fh.write(mac.upper() + "\t" + ip + "\n")
- fh.close()
-
- def regen_hosts(self):
- # dnsmasq knows how to read this database for host info
- # (other things may also make use of this later)
- fh = open("/var/lib/cobbler/cobbler_hosts","w+")
- for sys in self.systems:
- for (name, interface) in sys.interfaces.iteritems():
- mac = interface["mac_address"]
- host = interface["hostname"]
- ip = interface["ip_address"]
- if mac is None or mac == "":
- continue
- if host is not None and host != "" and ip is not None and ip != "":
- fh.write(ip + "\t" + host + "\n")
- fh.close()
-
-
- #def templatify(self, data, metadata, outfile):
- # for x in metadata.keys():
- # template_data = template_data.replace("$%s" % x, metadata[x])
-
def clean_trees(self):
"""
Delete any previously built pxelinux.cfg tree and virt tree info and then create
@@ -288,307 +132,4 @@ class BootSync:
utils.rmtree_contents(os.path.join(self.bootloc, "pxelinux.cfg"))
utils.rmtree_contents(os.path.join(self.bootloc, "images"))
- def copy_distros(self):
- """
- A distro is a kernel and an initrd. Copy all of them and error
- out if any files are missing. The conf file was correct if built
- via the CLI or API, though it's possible files have been moved
- since or perhaps they reference NFS directories that are no longer
- mounted.
-
- NOTE: this has to be done for both tftp and http methods
- """
- # copy is a 4-letter word but tftpboot runs chroot, thus it's required.
- for d in self.distros:
- print _("sync distro: %s") % d.name
- self.copy_single_distro_files(d)
-
- def copy_single_distro_files(self, d):
- for dirtree in [self.bootloc, self.settings.webdir]:
- distros = os.path.join(dirtree, "images")
- distro_dir = os.path.join(distros,d.name)
- utils.mkdir(distro_dir)
- kernel = utils.find_kernel(d.kernel) # full path
- initrd = utils.find_initrd(d.initrd) # full path
- if kernel is None or not os.path.isfile(kernel):
- raise CX(_("kernel not found: %(file)s, distro: %(distro)s") % { "file" : d.kernel, "distro" : d.name })
- if initrd is None or not os.path.isfile(initrd):
- raise CX(_("initrd not found: %(file)s, distro: %(distro)s") % { "file" : d.initrd, "distro" : d.name })
- b_kernel = os.path.basename(kernel)
- b_initrd = os.path.basename(initrd)
- if kernel.startswith(dirtree):
- utils.linkfile(kernel, os.path.join(distro_dir, b_kernel))
- else:
- utils.copyfile(kernel, os.path.join(distro_dir, b_kernel))
- if initrd.startswith(dirtree):
- utils.linkfile(initrd, os.path.join(distro_dir, b_initrd))
- else:
- utils.copyfile(initrd, os.path.join(distro_dir, b_initrd))
-
- def retemplate_all_yum_repos(self):
- for p in self.profiles:
- self.retemplate_yum_repos(p,True)
- for system in self.systems:
- self.retemplate_yum_repos(system,False)
-
- def retemplate_yum_repos(self,obj,is_profile):
- """
- Yum repository management files are in self.settings.webdir/repo_mirror/$name/config.repo
- and also potentially in listed in the source_repos structure of the distro object, however
- these files have server URLs in them that must be templated out. This function does this.
- """
- blended = utils.blender(self.api, False, obj)
-
- if is_profile:
- outseg = "repos_profile"
- else:
- outseg = "repos_system"
-
- input_files = []
-
- # chance old versions from upgrade do not have a source_repos
- # workaround for user bug
- if not blended.has_key("source_repos"):
- blended["source_repos"] = []
-
- # tack on all the install source repos IF there is more than one.
- # this is basically to support things like RHEL5 split trees
- # if there is only one, then there is no need to do this.
-
- for r in blended["source_repos"]:
- filename = self.settings.webdir + "/" + "/".join(r[0].split("/")[4:])
- input_files.append(filename)
-
- for repo in blended["repos"]:
- input_files.append(os.path.join(self.settings.webdir, "repo_mirror", repo, "config.repo"))
-
- for infile in input_files:
- if infile.find("ks_mirror") == -1:
- dispname = infile.split("/")[-2]
- else:
- dispname = infile.split("/")[-1].replace(".repo","")
- confdir = os.path.join(self.settings.webdir, outseg)
- outdir = os.path.join(confdir, blended["name"])
- utils.mkdir(outdir)
- try:
- infile_h = open(infile)
- except:
- print _("WARNING: cobbler reposync needs to be run on repo (%s), then re-run cobbler sync") % dispname
- continue
- infile_data = infile_h.read()
- infile_h.close()
- outfile = os.path.join(outdir, "%s.repo" % (dispname))
- self.templar.render(infile_data, blended, outfile, None)
-
-
- def write_all_system_files(self,system):
-
- profile = system.get_conceptual_parent()
- if profile is None:
- raise CX(_("system %(system)s references a missing profile %(profile)s") % { "system" : system.name, "profile" : system.profile})
- distro = profile.get_conceptual_parent()
- if distro is None:
- raise CX(_("profile %(profile)s references a missing distro %(distro)s") % { "profile" : system.profile, "distro" : profile.distro})
-
- # this used to just generate a single PXE config file, but now must
- # generate one record for each described NIC ...
-
- counter = 0
- for (name,interface) in system.interfaces.iteritems():
-
- ip = interface["ip_address"]
-
- f1 = utils.get_config_filename(system,interface=name)
-
- # for tftp only ...
- if distro.arch in [ "x86", "x86_64", "standard"]:
- # pxelinux wants a file named $name under pxelinux.cfg
- f2 = os.path.join(self.bootloc, "pxelinux.cfg", f1)
- if distro.arch == "ia64":
- # elilo expects files to be named "$name.conf" in the root
- # and can not do files based on the MAC address
- if ip is not None and ip != "":
- print _("Warning: Itanium system object (%s) needs an IP address to PXE") % system.name
-
- filename = "%s.conf" % utils.get_config_filename(system,interface=name)
- f2 = os.path.join(self.bootloc, filename)
-
- f3 = os.path.join(self.settings.webdir, "systems", f1)
-
- if system.netboot_enabled and system.is_pxe_supported():
- if distro.arch in [ "x86", "x86_64", "standard"]:
- self.write_pxe_file(f2,system,profile,distro,False)
- if distro.arch == "ia64":
- self.write_pxe_file(f2,system,profile,distro,True)
- else:
- # ensure the file doesn't exist
- utils.rmfile(f2)
-
- counter = counter + 1
-
-
- def make_pxe_menu(self):
- # only do this if there is NOT a system named default.
- default = self.systems.find(name="default")
- if default is not None:
- return
-
- fname = os.path.join(self.bootloc, "pxelinux.cfg", "default")
-
- # read the default template file
- template_src = open("/etc/cobbler/pxedefault.template")
- template_data = template_src.read()
-
- # sort the profiles
- profile_list = [profile for profile in self.profiles]
- def sort_name(a,b):
- return cmp(a.name,b.name)
- profile_list.sort(sort_name)
-
- # build out the menu entries
- pxe_menu_items = ""
- for profile in profile_list:
- distro = profile.get_conceptual_parent()
- contents = self.write_pxe_file(None,None,profile,distro,False,include_header=False)
- if contents is not None:
- pxe_menu_items = pxe_menu_items + contents + "\n"
-
- # if we have any memtest files in images, make entries for them
- # after we list the profiles
- memtests = glob.glob(self.bootloc + "/images/memtest*")
- if len(memtests) > 0:
- pxe_menu_items = pxe_menu_items + "\n\n"
- for memtest in glob.glob(self.bootloc + '/memtest*'):
- base = os.path.basename(memtest)
- contents = self.write_memtest_pxe("/images/%s" % base)
- pxe_menu_items = pxe_menu_items + contents + "\n"
-
- # save the template.
- metadata = { "pxe_menu_items" : pxe_menu_items }
- outfile = os.path.join(self.bootloc, "pxelinux.cfg", "default")
- self.templar.render(template_data, metadata, outfile, None)
- template_src.close()
-
- def write_memtest_pxe(self,filename):
- """
- Write a configuration file for memtest
- """
-
- # just some random variables
- template = None
- metadata = {}
- buffer = ""
-
- template = "/etc/cobbler/pxeprofile.template"
-
- # store variables for templating
- metadata["menu_label"] = "MENU LABEL %s" % os.path.basename(filename)
- metadata["profile_name"] = os.path.basename(filename)
- metadata["kernel_path"] = "/images/%s" % os.path.basename(filename)
- metadata["initrd_path"] = ""
- metadata["append_line"] = ""
-
- # get the template
- template_fh = open(template)
- template_data = template_fh.read()
- template_fh.close()
-
- # return results
- buffer = self.templar.render(template_data, metadata, None)
- return buffer
-
-
-
- def write_pxe_file(self,filename,system,profile,distro,is_ia64, include_header=True):
- """
- Write a configuration file for the boot loader(s).
- More system-specific configuration may come in later, if so
- that would appear inside the system object in api.py
-
- NOTE: relevant to tftp only
- """
-
- # ---
- # system might have netboot_enabled set to False (see item_system.py), if so,
- # don't do anything else and flag the error condition.
- if system is not None and not system.netboot_enabled:
- return None
-
- # ---
- # just some random variables
- template = None
- metadata = {}
- buffer = ""
-
- # ---
- # find kernel and initrd
- kernel_path = os.path.join("/images",distro.name,os.path.basename(distro.kernel))
- initrd_path = os.path.join("/images",distro.name,os.path.basename(distro.initrd))
-
- # Find the kickstart if we inherit from another profile
- kickstart_path = utils.blender(self.api, True, profile)["kickstart"]
-
- # ---
- # choose a template
- if system is None:
- template = "/etc/cobbler/pxeprofile.template"
- elif not is_ia64:
- template = "/etc/cobbler/pxesystem.template"
- else:
- template = "/etc/cobbler/pxesystem_ia64.template"
-
- # now build the kernel command line
- if system is not None:
- blended = utils.blender(self.api, True, system)
- else:
- blended = utils.blender(self.api, True,profile)
- kopts = blended["kernel_options"]
-
- # generate the append line
- append_line = "append %s" % utils.hash_to_string(kopts)
- if not is_ia64:
- append_line = "%s initrd=%s" % (append_line, initrd_path)
- if len(append_line) >= 255 + len("append "):
- print _("warning: kernel option length exceeds 255")
-
- # kickstart path rewriting (get URLs for local files)
- if kickstart_path is not None and kickstart_path != "":
-
- if system is not None and kickstart_path.startswith("/"):
- kickstart_path = "http://%s/cblr/svc/?op=ks&system=%s" % (blended["http_server"], system.name)
- elif kickstart_path.startswith("/") or kickstart_path.find("/cobbler/kickstarts/") != -1:
- kickstart_path = "http://%s/cblr/svc/?op=ks&profile=%s" % (blended["http_server"], profile.name)
-
- if distro.breed is None or distro.breed == "redhat":
- append_line = "%s ks=%s" % (append_line, kickstart_path)
- elif distro.breed == "suse":
- append_line = "%s autoyast=%s" % (append_line, kickstart_path)
- elif distro.breed == "debian":
- append_line = "%s auto=true url=%s" % (append_line, kickstart_path)
- append_line = append_line.replace("ksdevice","interface")
-
- # store variables for templating
- metadata["menu_label"] = ""
- if not is_ia64 and system is None:
- metadata["menu_label"] = "MENU LABEL %s" % profile.name
- metadata["profile_name"] = profile.name
- metadata["kernel_path"] = kernel_path
- metadata["initrd_path"] = initrd_path
- metadata["append_line"] = append_line
-
- # get the template
- template_fh = open(template)
- template_data = template_fh.read()
- template_fh.close()
-
- # save file and/or return results, depending on how called.
- buffer = self.templar.render(template_data, metadata, None)
- if filename is not None:
- fd = open(filename, "w")
- fd.write(buffer)
- fd.close()
- return buffer
-
-
-
diff --git a/cobbler/dhcpgen.py b/cobbler/dhcpgen.py
new file mode 100644
index 0000000..0ec3dda
--- /dev/null
+++ b/cobbler/dhcpgen.py
@@ -0,0 +1,196 @@
+"""
+Builds out DHCP info
+This is the code behind 'cobbler sync'.
+
+Copyright 2006-2008, 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 os
+import os.path
+import shutil
+import time
+import sub_process
+import sys
+import glob
+import traceback
+import errno
+
+import utils
+from cexceptions import *
+import templar
+
+import item_distro
+import item_profile
+import item_repo
+import item_system
+
+from utils import _
+
+class DHCPGen:
+ """
+ Handles conversion of internal state to the tftpboot tree layout
+ """
+
+ def __init__(self,config,verbose=False):
+ """
+ Constructor
+ """
+ self.verbose = verbose
+ self.config = config
+ self.api = config.api
+ self.distros = config.distros()
+ self.profiles = config.profiles()
+ self.systems = config.systems()
+ self.settings = config.settings()
+ self.repos = config.repos()
+ self.templar = templar.Templar(config)
+
+ def write_dhcp_file(self):
+ """
+ DHCP files are written when manage_dhcp is set in
+ /var/lib/cobbler/settings.
+ """
+
+ settings_file = self.settings.dhcpd_conf
+ template_file = "/etc/cobbler/dhcp.template"
+ mode = self.settings.manage_dhcp_mode.lower()
+ if mode == "dnsmasq":
+ settings_file = self.settings.dnsmasq_conf
+ template_file = "/etc/cobbler/dnsmasq.template"
+
+ try:
+ f2 = open(template_file,"r")
+ except:
+ raise CX(_("error writing template to file: %s") % template_file)
+ template_data = ""
+ template_data = f2.read()
+ f2.close()
+
+ # build each per-system definition
+ # as configured, this only works for ISC, patches accepted
+ # from those that care about Itanium. elilo seems to be unmaintained
+ # so additional maintaince in other areas may be required to keep
+ # this working.
+
+ elilo = os.path.basename(self.settings.bootloaders["ia64"])
+
+ system_definitions = {}
+ counter = 0
+
+ # we used to just loop through each system, but now we must loop
+ # through each network interface of each system.
+
+ for system in self.systems:
+ profile = system.get_conceptual_parent()
+ distro = profile.get_conceptual_parent()
+ for (name, interface) in system.interfaces.iteritems():
+
+ mac = interface["mac_address"]
+ ip = interface["ip_address"]
+ host = interface["hostname"]
+
+ if mac is None or mac == "":
+ # can't write a DHCP entry for this system
+ continue
+
+ counter = counter + 1
+ systxt = ""
+
+ if mode == "isc":
+
+ # the label the entry after the hostname if possible
+ if host is not None and host != "":
+ systxt = "\nhost %s {\n" % host
+ if self.settings.isc_set_host_name:
+ systxt = systxt + " option host-name = %s;\n" % host
+ else:
+ systxt = "\nhost generic%d {\n" % counter
+
+ if distro.arch == "ia64":
+ # can't use pxelinux.0 anymore
+ systxt = systxt + " filename \"/%s\";\n" % elilo
+ systxt = systxt + " hardware ethernet %s;\n" % mac
+ if ip is not None and ip != "":
+ systxt = systxt + " fixed-address %s;\n" % ip
+ systxt = systxt + "}\n"
+
+ else:
+ # dnsmasq. don't have to write IP and other info here, but we do tag
+ # each MAC based on the arch of it's distro, if it needs something other
+ # than pxelinux.0 -- for these arches, and these arches only, a dnsmasq
+ # reload (full "cobbler sync") would be required after adding the system
+ # to cobbler, just to tag this relationship.
+
+ if ip is not None and ip != "":
+ if distro.arch.lower() == "ia64":
+ systxt = "dhcp-host=net:ia64," + ip + "\n"
+ # support for other arches needs modifications here
+ else:
+ systxt = ""
+
+ dhcp_tag = interface["dhcp_tag"]
+ if dhcp_tag == "":
+ dhcp_tag = "default"
+
+ if not system_definitions.has_key(dhcp_tag):
+ system_definitions[dhcp_tag] = ""
+ system_definitions[dhcp_tag] = system_definitions[dhcp_tag] + systxt
+
+ # we are now done with the looping through each interface of each system
+
+ metadata = {
+ "insert_cobbler_system_definitions" : system_definitions.get("default",""),
+ "date" : time.asctime(time.gmtime()),
+ "cobbler_server" : self.settings.server,
+ "next_server" : self.settings.next_server,
+ "elilo" : elilo
+ }
+
+ # now add in other DHCP expansions that are not tagged with "default"
+ for x in system_definitions.keys():
+ if x == "default":
+ continue
+ metadata["insert_cobbler_system_definitions_%s" % x] = system_definitions[x]
+
+ self.templar.render(template_data, metadata, settings_file, None)
+
+ def regen_ethers(self):
+ # dnsmasq knows how to read this database of MACs -> IPs, so we'll keep it up to date
+ # every time we add a system.
+ # read 'man ethers' for format info
+ fh = open("/etc/ethers","w+")
+ for sys in self.systems:
+ for (name, interface) in sys.interfaces.iteritems():
+ mac = interface["mac_address"]
+ ip = interface["ip_address"]
+ if mac is None or mac == "":
+ # can't write this w/o a MAC address
+ continue
+ if ip is not None and ip != "":
+ fh.write(mac.upper() + "\t" + ip + "\n")
+ fh.close()
+
+ def regen_hosts(self):
+ # dnsmasq knows how to read this database for host info
+ # (other things may also make use of this later)
+ fh = open("/var/lib/cobbler/cobbler_hosts","w+")
+ for sys in self.systems:
+ for (name, interface) in sys.interfaces.iteritems():
+ mac = interface["mac_address"]
+ host = interface["hostname"]
+ ip = interface["ip_address"]
+ if mac is None or mac == "":
+ continue
+ if host is not None and host != "" and ip is not None and ip != "":
+ fh.write(ip + "\t" + host + "\n")
+ fh.close()
+
+
diff --git a/cobbler/pxegen.py b/cobbler/pxegen.py
new file mode 100644
index 0000000..527b6a2
--- /dev/null
+++ b/cobbler/pxegen.py
@@ -0,0 +1,322 @@
+"""
+Builds out filesystem trees/data based on the object tree.
+This is the code behind 'cobbler sync'.
+
+Copyright 2006-2008, 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 os
+import os.path
+import shutil
+import time
+import sub_process
+import sys
+import glob
+import traceback
+import errno
+
+import utils
+from cexceptions import *
+import templar
+
+import item_distro
+import item_profile
+import item_repo
+import item_system
+
+from utils import _
+
+
+class PXEGen:
+ """
+ Handles building out PXE stuff
+ """
+
+ def __init__(self,config):
+ """
+ Constructor
+ """
+ self.config = config
+ self.api = config.api
+ self.distros = config.distros()
+ self.profiles = config.profiles()
+ self.systems = config.systems()
+ self.settings = config.settings()
+ self.repos = config.repos()
+ self.templar = templar.Templar(config)
+ self.bootloc = utils.tftpboot_location()
+
+ def copy_bootloaders(self):
+ """
+ Copy bootloaders to the configured tftpboot directory
+ NOTE: we support different arch's if defined in
+ /var/lib/cobbler/settings.
+ """
+ for loader in self.settings.bootloaders.keys():
+ path = self.settings.bootloaders[loader]
+ newname = os.path.basename(path)
+ destpath = os.path.join(self.bootloc, newname)
+ utils.copyfile(path, destpath)
+ utils.copyfile("/var/lib/cobbler/menu.c32", os.path.join(self.bootloc, "menu.c32"))
+
+ # Copy memtest to tftpboot if package is installed on system
+ for memtest in glob.glob('/boot/memtest*'):
+ base = os.path.basename(memtest)
+ utils.copyfile(memtest,os.path.join(self.bootloc,"images",base))
+
+ def copy_distros(self):
+ """
+ A distro is a kernel and an initrd. Copy all of them and error
+ out if any files are missing. The conf file was correct if built
+ via the CLI or API, though it's possible files have been moved
+ since or perhaps they reference NFS directories that are no longer
+ mounted.
+
+ NOTE: this has to be done for both tftp and http methods
+ """
+ # copy is a 4-letter word but tftpboot runs chroot, thus it's required.
+ for d in self.distros:
+ print _("sync distro: %s") % d.name
+ self.copy_single_distro_files(d)
+
+ def copy_single_distro_files(self, d):
+ for dirtree in [self.bootloc, self.settings.webdir]:
+ distros = os.path.join(dirtree, "images")
+ distro_dir = os.path.join(distros,d.name)
+ utils.mkdir(distro_dir)
+ kernel = utils.find_kernel(d.kernel) # full path
+ initrd = utils.find_initrd(d.initrd) # full path
+ if kernel is None or not os.path.isfile(kernel):
+ raise CX(_("kernel not found: %(file)s, distro: %(distro)s") % { "file" : d.kernel, "distro" : d.name })
+ if initrd is None or not os.path.isfile(initrd):
+ raise CX(_("initrd not found: %(file)s, distro: %(distro)s") % { "file" : d.initrd, "distro" : d.name })
+ b_kernel = os.path.basename(kernel)
+ b_initrd = os.path.basename(initrd)
+ if kernel.startswith(dirtree):
+ utils.linkfile(kernel, os.path.join(distro_dir, b_kernel))
+ else:
+ utils.copyfile(kernel, os.path.join(distro_dir, b_kernel))
+ if initrd.startswith(dirtree):
+ utils.linkfile(initrd, os.path.join(distro_dir, b_initrd))
+ else:
+ utils.copyfile(initrd, os.path.join(distro_dir, b_initrd))
+
+ def write_all_system_files(self,system):
+
+ profile = system.get_conceptual_parent()
+ if profile is None:
+ raise CX(_("system %(system)s references a missing profile %(profile)s") % { "system" : system.name, "profile" : system.profile})
+ distro = profile.get_conceptual_parent()
+ if distro is None:
+ raise CX(_("profile %(profile)s references a missing distro %(distro)s") % { "profile" : system.profile, "distro" : profile.distro})
+
+ # this used to just generate a single PXE config file, but now must
+ # generate one record for each described NIC ...
+
+ counter = 0
+ for (name,interface) in system.interfaces.iteritems():
+
+ ip = interface["ip_address"]
+
+ f1 = utils.get_config_filename(system,interface=name)
+
+ # for tftp only ...
+ if distro.arch in [ "x86", "x86_64", "standard"]:
+ # pxelinux wants a file named $name under pxelinux.cfg
+ f2 = os.path.join(self.bootloc, "pxelinux.cfg", f1)
+ if distro.arch == "ia64":
+ # elilo expects files to be named "$name.conf" in the root
+ # and can not do files based on the MAC address
+ if ip is not None and ip != "":
+ print _("Warning: Itanium system object (%s) needs an IP address to PXE") % system.name
+
+ filename = "%s.conf" % utils.get_config_filename(system,interface=name)
+ f2 = os.path.join(self.bootloc, filename)
+
+ f3 = os.path.join(self.settings.webdir, "systems", f1)
+
+ if system.netboot_enabled and system.is_pxe_supported():
+ if distro.arch in [ "x86", "x86_64", "standard"]:
+ self.write_pxe_file(f2,system,profile,distro,False)
+ if distro.arch == "ia64":
+ self.write_pxe_file(f2,system,profile,distro,True)
+ else:
+ # ensure the file doesn't exist
+ utils.rmfile(f2)
+
+ counter = counter + 1
+
+
+ def make_pxe_menu(self):
+ # only do this if there is NOT a system named default.
+ default = self.systems.find(name="default")
+ if default is not None:
+ return
+
+ fname = os.path.join(self.bootloc, "pxelinux.cfg", "default")
+
+ # read the default template file
+ template_src = open("/etc/cobbler/pxedefault.template")
+ template_data = template_src.read()
+
+ # sort the profiles
+ profile_list = [profile for profile in self.profiles]
+ def sort_name(a,b):
+ return cmp(a.name,b.name)
+ profile_list.sort(sort_name)
+
+ # build out the menu entries
+ pxe_menu_items = ""
+ for profile in profile_list:
+ distro = profile.get_conceptual_parent()
+ contents = self.write_pxe_file(None,None,profile,distro,False,include_header=False)
+ if contents is not None:
+ pxe_menu_items = pxe_menu_items + contents + "\n"
+
+ # if we have any memtest files in images, make entries for them
+ # after we list the profiles
+ memtests = glob.glob(self.bootloc + "/images/memtest*")
+ if len(memtests) > 0:
+ pxe_menu_items = pxe_menu_items + "\n\n"
+ for memtest in glob.glob(self.bootloc + '/memtest*'):
+ base = os.path.basename(memtest)
+ contents = self.write_memtest_pxe("/images/%s" % base)
+ pxe_menu_items = pxe_menu_items + contents + "\n"
+
+ # save the template.
+ metadata = { "pxe_menu_items" : pxe_menu_items }
+ outfile = os.path.join(self.bootloc, "pxelinux.cfg", "default")
+ self.templar.render(template_data, metadata, outfile, None)
+ template_src.close()
+
+ def write_memtest_pxe(self,filename):
+ """
+ Write a configuration file for memtest
+ """
+
+ # just some random variables
+ template = None
+ metadata = {}
+ buffer = ""
+
+ template = "/etc/cobbler/pxeprofile.template"
+
+ # store variables for templating
+ metadata["menu_label"] = "MENU LABEL %s" % os.path.basename(filename)
+ metadata["profile_name"] = os.path.basename(filename)
+ metadata["kernel_path"] = "/images/%s" % os.path.basename(filename)
+ metadata["initrd_path"] = ""
+ metadata["append_line"] = ""
+
+ # get the template
+ template_fh = open(template)
+ template_data = template_fh.read()
+ template_fh.close()
+
+ # return results
+ buffer = self.templar.render(template_data, metadata, None)
+ return buffer
+
+
+
+ def write_pxe_file(self,filename,system,profile,distro,is_ia64, include_header=True):
+ """
+ Write a configuration file for the boot loader(s).
+ More system-specific configuration may come in later, if so
+ that would appear inside the system object in api.py
+
+ NOTE: relevant to tftp only
+ """
+
+ # ---
+ # system might have netboot_enabled set to False (see item_system.py), if so,
+ # don't do anything else and flag the error condition.
+ if system is not None and not system.netboot_enabled:
+ return None
+
+ # ---
+ # just some random variables
+ template = None
+ metadata = {}
+ buffer = ""
+
+ # ---
+ # find kernel and initrd
+ kernel_path = os.path.join("/images",distro.name,os.path.basename(distro.kernel))
+ initrd_path = os.path.join("/images",distro.name,os.path.basename(distro.initrd))
+
+ # Find the kickstart if we inherit from another profile
+ kickstart_path = utils.blender(self.api, True, profile)["kickstart"]
+
+ # ---
+ # choose a template
+ if system is None:
+ template = "/etc/cobbler/pxeprofile.template"
+ elif not is_ia64:
+ template = "/etc/cobbler/pxesystem.template"
+ else:
+ template = "/etc/cobbler/pxesystem_ia64.template"
+
+ # now build the kernel command line
+ if system is not None:
+ blended = utils.blender(self.api, True, system)
+ else:
+ blended = utils.blender(self.api, True,profile)
+ kopts = blended["kernel_options"]
+
+ # generate the append line
+ append_line = "append %s" % utils.hash_to_string(kopts)
+ if not is_ia64:
+ append_line = "%s initrd=%s" % (append_line, initrd_path)
+ if len(append_line) >= 255 + len("append "):
+ print _("warning: kernel option length exceeds 255")
+
+ # kickstart path rewriting (get URLs for local files)
+ if kickstart_path is not None and kickstart_path != "":
+
+ if system is not None and kickstart_path.startswith("/"):
+ kickstart_path = "http://%s/cblr/svc/?op=ks&system=%s" % (blended["http_server"], system.name)
+ elif kickstart_path.startswith("/") or kickstart_path.find("/cobbler/kickstarts/") != -1:
+ kickstart_path = "http://%s/cblr/svc/?op=ks&profile=%s" % (blended["http_server"], profile.name)
+
+ if distro.breed is None or distro.breed == "redhat":
+ append_line = "%s ks=%s" % (append_line, kickstart_path)
+ elif distro.breed == "suse":
+ append_line = "%s autoyast=%s" % (append_line, kickstart_path)
+ elif distro.breed == "debian":
+ append_line = "%s auto=true url=%s" % (append_line, kickstart_path)
+ append_line = append_line.replace("ksdevice","interface")
+
+ # store variables for templating
+ metadata["menu_label"] = ""
+ if not is_ia64 and system is None:
+ metadata["menu_label"] = "MENU LABEL %s" % profile.name
+ metadata["profile_name"] = profile.name
+ metadata["kernel_path"] = kernel_path
+ metadata["initrd_path"] = initrd_path
+ metadata["append_line"] = append_line
+
+ # get the template
+ template_fh = open(template)
+ template_data = template_fh.read()
+ template_fh.close()
+
+ # save file and/or return results, depending on how called.
+ buffer = self.templar.render(template_data, metadata, None)
+ if filename is not None:
+ fd = open(filename, "w")
+ fd.write(buffer)
+ fd.close()
+ return buffer
+
+
+
+
diff --git a/cobbler/webui/master.py b/cobbler/webui/master.py
index 0eec178..14f06b5 100644
--- a/cobbler/webui/master.py
+++ b/cobbler/webui/master.py
@@ -33,8 +33,8 @@ VFN=valueForName
currentTime=time.time
__CHEETAH_version__ = '2.0.1'
__CHEETAH_versionTuple__ = (2, 0, 1, 'final', 0)
-__CHEETAH_genTime__ = 1208545841.2024181
-__CHEETAH_genTimestamp__ = 'Fri Apr 18 15:10:41 2008'
+__CHEETAH_genTime__ = 1208557454.7957201
+__CHEETAH_genTimestamp__ = 'Fri Apr 18 18:24:14 2008'
__CHEETAH_src__ = 'webui_templates/master.tmpl'
__CHEETAH_srcLastModified__ = 'Fri Feb 15 14:47:43 2008'
__CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
diff --git a/cobbler/yumgen.py b/cobbler/yumgen.py
new file mode 100644
index 0000000..e39a72e
--- /dev/null
+++ b/cobbler/yumgen.py
@@ -0,0 +1,108 @@
+"""
+Builds out filesystem trees/data based on the object tree.
+This is the code behind 'cobbler sync'.
+
+Copyright 2006-2008, 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 os
+import os.path
+import shutil
+import time
+import sub_process
+import sys
+import glob
+import traceback
+import errno
+
+import utils
+from cexceptions import *
+import templar
+
+import item_distro
+import item_profile
+import item_repo
+import item_system
+
+from utils import _
+
+
+class YumGen:
+
+ def __init__(self,config):
+ """
+ Constructor
+ """
+ self.config = config
+ self.api = config.api
+ self.distros = config.distros()
+ self.profiles = config.profiles()
+ self.systems = config.systems()
+ self.settings = config.settings()
+ self.repos = config.repos()
+ self.templar = templar.Templar(config)
+
+ def retemplate_all_yum_repos(self):
+ for p in self.profiles:
+ self.retemplate_yum_repos(p,True)
+ for system in self.systems:
+ self.retemplate_yum_repos(system,False)
+
+ def retemplate_yum_repos(self,obj,is_profile):
+ """
+ Yum repository management files are in self.settings.webdir/repo_mirror/$name/config.repo
+ and also potentially in listed in the source_repos structure of the distro object, however
+ these files have server URLs in them that must be templated out. This function does this.
+ """
+ blended = utils.blender(self.api, False, obj)
+
+ if is_profile:
+ outseg = "repos_profile"
+ else:
+ outseg = "repos_system"
+
+ input_files = []
+
+ # chance old versions from upgrade do not have a source_repos
+ # workaround for user bug
+ if not blended.has_key("source_repos"):
+ blended["source_repos"] = []
+
+ # tack on all the install source repos IF there is more than one.
+ # this is basically to support things like RHEL5 split trees
+ # if there is only one, then there is no need to do this.
+
+ for r in blended["source_repos"]:
+ filename = self.settings.webdir + "/" + "/".join(r[0].split("/")[4:])
+ input_files.append(filename)
+
+ for repo in blended["repos"]:
+ input_files.append(os.path.join(self.settings.webdir, "repo_mirror", repo, "config.repo"))
+
+ for infile in input_files:
+ if infile.find("ks_mirror") == -1:
+ dispname = infile.split("/")[-2]
+ else:
+ dispname = infile.split("/")[-1].replace(".repo","")
+ confdir = os.path.join(self.settings.webdir, outseg)
+ outdir = os.path.join(confdir, blended["name"])
+ utils.mkdir(outdir)
+ try:
+ infile_h = open(infile)
+ except:
+ print _("WARNING: cobbler reposync needs to be run on repo (%s), then re-run cobbler sync") % dispname
+ continue
+ infile_data = infile_h.read()
+ infile_h.close()
+ outfile = os.path.join(outdir, "%s.repo" % (dispname))
+ self.templar.render(infile_data, blended, outfile, None)
+
+