summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@redhat.com>2008-04-18 17:31:26 -0400
committerMichael DeHaan <mdehaan@redhat.com>2008-04-18 17:31:26 -0400
commitefbcc041464733e90af670a5d1dfe13e70aaa05c (patch)
treee04450dfaed37ad8757f0e96d5314d36a46444b0
parentb15ca0fe01a01ee6792c857e766642d9d50ab760 (diff)
downloadthird_party-cobbler-efbcc041464733e90af670a5d1dfe13e70aaa05c.zip
third_party-cobbler-efbcc041464733e90af670a5d1dfe13e70aaa05c.tar.gz
third_party-cobbler-efbcc041464733e90af670a5d1dfe13e70aaa05c.tar.xz
Kickstarts are now dynamically generated by mod_python, CGI's now fall
under mod_python, kickstart templating code now moved out of sync function.
-rw-r--r--CHANGELOG4
-rw-r--r--MANIFEST.in7
-rw-r--r--Makefile1
-rw-r--r--cobbler.spec5
-rw-r--r--cobbler/action_litesync.py13
-rw-r--r--cobbler/action_sync.py365
-rw-r--r--cobbler/api.py11
-rw-r--r--cobbler/kickgen.py272
-rw-r--r--cobbler/remote.py8
-rw-r--r--cobbler/services.py94
-rw-r--r--cobbler/settings.py5
-rw-r--r--cobbler/webui/master.py4
-rw-r--r--config/cobbler_svc.conf12
-rw-r--r--config/settings5
-rwxr-xr-xlegacy/change_profile.cgi (renamed from scripts/change_profile.cgi)0
-rwxr-xr-xlegacy/findks.cgi (renamed from scripts/findks.cgi)0
-rw-r--r--legacy/install_trigger.cgi (renamed from scripts/install_trigger.cgi)0
-rwxr-xr-xlegacy/nopxe.cgi (renamed from scripts/nopxe.cgi)0
-rwxr-xr-xlegacy/register_mac.cgi (renamed from scripts/register_mac.cgi)2
-rwxr-xr-xlegacy/watcher.py (renamed from scripts/watcher.py)0
-rwxr-xr-xscripts/services.py67
-rw-r--r--setup.py9
22 files changed, 494 insertions, 390 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f721aa3..a97dd5e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -26,6 +26,10 @@ Cobbler CHANGELOG
- change default authentication to deny_all, xmlrpc_rw_enabled now on by default
- additional fix for mod_python select box submissions
- set repo arch if found in the URL and no --arch is specified
+- CGI scripts have been moved under mod_python for speed/consolidation
+- kickstart templates are now evaluated dynamically
+- optional MAC registration is now built-in to requesting kickstarts
+- legacy static file generation from /var/www/cobbler removed
- ??? - 0.8.3
- Make createrepo get run for local cobbler reposync invocations as needed
diff --git a/MANIFEST.in b/MANIFEST.in
index 12f9f9e..644c0c2 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -17,12 +17,7 @@ include docs/cobbler.1.gz
include docs/cobbler.html
include docs/wui.html
include COPYING AUTHORS README CHANGELOG
-include scripts/watcher.py
-include scripts/index.py
-include scripts/cobblerd
-include scripts/findks.cgi
-include scripts/nopxe.cgi
-include scripts/install_trigger.cgi
+include scripts/*.py
include snippets/*
recursive-include po *.pot
recursive-include po *.po
diff --git a/Makefile b/Makefile
index b8e5d0a..6287f5d 100644
--- a/Makefile
+++ b/Makefile
@@ -51,6 +51,7 @@ devinstall:
chown -R apache /var/www/cobbler
chown -R apache /var/www/cgi-bin/cobbler
chmod -R +x /var/www/cobbler/web
+ chmod -R +x /var/www/cobbler/svc
webtest: devinstall
/sbin/service cobblerd restart
diff --git a/cobbler.spec b/cobbler.spec
index 5e63f48..7b4e525 100644
--- a/cobbler.spec
+++ b/cobbler.spec
@@ -83,8 +83,8 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT
%defattr(755,apache,apache)
%dir /var/www/cobbler/web/
/var/www/cobbler/web/*.py*
-%dir /var/www/cgi-bin/cobbler/
-/var/www/cgi-bin/cobbler/*.cgi
+%dir /var/www/cobbler/svc/
+/var/www/cobbler/svc/*.py*
%defattr(755,apache,apache)
%dir /usr/share/cobbler/webui_templates
@@ -195,6 +195,7 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT
* Tue Apr 08 2008 Michael DeHaan <mdehaan@redhat.com> - 0.9.0-1
- Upstream changes (see CHANGELOG)
- packaged /etc/cobbler/users.conf
+- remaining CGI replaced with mod_python
* Tue Apr 08 2008 Michael DeHaan <mdehaan@redhat.com> - 0.8.3-2
- Upstream changes (see CHANGELOG)
diff --git a/cobbler/action_litesync.py b/cobbler/action_litesync.py
index bc7ffb2..3a2de8e 100644
--- a/cobbler/action_litesync.py
+++ b/cobbler/action_litesync.py
@@ -82,11 +82,7 @@ class BootLiteSync:
if profile is None:
raise CX(_("error in profile lookup"))
# rebuild profile_list YAML file in webdir
- self.sync.write_listings()
- # add profiles/$name YAML file in webdir
self.sync.write_profile_file(profile)
- # generate kickstart for kickstarts/$name/ks.cfg in webdir
- self.sync.validate_kickstart_for_specific_profile(profile)
# rebuild the yum configuration files for any attached repos
self.sync.retemplate_yum_repos(profile,True)
# cascade sync
@@ -98,8 +94,6 @@ class BootLiteSync:
self.add_single_system(k.name)
def remove_single_profile(self, name):
- # rebuild profile_list YAML file in webdir
- self.sync.write_listings()
# delete profiles/$name file in webdir
utils.rmfile(os.path.join(self.settings.webdir, "profiles", name))
# delete contents on kickstarts/$name directory in webdir
@@ -109,7 +103,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,True)
+ self.sync.write_all_system_files(system)
def add_single_system(self, name):
# get the system object:
@@ -119,19 +113,14 @@ class BootLiteSync:
# 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.write_listings()
# write the PXE and YAML files for the system
self.sync.write_all_system_files(system)
# per system kickstarts
- self.sync.validate_kickstart_for_specific_system(system)
- # rebuild the yum configuration files for any attached repos
self.sync.retemplate_yum_repos(system,False)
def remove_single_system(self, name):
bootloc = utils.tftpboot_location()
system_record = self.systems.find(name=name)
- # rebuild system_list file in webdir
- self.sync.write_listings()
# delete system YAML file in systems/$name in webdir
utils.rmfile(os.path.join(self.settings.webdir, "systems", name))
# delete contents of kickstarts_sys/$name in webdir
diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py
index 5b7b546..aa53d12 100644
--- a/cobbler/action_sync.py
+++ b/cobbler/action_sync.py
@@ -27,7 +27,7 @@ import errno
import utils
from cexceptions import *
import templar
-
+import kickgen
import item_distro
import item_profile
@@ -57,6 +57,7 @@ class BootSync:
self.settings = config.settings()
self.repos = config.repos()
self.templar = templar.Templar(config)
+ self.kickgen = kickgen.KickGen(config)
self.bootloc = utils.tftpboot_location()
def run(self):
@@ -82,9 +83,9 @@ class BootSync:
self.clean_trees()
self.copy_bootloaders()
self.copy_distros()
+ for x in self.systems:
+ self.write_all_system_files(x)
self.retemplate_all_yum_repos()
- self.validate_kickstarts()
- self.build_trees()
if self.settings.manage_dhcp:
# these functions DRT for ISC or dnsmasq
self.write_dhcp_file()
@@ -278,7 +279,7 @@ class BootSync:
if not x.endswith(".py"):
utils.rmfile(path)
if os.path.isdir(path):
- if not x in ["web", "webui", "localmirror","repo_mirror","ks_mirror","kickstarts","kickstarts_sys","distros","images","systems","profiles","links","repo_profile","repo_system"] :
+ if not x in ["web", "webui", "localmirror","repo_mirror","ks_mirror","images","links","repo_profile","repo_system","svc"] :
# delete directories that shouldn't exist
utils.rmtree(path)
if x in ["kickstarts","kickstarts_sys","images","systems","distros","profiles","repo_profile","repo_system"]:
@@ -324,294 +325,6 @@ class BootSync:
else:
utils.copyfile(initrd, os.path.join(distro_dir, b_initrd))
- def validate_kickstarts(self):
- """
- Similar to what we do for distros, ensure all the kickstarts
- in conf file are valid. kickstarts are referenced by URL
- (http or ftp), can stay as is. kickstarts referenced by absolute
- path (i.e. are files path) will be mirrored over http.
- """
-
- self.validate_kickstarts_per_profile()
- self.validate_kickstarts_per_system()
- return True
-
- def validate_kickstarts_per_profile(self):
- """
- Koan provisioning (Virt + 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.
-
- NOTE: kickstart only uses the web directory (if it uses them at all)
- """
-
- for g in self.profiles:
- print _("sync profile: %s") % g.name
- self.validate_kickstart_for_specific_profile(g)
-
- def validate_kickstart_for_specific_profile(self,g):
- distro = g.get_conceptual_parent()
- meta = utils.blender(self.api, False, g)
- if distro is None:
- raise CX(_("profile %(profile)s references missing distro %(distro)s") % { "profile" : g.name, "distro" : g.distro })
- kickstart_path = utils.find_kickstart(meta["kickstart"])
- if kickstart_path is not None 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.webdir,
- "kickstarts", # profile kickstarts go here
- g.name
- )
- utils.mkdir(copy_path)
- dest = os.path.join(copy_path, "ks.cfg")
- try:
- meta = utils.blender(self.api, False, g)
- ksmeta = meta["ks_meta"]
- del meta["ks_meta"]
- meta.update(ksmeta) # make available at top level
- meta["yum_repo_stanza"] = self.generate_repo_stanza(g,True)
- meta["yum_config_stanza"] = self.generate_config_stanza(g,True)
- meta["kickstart_done"] = self.generate_kickstart_signal(0, g, None)
- meta["kickstart_start"] = self.generate_kickstart_signal(1, g, None)
- meta["kernel_options"] = utils.hash_to_string(meta["kernel_options"])
- kfile = open(kickstart_path)
- self.templar.render(kfile, meta, dest, g)
- kfile.close()
- except:
- traceback.print_exc() # leave this in, for now...
- msg = "err_kickstart2"
- raise CX(_("Error while rendering kickstart file %(src)s to %(dest)s") % { "src" : kickstart_path, "dest" : dest })
-
- def generate_kickstart_signal(self, is_pre=0, profile=None, system=None):
- """
- Do things that we do at the start/end of kickstarts...
- * start: signal the status watcher we're starting
- * end: signal the status watcher we're done
- * end: disable PXE if needed
- * end: save the original kickstart file for debug
- """
-
- # FIXME: watcher is more of a request than a packaged file
- # we should eventually package something and let it do something important"
-
- nopxe = "\nwget \"http://%s/cgi-bin/cobbler/nopxe.cgi?system=%s\""
- saveks = "\nwget \"http://%s/cobbler/%s/%s/ks.cfg\" -O /root/cobbler.ks"
- runpost = "\nwget \"http://%s/cgi-bin/cobbler/install_trigger.cgi?mode=post&%s=%s\""
- runpre = "\nwget \"http://%s/cgi-bin/cobbler/install_trigger.cgi?mode=pre&%s=%s\""
-
- what = "profile"
- blend_this = profile
- if system:
- what = "system"
- blend_this = system
-
- blended = utils.blender(self.api, False, blend_this)
- kickstart = blended.get("kickstart",None)
-
- buf = ""
- srv = blended["http_server"]
- if system is not None:
- if not is_pre:
- if str(self.settings.pxe_just_once).upper() in [ "1", "Y", "YES", "TRUE" ]:
- buf = buf + nopxe % (srv, system.name)
- if kickstart and os.path.exists(kickstart):
- buf = buf + saveks % (srv, "kickstarts_sys", system.name)
- if self.settings.run_install_trigger:
- buf = buf + runpost % (srv, what, system.name)
- else:
- if self.settings.run_install_trigger:
- buf = buf + runpre % (srv, what, system.name)
-
- else:
- if not is_pre:
- if kickstart and os.path.exists(kickstart):
- buf = buf + saveks % (srv, "kickstarts", profile.name)
- if self.settings.run_install_trigger:
- buf = buf + runpost % (srv, what, profile.name)
- else:
- if self.settings.run_install_trigger:
- buf = buf + runpre % (srv, what, profile.name)
-
- return buf
-
- def get_repo_segname(self, is_profile):
- if is_profile:
- return "repos_profile"
- else:
- return "repos_system"
-
- def generate_repo_stanza(self, obj, is_profile=True):
-
- """
- Automatically attaches yum repos to profiles/systems in kickstart files
- that contain the magic $yum_repo_stanza variable.
- """
-
- buf = ""
- blended = utils.blender(self.api, False, obj)
- configs = self.get_repo_filenames(obj,is_profile)
- repos = self.repos
-
- for c in configs:
- name = c.split("/")[-1].replace(".repo","")
- (is_core, baseurl) = self.analyze_repo_config(c)
- for repo in repos:
- if repo.name == name:
- if not repo.yumopts.has_key('enabled') or repo.yumopts['enabled'] == '1':
- buf = buf + "repo --name=%s --baseurl=%s\n" % (name, baseurl)
- return buf
-
- def analyze_repo_config(self, filename):
- fd = open(filename)
- data = fd.read()
- lines = data.split("\n")
- ret = False
- baseurl = None
- for line in lines:
- if line.find("ks_mirror") != -1:
- ret = True
- if line.find("baseurl") != -1:
- first, baseurl = line.split("=")
- fd.close()
- return (ret, baseurl)
-
- def get_repo_baseurl(self, server, repo_name, is_repo_mirror=True):
- """
- Construct the URL to a repo definition.
- """
- if is_repo_mirror:
- return "http://%s/cobbler/repo_mirror/%s" % (server, repo_name)
- else:
- return "http://%s/cobbler/ks_mirror/config/%s" % (server, repo_name)
-
- def get_repo_filenames(self, obj, is_profile=True):
- """
- For a given object, return the paths to repo configuration templates
- that will be used to generate per-object repo configuration files and
- baseurls
- """
-
- blended = utils.blender(self.api, False, obj)
- urlseg = self.get_repo_segname(is_profile)
-
- topdir = "%s/%s/%s/*.repo" % (self.settings.webdir, urlseg, blended["name"])
- files = glob.glob(topdir)
- return files
-
-
- def generate_config_stanza(self, obj, is_profile=True):
-
- """
- Add in automatic to configure /etc/yum.repos.d on the remote system
- if the kickstart file contains the magic $yum_config_stanza.
- """
-
- if not self.settings.yum_post_install_mirror:
- return ""
-
- urlseg = self.get_repo_segname(is_profile)
-
- distro = obj.get_conceptual_parent()
- if not is_profile:
- distro = distro.get_conceptual_parent()
-
- blended = utils.blender(self.api, False, obj)
- configs = self.get_repo_filenames(obj, is_profile)
- buf = ""
-
- # for each kickstart template we have rendered ...
- for c in configs:
-
- name = c.split("/")[-1].replace(".repo","")
- # add the line to create the yum config file on the target box
- conf = self.get_repo_config_file(blended["http_server"],urlseg,blended["name"],name)
- buf = buf + "wget \"%s\" --output-document=/etc/yum.repos.d/%s.repo\n" % (conf, name)
-
- return buf
-
- def get_repo_config_file(self,server,urlseg,obj_name,repo_name):
- """
- Construct the URL to a repo config file that is usable in kickstart
- for use with yum. This is different than the templates cobbler reposync
- creates, as this file will allow the server to migrate and have different
- variables for different subnets/profiles/etc.
- """
- return "http://%s/cblr/%s/%s/%s.repo" % (server,urlseg,obj_name,repo_name)
-
- 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.
-
- NOTE: kickstart only uses the web directory (if it uses them at all)
- """
-
- for s in self.systems:
- print _("sync system: %s") % s.name
- self.validate_kickstart_for_specific_system(s)
-
- def validate_kickstart_for_specific_system(self,s):
- profile = s.get_conceptual_parent()
- if profile is None:
- raise CX(_("system %(system)s references missing profile %(profile)s") % { "system" : s.name, "profile" : s.profile })
- distro = profile.get_conceptual_parent()
- meta = utils.blender(self.api, False, s)
- kickstart_path = utils.find_kickstart(meta["kickstart"])
- if kickstart_path and os.path.exists(kickstart_path):
- copy_path = os.path.join(self.settings.webdir,
- "kickstarts_sys", # system kickstarts go here
- s.name
- )
- utils.mkdir(copy_path)
- dest = os.path.join(copy_path, "ks.cfg")
- try:
- ksmeta = meta["ks_meta"]
- del meta["ks_meta"]
- meta.update(ksmeta) # make available at top level
- meta["yum_repo_stanza"] = self.generate_repo_stanza(s, False)
- meta["yum_config_stanza"] = self.generate_config_stanza(s, False)
- meta["kickstart_done"] = self.generate_kickstart_signal(0, profile, s)
- meta["kickstart_start"] = self.generate_kickstart_signal(1, profile, s)
- meta["kernel_options"] = utils.hash_to_string(meta["kernel_options"])
- kfile = open(kickstart_path)
- self.templar.render(kfile, meta, dest, s)
- kfile.close()
- except:
- traceback.print_exc()
- raise CX(_("Error templating file %(src)s to %(dest)s") % { "src" : meta["kickstart"], "dest" : dest })
-
- def build_trees(self):
- """
- Now that kernels and initrds are copied and kickstarts are all valid,
- build the pxelinux.cfg tree, which contains a directory for each
- configured IP or MAC address. Also build a tree for Virt info.
-
- NOTE: some info needs to go in TFTP and HTTP directories, but not all.
- Usually it's just one or the other.
-
- """
-
- self.write_listings()
-
- # create pxelinux.cfg under tftpboot
- # and file for each MAC or IP (hex encoded 01-XX-XX-XX-XX-XX-XX)
-
- for d in self.distros:
- self.write_distro_file(d)
-
- for p in self.profiles:
- self.write_profile_file(p)
-
- for system in self.systems:
- self.write_all_system_files(system)
-
def retemplate_all_yum_repos(self):
for p in self.profiles:
self.retemplate_yum_repos(p,True)
@@ -619,8 +332,6 @@ class BootSync:
self.retemplate_yum_repos(system,False)
def retemplate_yum_repos(self,obj,is_profile):
- # FIXME: blender could use caching for performance
- # FIXME: make stanza generation code load stuff from the right place
"""
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
@@ -670,7 +381,7 @@ class BootSync:
self.templar.render(infile_data, blended, outfile, None)
- def write_all_system_files(self,system,just_edit_pxe=False):
+ def write_all_system_files(self,system):
profile = system.get_conceptual_parent()
if profile is None:
@@ -713,11 +424,6 @@ class BootSync:
# ensure the file doesn't exist
utils.rmfile(f2)
- if not just_edit_pxe:
- # allows netboot-disable to be highly performant
- # by not invoking the Cheetah engine
- self.write_system_file(f3,system)
-
counter = counter + 1
@@ -849,9 +555,9 @@ class BootSync:
if kickstart_path is not None and kickstart_path != "":
if system is not None and kickstart_path.startswith("/"):
- kickstart_path = "http://%s/cblr/kickstarts_sys/%s/ks.cfg" % (blended["http_server"], system.name)
+ 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/kickstarts/%s/ks.cfg" % (blended["http_server"], profile.name)
+ 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)
@@ -884,60 +590,5 @@ class BootSync:
return buffer
- def write_listings(self):
- """
- Creates a very simple index of available systems and profiles
- that cobbler knows about. Just the names, no details.
- """
- names1 = [x.name for x in self.profiles]
- names2 = [x.name for x in self.systems]
- data1 = yaml.dump(names1)
- data2 = yaml.dump(names2)
- fd1 = open(os.path.join(self.settings.webdir, "profile_list"), "w+")
- fd2 = open(os.path.join(self.settings.webdir, "system_list"), "w+")
- fd1.write(data1)
- fd2.write(data2)
- fd1.close()
- fd2.close()
-
- def write_distro_file(self,distro):
- """
- Create distro information for koan install
- """
- blended = utils.blender(self.api, True, distro)
- filename = os.path.join(self.settings.webdir,"distros",distro.name)
- fd = open(filename, "w+")
- fd.write(yaml.dump(blended))
- fd.close()
-
- def write_profile_file(self,profile):
- """
- Create profile information for virt install
-
- NOTE: relevant to http only
- """
-
- blended = utils.blender(self.api, True, profile)
- filename = os.path.join(self.settings.webdir,"profiles",profile.name)
- fd = open(filename, "w+")
- if blended.has_key("kickstart") and blended["kickstart"].startswith("/"):
- # write the file location as needed by koan
- blended["kickstart"] = "http://%s/cblr/kickstarts/%s/ks.cfg" % (blended["http_server"], profile.name)
- fd.write(yaml.dump(blended))
- fd.close()
-
- def write_system_file(self,filename,system):
- """
- Create system information for virt install
-
- NOTE: relevant to http only
- """
-
- blended = utils.blender(self.api, True, system)
- filename = os.path.join(self.settings.webdir,"systems",system.name)
- fd = open(filename, "w+")
- fd.write(yaml.dump(blended))
- fd.close()
-
diff --git a/cobbler/api.py b/cobbler/api.py
index 6f7d1b1..ebe987e 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -25,6 +25,7 @@ import action_validate
from cexceptions import *
import sub_process
import module_loader
+import kickgen
import logging
import os
@@ -79,6 +80,7 @@ class BootAPI:
"module",
"authz_allowall"
)
+ self.kickgen = kickgen.KickGen(self._config)
self.logger.debug("API handle initialized")
def __setup_logger(self,name):
@@ -284,7 +286,14 @@ class BootAPI:
# run cobbler reposync to apply changes
return True
-
+
+ def generate_kickstart(self,profile,system):
+ self.log("generate_kickstart")
+ if system:
+ return self.kickgen.generate_kickstart_for_system(system)
+ else:
+ return self.kickgen.generate_kickstart_for_profile(profile)
+
def check(self):
"""
See if all preqs for network booting are valid. This returns
diff --git a/cobbler/kickgen.py b/cobbler/kickgen.py
new file mode 100644
index 0000000..dce33f3
--- /dev/null
+++ b/cobbler/kickgen.py
@@ -0,0 +1,272 @@
+"""
+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 KickGen:
+ """
+ Handles conversion of internal state to the tftpboot tree layout
+ """
+
+ 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 generate_kickstart_for_profile(self,g):
+
+ g = self.api.find_profile(name=g)
+ if g is None:
+ return "# profile not found"
+
+ distro = g.get_conceptual_parent()
+ meta = utils.blender(self.api, False, g)
+ if distro is None:
+ raise CX(_("profile %(profile)s references missing distro %(distro)s") % { "profile" : g.name, "distro" : g.distro })
+ kickstart_path = utils.find_kickstart(meta["kickstart"])
+ if kickstart_path is not None and os.path.exists(kickstart_path):
+ # the input is an *actual* file, hence we have to copy it
+ try:
+ meta = utils.blender(self.api, False, g)
+ ksmeta = meta["ks_meta"]
+ del meta["ks_meta"]
+ meta.update(ksmeta) # make available at top level
+ meta["yum_repo_stanza"] = self.generate_repo_stanza(g,True)
+ meta["yum_config_stanza"] = self.generate_config_stanza(g,True)
+ meta["kickstart_done"] = self.generate_kickstart_signal(0, g, None)
+ meta["kickstart_start"] = self.generate_kickstart_signal(1, g, None)
+ meta["kernel_options"] = utils.hash_to_string(meta["kernel_options"])
+ kfile = open(kickstart_path)
+ data = self.templar.render(kfile, meta, None, g)
+ kfile.close()
+ return data
+ except:
+ traceback.print_exc() # leave this in, for now...
+ msg = "err_kickstart2"
+ raise CX(_("Error while rendering kickstart file"))
+
+ def generate_kickstart_signal(self, is_pre=0, profile=None, system=None):
+ """
+ Do things that we do at the start/end of kickstarts...
+ * start: signal the status watcher we're starting
+ * end: signal the status watcher we're done
+ * end: disable PXE if needed
+ * end: save the original kickstart file for debug
+ """
+
+ nopxe = "\nwget \"http://%s/cblr/svc/?op=nopxe&system=%s\" -O /dev/null"
+ saveks = "\nwget \"http://%s/cblr/svc/?op=ks&%s=%s\" -O /root/cobbler.ks"
+ runpost = "\nwget \"http://%s/cblr/srv/?op=trig&?mode=post&%s=%s\" -O /dev/null"
+ runpre = "\nwget \"http://%s/cblr/srv/?op=trig&?mode=pre&%s=%s\" -O /dev/null"
+
+ what = "profile"
+ blend_this = profile
+ if system:
+ what = "system"
+ blend_this = system
+
+ blended = utils.blender(self.api, False, blend_this)
+ kickstart = blended.get("kickstart",None)
+
+ buf = ""
+ srv = blended["http_server"]
+ if system is not None:
+ if not is_pre:
+ if str(self.settings.pxe_just_once).upper() in [ "1", "Y", "YES", "TRUE" ]:
+ buf = buf + nopxe % (srv, system.name)
+ if kickstart and os.path.exists(kickstart):
+ buf = buf + saveks % (srv, "system", system.name)
+ if self.settings.run_install_triggers:
+ buf = buf + runpost % (srv, what, system.name)
+ else:
+ if self.settings.run_install_triggers:
+ buf = buf + runpre % (srv, what, system.name)
+
+ else:
+ if not is_pre:
+ if kickstart and os.path.exists(kickstart):
+ buf = buf + saveks % (srv, "profile", profile.name)
+ if self.settings.run_install_triggers:
+ buf = buf + runpost % (srv, what, profile.name)
+ else:
+ if self.settings.run_install_triggers:
+ buf = buf + runpre % (srv, what, profile.name)
+
+ return buf
+
+ def generate_repo_stanza(self, obj, is_profile=True):
+
+ """
+ Automatically attaches yum repos to profiles/systems in kickstart files
+ that contain the magic $yum_repo_stanza variable.
+ """
+
+ buf = ""
+ blended = utils.blender(self.api, False, obj)
+ configs = self.get_repo_filenames(obj,is_profile)
+ repos = self.repos
+
+ for c in configs:
+ name = c.split("/")[-1].replace(".repo","")
+ (is_core, baseurl) = self.analyze_repo_config(c)
+ for repo in repos:
+ if repo.name == name:
+ if not repo.yumopts.has_key('enabled') or repo.yumopts['enabled'] == '1':
+ buf = buf + "repo --name=%s --baseurl=%s\n" % (name, baseurl)
+ return buf
+
+ def analyze_repo_config(self, filename):
+ fd = open(filename)
+ data = fd.read()
+ lines = data.split("\n")
+ ret = False
+ baseurl = None
+ for line in lines:
+ if line.find("ks_mirror") != -1:
+ ret = True
+ if line.find("baseurl") != -1:
+ first, baseurl = line.split("=")
+ fd.close()
+ return (ret, baseurl)
+
+ def get_repo_baseurl(self, server, repo_name, is_repo_mirror=True):
+ """
+ Construct the URL to a repo definition.
+ """
+ if is_repo_mirror:
+ return "http://%s/cobbler/repo_mirror/%s" % (server, repo_name)
+ else:
+ return "http://%s/cobbler/ks_mirror/config/%s" % (server, repo_name)
+
+ def get_repo_filenames(self, obj, is_profile=True):
+ """
+ For a given object, return the paths to repo configuration templates
+ that will be used to generate per-object repo configuration files and
+ baseurls
+ """
+
+ blended = utils.blender(self.api, False, obj)
+ urlseg = self.get_repo_segname(is_profile)
+
+ topdir = "%s/%s/%s/*.repo" % (self.settings.webdir, urlseg, blended["name"])
+ files = glob.glob(topdir)
+ return files
+
+
+ def get_repo_segname(self, is_profile):
+ if is_profile:
+ return "repos_profile"
+ else:
+ return "repos_system"
+
+
+ def generate_config_stanza(self, obj, is_profile=True):
+
+ """
+ Add in automatic to configure /etc/yum.repos.d on the remote system
+ if the kickstart file contains the magic $yum_config_stanza.
+ """
+
+ if not self.settings.yum_post_install_mirror:
+ return ""
+
+ urlseg = self.get_repo_segname(is_profile)
+
+ distro = obj.get_conceptual_parent()
+ if not is_profile:
+ distro = distro.get_conceptual_parent()
+
+ blended = utils.blender(self.api, False, obj)
+ configs = self.get_repo_filenames(obj, is_profile)
+ buf = ""
+
+ # for each kickstart template we have rendered ...
+ for c in configs:
+
+ name = c.split("/")[-1].replace(".repo","")
+ # add the line to create the yum config file on the target box
+ conf = self.get_repo_config_file(blended["http_server"],urlseg,blended["name"],name)
+ buf = buf + "wget \"%s\" --output-document=/etc/yum.repos.d/%s.repo\n" % (conf, name)
+
+ return buf
+
+ def get_repo_config_file(self,server,urlseg,obj_name,repo_name):
+ """
+ Construct the URL to a repo config file that is usable in kickstart
+ for use with yum. This is different than the templates cobbler reposync
+ creates, as this file will allow the server to migrate and have different
+ variables for different subnets/profiles/etc.
+ """
+ return "http://%s/cblr/%s/%s/%s.repo" % (server,urlseg,obj_name,repo_name)
+
+ def generate_kickstart_for_system(self,s):
+
+
+ s = self.api.find_system(name=s)
+ if s is None:
+ return "# system not found"
+
+ profile = s.get_conceptual_parent()
+ if profile is None:
+ raise CX(_("system %(system)s references missing profile %(profile)s") % { "system" : s.name, "profile" : s.profile })
+ distro = profile.get_conceptual_parent()
+ meta = utils.blender(self.api, False, s)
+ kickstart_path = utils.find_kickstart(meta["kickstart"])
+ if kickstart_path and os.path.exists(kickstart_path):
+ try:
+ ksmeta = meta["ks_meta"]
+ del meta["ks_meta"]
+ meta.update(ksmeta) # make available at top level
+ meta["yum_repo_stanza"] = self.generate_repo_stanza(s, False)
+ meta["yum_config_stanza"] = self.generate_config_stanza(s, False)
+ meta["kickstart_done"] = self.generate_kickstart_signal(0, profile, s)
+ meta["kickstart_start"] = self.generate_kickstart_signal(1, profile, s)
+ meta["kernel_options"] = utils.hash_to_string(meta["kernel_options"])
+ kfile = open(kickstart_path)
+ data = self.templar.render(kfile, meta, None, s)
+ kfile.close()
+ return data
+ except:
+ traceback.print_exc()
+ raise CX(_("Error templating file"))
+
diff --git a/cobbler/remote.py b/cobbler/remote.py
index 0cbaf22..a87355b 100644
--- a/cobbler/remote.py
+++ b/cobbler/remote.py
@@ -158,6 +158,14 @@ class CobblerXMLRPCInterface:
return self._fix_none(data)
+ def generate_kickstart(self,profile=None,system=None,REMOTE_ADDR=None,REMOTE_MAC=None,reg=None):
+ self.log("generate_kickstart")
+
+ if reg is not None and profile and not system:
+ regrc = self.register_mac(REMOTE_MAC,profile)
+
+ return self.api.generate_kickstart(profile,system)
+
def get_settings(self,token=None):
"""
Return the contents of /var/lib/cobbler/settings, which is a hash.
diff --git a/cobbler/services.py b/cobbler/services.py
new file mode 100644
index 0000000..9ced0c6
--- /dev/null
+++ b/cobbler/services.py
@@ -0,0 +1,94 @@
+# Mod Python service functions for Cobbler's public interface
+# (aka cool stuff that works with wget)
+#
+# Copyright 2007 Albert P. Tobey <tobert@gmail.com>
+# additions: 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 exceptions
+import xmlrpclib
+import os
+import traceback
+import string
+import sys
+import time
+
+def log_exc(apache):
+ """
+ Log active traceback to logfile.
+ """
+ (t, v, tb) = sys.exc_info()
+ apache.log_error("Exception occured: %s" % t )
+ apache.log_error("Exception value: %s" % v)
+ apache.log_error("Exception Info:\n%s" % string.join(traceback.format_list(traceback.extract_tb(tb))))
+
+class CobblerSvc(object):
+ """
+ Interesting mod python functions are all keyed off the parameter
+ mode, which defaults to index. All options are passed
+ as parameters into the function.
+ """
+ def __init__(self, server=None, apache=None):
+ self.server = server
+ self.apache = apache
+ self.remote = None
+
+ def __xmlrpc_setup(self):
+ """
+ Sets up the connection to the Cobbler XMLRPC server.
+ This is the version that does not require logins.
+ """
+ self.remote = xmlrpclib.Server(self.server, allow_none=True)
+
+ def modes(self):
+ """
+ Returns a list of methods in this object that can be run as web
+ modes.
+ """
+ retval = list()
+ for m in dir(self):
+ func = getattr( self, m )
+ if hasattr(func, 'exposed') and getattr(func,'exposed'):
+ retval.append(m)
+ return retval
+
+ def index(self,**args):
+ return "no mode specified"
+
+ def ks(self,profile=None,system=None,REMOTE_ADDR=None,REMOTE_MAC=None,reg=None,**rest):
+ """
+ Generate kickstart files...
+ """
+ self.__xmlrpc_setup()
+ return self.remote.generate_kickstart(profile,system,REMOTE_ADDR,REMOTE_MAC)
+
+ def trig(self,mode="?",profile=None,system=None,REMOTE_ADDR=None,**rest):
+ """
+ Hook to call install triggers.
+ """
+ self.__xmlrpc_setup()
+ ip = REMOTE_ADDR
+ if profile:
+ rc = self.remote.run_install_triggers(mode,"profile",profile,ip)
+ else:
+ rc = self.remote.run_install_triggers(mode,"system",system,ip)
+ return str(rc)
+
+ def nopxe(self,system=None,**rest):
+ self.__xmlrpc_setup()
+ return str(self.remote.disable_netboot(system))
+
+ # =======================================================
+ # list of functions that are callable via mod_python:
+ modes.exposed = False
+ index.exposed = True
+ ks.exposed = True
+ trig.exposed = True
+
+
diff --git a/cobbler/settings.py b/cobbler/settings.py
index 1695cbb..40ed571 100644
--- a/cobbler/settings.py
+++ b/cobbler/settings.py
@@ -22,8 +22,6 @@ TESTMODE = False
# we need.
DEFAULTS = {
- "allow_cgi_mac_registration" : 0,
- "allow_cgi_profile_change" : 0,
"allow_duplicate_macs" : 0,
"allow_duplicate_ips" : 0,
"bootloaders" : {
@@ -61,7 +59,8 @@ DEFAULTS = {
"manage_dhcp_mode" : "isc",
"next_server" : "127.0.0.1",
"pxe_just_once" : 0,
- "run_install_trigger" : 1,
+ "register_new_installs" : 0,
+ "run_install_triggers" : 1,
"server" : "127.0.0.1",
"snippetsdir" : "/var/lib/cobbler/snippets",
"syslog_port" : 25150,
diff --git a/cobbler/webui/master.py b/cobbler/webui/master.py
index 1e6d30d..0eec178 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__ = 1208298360.4598751
-__CHEETAH_genTimestamp__ = 'Tue Apr 15 18:26:00 2008'
+__CHEETAH_genTime__ = 1208545841.2024181
+__CHEETAH_genTimestamp__ = 'Fri Apr 18 15:10:41 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/config/cobbler_svc.conf b/config/cobbler_svc.conf
new file mode 100644
index 0000000..f0c86de
--- /dev/null
+++ b/config/cobbler_svc.conf
@@ -0,0 +1,12 @@
+# This configuration file allows cobbler data
+# to be accessed over HTTP.
+
+# mod_python WebUI/services
+
+<Directory "/var/www/cobbler/svc/">
+ SetHandler mod_python
+ PythonHandler services
+ PythonDebug on
+</Directory>
+
+
diff --git a/config/settings b/config/settings
index fc6739a..10e06e2 100644
--- a/config/settings
+++ b/config/settings
@@ -1,6 +1,4 @@
---
-allow_cgi_mac_registration: 0
-allow_cgi_profile_change: 0
allow_duplicate_macs: 0
allow_duplicate_ips: 0
bootloaders:
@@ -35,7 +33,8 @@ manage_dhcp: 0
manage_dhcp_mode: isc
next_server: '127.0.0.1'
pxe_just_once: 0
-run_install_trigger: 1
+register_new_installs: 0
+run_install_triggers: 1
server: '127.0.0.1'
snippetsdir: /var/lib/cobbler/snippets
syslog_port: 25150
diff --git a/scripts/change_profile.cgi b/legacy/change_profile.cgi
index f7330f1..f7330f1 100755
--- a/scripts/change_profile.cgi
+++ b/legacy/change_profile.cgi
diff --git a/scripts/findks.cgi b/legacy/findks.cgi
index 39adbcf..39adbcf 100755
--- a/scripts/findks.cgi
+++ b/legacy/findks.cgi
diff --git a/scripts/install_trigger.cgi b/legacy/install_trigger.cgi
index b83ff57..b83ff57 100644
--- a/scripts/install_trigger.cgi
+++ b/legacy/install_trigger.cgi
diff --git a/scripts/nopxe.cgi b/legacy/nopxe.cgi
index a2eae88..a2eae88 100755
--- a/scripts/nopxe.cgi
+++ b/legacy/nopxe.cgi
diff --git a/scripts/register_mac.cgi b/legacy/register_mac.cgi
index 5507525..3f251c4 100755
--- a/scripts/register_mac.cgi
+++ b/legacy/register_mac.cgi
@@ -13,7 +13,7 @@
# what is this? This is a
# script to auto add systems who make a wget into cobbler.
# right now it requires "kssendmac" in kernel options and takes only 1 arg
-# ex: wget http://cobbler.example.org/cgi-bin/regsister_mac?profile=foo
+# ex: wget http://cobbler.example.org/cgi-bin/register_mac?profile=foo
# suitable to be called from kickstart,etc
import cgi
diff --git a/scripts/watcher.py b/legacy/watcher.py
index dfa8dc3..dfa8dc3 100755
--- a/scripts/watcher.py
+++ b/legacy/watcher.py
diff --git a/scripts/services.py b/scripts/services.py
new file mode 100755
index 0000000..07243ae
--- /dev/null
+++ b/scripts/services.py
@@ -0,0 +1,67 @@
+"""
+mod_python gateway to cgi-like cobbler web functions
+
+Copyright 2007-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.
+"""
+
+from mod_python import apache
+from mod_python import Session
+from mod_python import util
+
+import xmlrpclib
+import cgi
+import os
+from cobbler.services import CobblerSvc
+
+#=======================================
+
+def handler(req):
+
+ """
+ Right now, index serves everything.
+
+ Hitting this URL means we've already cleared authn/authz
+ but we still need to use the token for all remote requests.
+ """
+
+ my_uri = req.uri
+
+ # apache.log_error("cannot load /var/lib/cobbler/web.ss")
+ req.add_common_vars()
+
+ # process form and qs data, if any
+ fs = util.FieldStorage(req)
+ form = {}
+ for x in fs.keys():
+ form[x] = str(fs.get(x,'default'))
+
+ form["REMOTE_ADDR"] = req.subprocess_env.get("REMOTE_ADDR",None)
+ form["REMOTE_MAC"] = req.subprocess_env.get("HTTP_X_RHN_PROVISIONING_MAC_0",None)
+
+ # instantiate a CobblerWeb object
+ cw = CobblerSvc(
+ apache = apache,
+ server = "http://127.0.0.1/cobbler_api"
+ )
+
+ # check for a valid path/mode
+ # handle invalid paths gracefully
+ mode = form.get('op','index')
+
+ func = getattr( cw, mode )
+ content = func( **form )
+
+ # apache.log_error("%s:%s ... %s" % (my_user, my_uri, str(form)))
+ req.content_type = "text/plain"
+ req.write(content)
+
+ return apache.OK
+
diff --git a/setup.py b/setup.py
index c8a6c99..eb969d1 100644
--- a/setup.py
+++ b/setup.py
@@ -44,8 +44,9 @@ if __name__ == "__main__":
tftp_cfg = "/tftpboot/pxelinux.cfg"
tftp_images = "/tftpboot/images"
rotpath = "/etc/logrotate.d"
- cgipath = "/var/www/cgi-bin/cobbler"
+ # cgipath = "/var/www/cgi-bin/cobbler"
modpython = "/var/www/cobbler/web"
+ modpythonsvc = "/var/www/cobbler/svc"
setup(
name="cobbler",
version = VERSION,
@@ -63,13 +64,15 @@ if __name__ == "__main__":
scripts = ["scripts/cobbler", "scripts/cobblerd"],
data_files = [
(modpython, ['scripts/index.py']),
+ (modpythonsvc, ['scripts/services.py']),
# cgi files
- (cgipath, ['scripts/findks.cgi', 'scripts/nopxe.cgi']),
- (cgipath, ['scripts/install_trigger.cgi']),
+ # (cgipath, ['scripts/nopxe.cgi']),
+ # (cgipath, ['scripts/install_trigger.cgi']),
# miscellaneous config files
(rotpath, ['config/cobblerd_rotate']),
(wwwconf, ['config/cobbler.conf']),
+ (wwwconf, ['config/cobbler_svc.conf']),
(cobpath, ['config/cobbler_hosts']),
(etcpath, ['config/modules.conf']),
(etcpath, ['config/users.digest']),