summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--cobbler.spec4
-rw-r--r--cobbler/action_hardlink.py56
-rw-r--r--cobbler/action_sync.py3
-rw-r--r--cobbler/api.py5
-rw-r--r--cobbler/collection.py1
-rw-r--r--cobbler/collection_distros.py2
-rw-r--r--cobbler/collection_images.py2
-rw-r--r--cobbler/collection_profiles.py2
-rw-r--r--cobbler/collection_repos.py4
-rw-r--r--cobbler/collection_systems.py2
-rw-r--r--cobbler/modules/cli_misc.py17
-rw-r--r--cobbler/modules/scm_track.py62
-rw-r--r--cobbler/remote.py11
-rw-r--r--cobbler/settings.py2
-rw-r--r--installer_templates/settings.template9
-rw-r--r--kickstarts/legacy.ks2
-rw-r--r--kickstarts/sample.ks3
-rw-r--r--kickstarts/sample_end.ks2
-rw-r--r--setup.py8
-rw-r--r--snippets/keep_ssh_host_keys37
-rw-r--r--snippets/log_ks_post2
-rw-r--r--snippets/log_ks_pre12
-rw-r--r--webui_templates/paginate.tmpl2
24 files changed, 246 insertions, 7 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 285ebcf6..6e4b7fc7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -20,6 +20,9 @@ Cobbler CHANGELOG
- (BUGF) sleep 5 seconds between power cycling systems
- (FEAT) support for /usr/bin/cobbler-register
- (FEAT) web UI search feature
+- (FEAT) very simple "cobbler hardlink" command to optimize space in /var/www/cobbler
+- (FEAT) new "change" trigger that runs on all adds/edits/removes/syncs
+- (FEAT) an SCM trigger that knows about /var/lib/cobbler kickstarts, snippets, and config directories, and audits them for changes
- Tue Mar 3 2009 - 1.4.3
- (BUGF) fix OMAPI support's (note: deprecated) usage of subprocess
diff --git a/cobbler.spec b/cobbler.spec
index 73a39f14..cb2f0cbe 100644
--- a/cobbler.spec
+++ b/cobbler.spec
@@ -220,6 +220,7 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT
%dir /var/lib/cobbler/kickstarts/
%dir /var/lib/cobbler/backup/
%dir /var/lib/cobbler/triggers
+%dir /var/lib/cobbler/triggers/change
%dir /var/lib/cobbler/triggers/add
%dir /var/lib/cobbler/triggers/add/distro
%dir /var/lib/cobbler/triggers/add/distro/pre
@@ -273,6 +274,9 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT
%config(noreplace) /var/lib/cobbler/snippets/post_s390_reboot
%config(noreplace) /var/lib/cobbler/snippets/redhat_register
%config(noreplace) /var/lib/cobbler/snippets/cobbler_register
+%config(noreplace) /var/lib/cobbler/snippets/keep_ssh_host_keys
+%config(noreplace) /var/lib/cobbler/snippets/log_ks_pre
+%config(noreplace) /var/lib/cobbler/snippets/log_ks_post
/var/lib/cobbler/elilo-3.8-ia64.efi
/var/lib/cobbler/menu.c32
/var/lib/cobbler/yaboot-1.3.14
diff --git a/cobbler/action_hardlink.py b/cobbler/action_hardlink.py
new file mode 100644
index 00000000..6e17e357
--- /dev/null
+++ b/cobbler/action_hardlink.py
@@ -0,0 +1,56 @@
+"""
+Hard links cobbler content together to save space.
+
+Copyright 2009, Red Hat, Inc
+Michael DeHaan <mdehaan@redhat.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA
+"""
+
+import os
+import utils
+from cexceptions import *
+
+class HardLinker:
+
+ def __init__(self,config):
+ """
+ Constructor
+ """
+ #self.config = config
+ #self.api = config.api
+ #self.settings = config.settings()
+ pass
+
+ def run(self):
+ """
+ Simply hardlinks directories that are cobbler managed.
+ This is a /very/ simple command but may grow more complex
+ and intelligent over time.
+ """
+
+ # FIXME: if these directories become configurable some
+ # changes will be required here.
+
+ if not os.path.exists("/usr/sbin/hardlink"):
+ raise CX("please install 'hardlink' (/usr/sbin/hardlink) to use this feature")
+
+ print "now hardlinking to save space, this may take some time."
+
+ rc = os.system("/usr/sbin/hardlink -c -v /var/www/cobbler/ks_mirror /var/www/cobbler/repo_mirror")
+
+ return rc
+
diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py
index 78f3c3d5..76211418 100644
--- a/cobbler/action_sync.py
+++ b/cobbler/action_sync.py
@@ -141,7 +141,10 @@ class BootSync:
# run post-triggers
if self.verbose:
print "- running post-sync triggers"
+
utils.run_triggers(self.api, None, "/var/lib/cobbler/triggers/sync/post/*")
+ utils.run_triggers(self.api, None, "/var/lib/cobbler/triggers/change/*")
+
return True
def clean_trees(self):
diff --git a/cobbler/api.py b/cobbler/api.py
index ffc62644..c36d442a 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -36,6 +36,7 @@ import action_replicate
import action_acl
import action_report
import action_power
+import action_hardlink
from cexceptions import *
import sub_process
import module_loader
@@ -681,6 +682,10 @@ class BootAPI:
iso=iso, profiles=profiles, systems=systems, tempdir=tempdir, distro=distro, standalone=standalone, source=source
)
+ def hardlink(self):
+ linker = action_hardlink.HardLinker(self._config)
+ return linker.run()
+
def replicate(self, cobbler_master = None, sync_all=False, sync_kickstarts=False, sync_trees=False, sync_repos=False, sync_triggers=False, systems=False):
"""
Pull down metadata from a remote cobbler server that is a master to this server.
diff --git a/cobbler/collection.py b/cobbler/collection.py
index 3bc15cc7..50c07094 100644
--- a/cobbler/collection.py
+++ b/cobbler/collection.py
@@ -284,6 +284,7 @@ class Collection(serializable.Serializable):
# save the tree, so if neccessary, scripts can examine it.
if with_triggers:
+ self._run_triggers(self.api, ref, "/var/lib/cobbler/triggers/change/*")
self._run_triggers(self.api, ref,"/var/lib/cobbler/triggers/add/%s/post/*" % self.collection_type())
diff --git a/cobbler/collection_distros.py b/cobbler/collection_distros.py
index ae75f407..bcb23a07 100644
--- a/cobbler/collection_distros.py
+++ b/cobbler/collection_distros.py
@@ -76,6 +76,8 @@ class Distros(collection.Collection):
self.log_func("deleted distro %s" % name)
if with_triggers:
self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/delete/distro/post/*")
+ self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/change/*")
+
# look through all mirrored directories and find if any directory is holding
# this particular distribution's kernel and initrd
diff --git a/cobbler/collection_images.py b/cobbler/collection_images.py
index 2dfeeac1..1bf52069 100644
--- a/cobbler/collection_images.py
+++ b/cobbler/collection_images.py
@@ -69,6 +69,8 @@ class Images(collection.Collection):
self.log_func("deleted repo %s" % name)
if with_triggers:
self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/delete/image/post/*")
+ self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/change/*")
+
return True
if with_delete and not self.api.is_cobblerd:
diff --git a/cobbler/collection_profiles.py b/cobbler/collection_profiles.py
index bb27dea4..5ca9d4be 100644
--- a/cobbler/collection_profiles.py
+++ b/cobbler/collection_profiles.py
@@ -74,6 +74,8 @@ class Profiles(collection.Collection):
self.log_func("deleted profile %s" % name)
if with_triggers:
self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/delete/profile/post/*")
+ self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/change/*")
+
if with_delete and not self.api.is_cobblerd:
self.api._internal_cache_update("profile", name, remove=True)
diff --git a/cobbler/collection_repos.py b/cobbler/collection_repos.py
index 3b2c1c20..eef82b8c 100644
--- a/cobbler/collection_repos.py
+++ b/cobbler/collection_repos.py
@@ -65,7 +65,9 @@ class Repos(collection.Collection):
self.log_func("deleted repo %s" % name)
if with_triggers:
self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/delete/repo/post/*")
-
+ self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/change/*")
+
+
path = "/var/www/cobbler/repo_mirror/%s" % obj.name
if os.path.exists(path):
utils.rmtree(path)
diff --git a/cobbler/collection_systems.py b/cobbler/collection_systems.py
index 5a628248..16576802 100644
--- a/cobbler/collection_systems.py
+++ b/cobbler/collection_systems.py
@@ -62,7 +62,9 @@ class Systems(collection.Collection):
self.log_func("deleted system %s" % name)
if with_triggers:
self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/delete/system/post/*")
+ self._run_triggers(self.config.api, obj, "/var/lib/cobbler/triggers/change/*")
+
if with_delete and not self.api.is_cobblerd:
self.api._internal_cache_update("system", name, remove=True)
diff --git a/cobbler/modules/cli_misc.py b/cobbler/modules/cli_misc.py
index f83b5c8c..a4fc4708 100644
--- a/cobbler/modules/cli_misc.py
+++ b/cobbler/modules/cli_misc.py
@@ -39,6 +39,21 @@ HELP_FORMAT = commands.HELP_FORMAT
########################################################
+class HardLinkFunction(commands.CobblerFunction):
+ def help_me(self):
+ return HELP_FORMAT % ("cobbler hardlink","")
+
+ def command_name(self):
+ return "hardlink"
+
+ def add_options(self, p, args):
+ pass
+
+ def run(self):
+ self.api.hardlink()
+
+########################################################
+
class CheckFunction(commands.CobblerFunction):
def help_me(self):
@@ -359,7 +374,7 @@ def cli_functions(api):
ListFunction(api), StatusFunction(api),
SyncFunction(api), RepoSyncFunction(api), ValidateKsFunction(api),
ReplicateFunction(api), AclFunction(api),
- VersionFunction(api)
+ VersionFunction(api), HardLinkFunction(api)
]
return []
diff --git a/cobbler/modules/scm_track.py b/cobbler/modules/scm_track.py
new file mode 100644
index 00000000..dd9ef687
--- /dev/null
+++ b/cobbler/modules/scm_track.py
@@ -0,0 +1,62 @@
+import distutils.sysconfig
+import sys
+import os
+import traceback
+from cobbler.cexceptions import *
+import os
+import sub_process
+import sys
+#import xmlrpclib
+import cobbler.module_loader as module_loader
+
+plib = distutils.sysconfig.get_python_lib()
+mod_path="%s/cobbler" % plib
+sys.path.insert(0, mod_path)
+
+def register():
+ # this pure python trigger acts as if it were a legacy shell-trigger, but is much faster.
+ # the return of this method indicates the trigger type
+ return "/var/lib/cobbler/triggers/change/*"
+
+def scall(args):
+ op = sub_process.Popen(args, shell=False, stdout=sub_process.PIPE, stderr=sub_process.PIPE)
+ op.communicate()
+
+
+def run(api,args):
+
+
+ settings = api.settings()
+
+ scm_track_enabled = str(settings.scm_track_enabled).lower()
+ mode = str(settings.scm_track_mode).lower()
+
+ if scm_track_enabled not in [ "y", "yes", "1", "true" ]:
+ # feature disabled
+ return 0
+
+ if mode == "git":
+
+ old_dir = os.getcwd()
+ os.chdir("/var/lib/cobbler")
+ if os.getcwd() != "/var/lib/cobbler":
+ raise "danger will robinson"
+
+ if not os.path.exists("/var/lib/cobbler/.git"):
+ scall(["git","init"])
+
+ # FIXME: if we know the remote user of an XMLRPC call
+ # use them as the author
+
+ scall(["git","add","config"])
+ scall(["git","add","kickstarts"])
+ scall(["git","add","snippets"])
+
+ scall(["git","commit","-m",'API update',"--author","'cobbler <root@localhost.localdomain>'"])
+
+ os.chdir(old_dir)
+
+ return 0
+
+ else:
+ raise CX("currently unsupported SCM type: %s" % mode)
diff --git a/cobbler/remote.py b/cobbler/remote.py
index 6fdb96fe..eaf3d41d 100644
--- a/cobbler/remote.py
+++ b/cobbler/remote.py
@@ -1138,6 +1138,17 @@ class CobblerXMLRPCInterface:
self.check_access(token,"sync")
return self.api.sync()
+ def hardlink(self,token):
+ """
+ Hardlink trees and repos to save disk space. Caution: long
+ running op. Until we have a task engine, this may lock other
+ folks out of the web app, so use wisely. It may also be timeout
+ prone.
+ """
+ self._log("hardlink",token=token)
+ self.check_access(token,"hardlink")
+ return self.api.hardlink()
+
def new_distro(self,token):
"""
Creates a new (unconfigured) distro object. It works something like
diff --git a/cobbler/settings.py b/cobbler/settings.py
index bf94d24d..2422341a 100644
--- a/cobbler/settings.py
+++ b/cobbler/settings.py
@@ -105,6 +105,8 @@ DEFAULTS = {
"ris_linuxd_dir" : "/tftpboot/drivers",
"ris_linuxd_flags" : "",
"run_install_triggers" : 1,
+ "scm_track_enabled" : 0,
+ "scm_track_mode" : "git",
"server" : "127.0.0.1",
"snippetsdir" : "/var/lib/cobbler/snippets",
"tftpd_bin" : "/usr/sbin/in.tftpd",
diff --git a/installer_templates/settings.template b/installer_templates/settings.template
index d97f32c8..51457a64 100644
--- a/installer_templates/settings.template
+++ b/installer_templates/settings.template
@@ -311,6 +311,15 @@ register_new_installs: 0
# uses a logging trigger to audit install progress.
run_install_triggers: 1
+# enables a trigger which version controls all changes to /var/lib/cobbler
+# when add, edit, or sync events are performed. This can be used
+# to revert to previous database versions, generate RSS feeds, or for
+# other auditing or backup purposes. git is the recommend SCM
+# for use with this feature.
+
+scm_track_enabled: 0
+scm_track_mode: "git"
+
# this is the address of the cobbler server -- as it is used
# by systems during the install process, it must be the address
# or hostname of the system as those systems can see the server.
diff --git a/kickstarts/legacy.ks b/kickstarts/legacy.ks
index 70918985..86e31d8a 100644
--- a/kickstarts/legacy.ks
+++ b/kickstarts/legacy.ks
@@ -38,6 +38,7 @@ zerombr
autopart
%pre
+$SNIPPET('log_ks_pre')
$kickstart_start
$SNIPPET('pre_install_network_config')
$SNIPPET('pre_anamon')
@@ -45,6 +46,7 @@ $SNIPPET('pre_anamon')
%packages
%post
+$SNIPPET('log_ks_post')
# Begin yum configuration
$yum_config_stanza
# End yum configuration
diff --git a/kickstarts/sample.ks b/kickstarts/sample.ks
index f41470d9..15a0b329 100644
--- a/kickstarts/sample.ks
+++ b/kickstarts/sample.ks
@@ -41,6 +41,7 @@ autopart
%pre
+$SNIPPET('log_ks_pre')
$kickstart_start
$SNIPPET('pre_install_network_config')
# Enable installation monitoring
@@ -50,7 +51,7 @@ $SNIPPET('pre_anamon')
$SNIPPET('func_install_if_enabled')
%post
-
+$SNIPPET('log_ks_post')
# Start yum configuration
$yum_config_stanza
# End yum configuration
diff --git a/kickstarts/sample_end.ks b/kickstarts/sample_end.ks
index 21148366..0112fc0e 100644
--- a/kickstarts/sample_end.ks
+++ b/kickstarts/sample_end.ks
@@ -44,6 +44,7 @@ zerombr
autopart
%pre
+$SNIPPET('log_ks_pre')
$kickstart_start
$SNIPPET('pre_install_network_config')
# Enable installation monitoring
@@ -55,6 +56,7 @@ $SNIPPET('func_install_if_enabled')
%end
%post
+$SNIPPET('log_ks_post')
# Start yum configuration
$yum_config_stanza
# End yum configuration
diff --git a/setup.py b/setup.py
index 1a218981..62675a5e 100644
--- a/setup.py
+++ b/setup.py
@@ -164,7 +164,7 @@ if __name__ == "__main__":
# for --version support across distros
(libpath, ['config/version']),
-
+
# bootloaders and syslinux support files
(libpath, ['loaders/elilo-3.8-ia64.efi']),
(libpath, ['loaders/menu.c32']),
@@ -251,6 +251,9 @@ if __name__ == "__main__":
(snippetpath, ['snippets/post_s390_reboot']),
(snippetpath, ['snippets/redhat_register']),
(snippetpath, ['snippets/cobbler_register']),
+ (snippetpath, ['snippets/keep_ssh_host_keys']),
+ (snippetpath, ['snippets/log_ks_pre']),
+ (snippetpath, ['snippets/log_ks_post']),
# documentation
(manpath, ['docs/cobbler.1.gz']),
@@ -356,7 +359,8 @@ if __name__ == "__main__":
("%s/install/pre" % trigpath, []),
("%s/install/post" % trigpath, []),
("%s/sync/pre" % trigpath, []),
- ("%s/sync/post" % trigpath, [])
+ ("%s/sync/post" % trigpath, []),
+ ("%s/change" % trigpath, [])
],
description = SHORT_DESC,
long_description = LONG_DESC
diff --git a/snippets/keep_ssh_host_keys b/snippets/keep_ssh_host_keys
index 93a6fadb..527c9926 100644
--- a/snippets/keep_ssh_host_keys
+++ b/snippets/keep_ssh_host_keys
@@ -8,14 +8,17 @@ keys_found=no
insmod /lib/jbd.o
insmod /lib/ext3.o
+mkdir -p /tmp/ssh
+
drives=$(list-harddrives | awk '{print $1}')
for disk in $drives; do
DISKS="$DISKS $(fdisk -l /dev/$disk | awk '/^\/dev/{print $1}')"
done
+# Try to find the keys on ordinary partitions
for disk in $DISKS; do
name=$(basename $disk)
- mkdir -p /tmp/$name /tmp/ssh
+ mkdir -p /tmp/$name
mount $disk /tmp/$name
[ $? -eq 0 ] || continue # Skip to the next partition if the mount fails
@@ -30,6 +33,38 @@ for disk in $DISKS; do
rm -r /tmp/$name
done
+# Try LVM if that didn't work
+if [ "$keys_found" = "no" ]; then
+ lvm lvmdiskscan
+ vgs=$(lvm vgs | tail -n +2 | awk '{ print $1 }')
+ for vg in $vgs; do
+ # Activate any VG we found
+ lvm vgchange -ay $vg
+ done
+
+ lvs=$(lvm lvs | tail -n +2 | awk '{ print "/dev/" $2 "/" $1 }')
+ for lv in $lvs; do
+ tmpdir=$(mktemp -d findkeys.XXXXXX)
+ mkdir -p /tmp/${tmpdir}
+ mount $lv /tmp/${tmpdir} || continue # Skip to next volume if this fails
+
+ # Let's see if the keys are in there
+ if [ -d /tmp/${tmpdir}/etc/ssh ]; then
+ cp -a /tmp/${tmpdir}/etc/ssh/ssh_host* /tmp/ssh/
+ keys_found="yes"
+ umount /tmp/${tmpdir}
+ break # We're done!
+ fi
+ umount /tmp/${tmpdir}
+ rm -r /tmp/${tmpdir}
+ done
+
+ # And clean up..
+ for vg in $vgs; do
+ lvm vgchange -an $vg
+ done
+fi
+
# Loop until the ssh rpm is installed
if [ "$keys_found" = "yes" ]; then
while : ; do
diff --git a/snippets/log_ks_post b/snippets/log_ks_post
new file mode 100644
index 00000000..d1df0265
--- /dev/null
+++ b/snippets/log_ks_post
@@ -0,0 +1,2 @@
+set -x -v
+exec 1>/root/ks-post.log 2>&1
diff --git a/snippets/log_ks_pre b/snippets/log_ks_pre
new file mode 100644
index 00000000..fe71c592
--- /dev/null
+++ b/snippets/log_ks_pre
@@ -0,0 +1,12 @@
+set -x -v
+exec 1>/tmp/ks-pre.log 2>&1
+
+# Once root's homedir is there, copy over the log.
+while : ; do
+ sleep 10
+ if [ -d /mnt/sysimage/root ]; then
+ cp /tmp/ks-pre.log /mnt/sysimage/root/
+ logger "Copied %pre section log to system"
+ break
+ fi
+done &
diff --git a/webui_templates/paginate.tmpl b/webui_templates/paginate.tmpl
index 66bf6345..9f5803d3 100644
--- a/webui_templates/paginate.tmpl
+++ b/webui_templates/paginate.tmpl
@@ -1,4 +1,4 @@
-#if $page > 0
+#if $pages > 0
## USAGE: # set global what="system"
## # include "/path/to/this/file"