From 3fa049a27ddf44e72e1a457d4b01e4c395765638 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 18 Nov 2008 12:22:06 -0500 Subject: Add a --version command to cobbler which works by writing the file for /var/lib/cobbler/version at build time, including git hash info since the version during development may not be as definitive as the package version --- MANIFEST.in | 1 + cobbler.spec | 4 ++- cobbler/api.py | 34 +++++++++++++++--------- cobbler/commands.py | 12 ++++++--- cobbler/modules/cli_distro.py | 2 +- cobbler/modules/cli_image.py | 2 +- cobbler/modules/cli_misc.py | 59 ++++++++++++++++++++++++++++++++++++------ cobbler/modules/cli_profile.py | 4 +-- cobbler/modules/cli_repo.py | 2 +- cobbler/modules/cli_report.py | 2 +- cobbler/modules/cli_system.py | 4 +-- setup.py | 34 ++++++++++++++++++++++++ 12 files changed, 128 insertions(+), 32 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 1c75f4de..a3672670 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -12,6 +12,7 @@ include config/cobbler_hosts include config/modules.conf include config/auth.conf include config/settings +include config/version include config/users.digest include config/users.conf include config/completions diff --git a/cobbler.spec b/cobbler.spec index 09ac3b79..d8910644 100644 --- a/cobbler.spec +++ b/cobbler.spec @@ -223,6 +223,7 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT %defattr(664,root,root) %config(noreplace) /etc/cobbler/settings +/var/lib/cobbler/version %config(noreplace) /var/lib/cobbler/snippets/partition_select %config(noreplace) /var/lib/cobbler/snippets/pre_partition_select %config(noreplace) /var/lib/cobbler/snippets/main_partition_select @@ -251,9 +252,10 @@ test "x$RPM_BUILD_ROOT" != "x" && rm -rf $RPM_BUILD_ROOT %changelog -* Mon Nov 17 2008 Michael DeHaan - 1.3.2-1 +* Tue Nov 18 2008 Michael DeHaan - 1.3.2-1 - Upstream changes (see CHANGELOG) - placeholder for future test release +- packaged /var/lib/cobbler/version * Fri Nov 14 2008 Michael DeHaan - 1.3.1-1 - Upstream changes (see CHANGELOG) diff --git a/cobbler/api.py b/cobbler/api.py index 89ed49fe..84034ffa 100644 --- a/cobbler/api.py +++ b/cobbler/api.py @@ -47,7 +47,7 @@ from utils import _ import logging import time import os -import fcntl +import yaml ERROR = 100 INFO = 10 @@ -139,19 +139,29 @@ class BootAPI: else: logger("%s; %s" % (msg, str(args))) - def version(self): + def version(self, extended=False): """ What version is cobbler? - Currently checks the RPM DB, which is not perfect. - Will return "?" if not installed. - """ - self.log("version") - cmd = sub_process.Popen("/bin/rpm -q cobbler", stdout=sub_process.PIPE, shell=True) - result = cmd.communicate()[0].replace("cobbler-","") - if result.find("not installed") != -1: - return "" - tokens = result[:result.rfind("-")].split(".") - return int(tokens[0]) + 0.1 * int(tokens[1]) + 0.001 * int(tokens[2]) + + If extended == False, returns a float for backwards compatibility + + If extended == True, returns a dict: + + gitstamp -- the last git commit hash + gitdate -- the last git commit date on the builder machine + builddate -- the time of the build + version -- something like "1.3.2" + version_tuple -- something like [ 1, 3, 2 ] + """ + fd = open("/var/lib/cobbler/version") + data = yaml.load(fd.read()).next() + fd.close() + if not extended: + # for backwards compatibility and use with koan's comparisons + elems = data["version_tuple"] + return int(elems[0]) + 0.1*int(elems[1]) + 0.001*int(elems[2]) + else: + return data def clear(self): """ diff --git a/cobbler/commands.py b/cobbler/commands.py index 380f374c..98a3a3a7 100644 --- a/cobbler/commands.py +++ b/cobbler/commands.py @@ -59,8 +59,14 @@ class FunctionLoader: # if no args given, show all loaded fns if len(args) == 1: return self.show_options() + called_name = args[1].lower() + # if -v or --version, make it work + if called_name in [ "--version", "-v" ]: + called_name = "version" + args = [ "/usr/bin/cobbler", "version" ] + # also show avail options if command name is bogus if len(args) == 2 and not called_name in self.functions.keys(): @@ -161,8 +167,8 @@ class FunctionLoader: Prints out all loaded functions. """ - print "commands:" - print "=========" + print "commands: (use --help on a subcommand for usage)" + print "========" names = self.functions.keys() names.sort() @@ -254,7 +260,7 @@ class CobblerFunction: print "usage:" print "======" for x in subs: - print "cobbler %s %s [ARGS|--help]" % (self.command_name(), x) + print "cobbler %s %s [ARGS]" % (self.command_name(), x) return True (self.options, self.args) = p.parse_args(args) return True diff --git a/cobbler/modules/cli_distro.py b/cobbler/modules/cli_distro.py index ad2ea935..06d51ff7 100644 --- a/cobbler/modules/cli_distro.py +++ b/cobbler/modules/cli_distro.py @@ -35,7 +35,7 @@ import cexceptions class DistroFunction(commands.CobblerFunction): def help_me(self): - return commands.HELP_FORMAT % ("cobbler distro", " [ARGS|--help]") + return commands.HELP_FORMAT % ("cobbler distro", " [ARGS]") def command_name(self): return "distro" diff --git a/cobbler/modules/cli_image.py b/cobbler/modules/cli_image.py index 2cf39ec7..b1e3ca73 100644 --- a/cobbler/modules/cli_image.py +++ b/cobbler/modules/cli_image.py @@ -27,7 +27,7 @@ import cexceptions class ImageFunction(commands.CobblerFunction): def help_me(self): - return commands.HELP_FORMAT % ("cobbler image"," [ARGS|--help]") + return commands.HELP_FORMAT % ("cobbler image"," [ARGS]") def command_name(self): return "image" diff --git a/cobbler/modules/cli_misc.py b/cobbler/modules/cli_misc.py index a293474c..503b6c70 100644 --- a/cobbler/modules/cli_misc.py +++ b/cobbler/modules/cli_misc.py @@ -67,7 +67,7 @@ class CheckFunction(commands.CobblerFunction): class ImportFunction(commands.CobblerFunction): def help_me(self): - return HELP_FORMAT % ("cobbler import","[ARGS|--help]") + return HELP_FORMAT % ("cobbler import","[ARGS]") def command_name(self): return "import" @@ -120,7 +120,7 @@ class ReserializeFunction(commands.CobblerFunction): class ListFunction(commands.CobblerFunction): def help_me(self): - return HELP_FORMAT % ("cobbler list","[ARGS|--help]") + return HELP_FORMAT % ("cobbler list","[ARGS]") def command_name(self): return "list" @@ -151,7 +151,7 @@ class ListFunction(commands.CobblerFunction): class StatusFunction(commands.CobblerFunction): def help_me(self): - return HELP_FORMAT % ("cobbler status","[ARGS|--help]") + return HELP_FORMAT % ("cobbler status","[ARGS]") def command_name(self): return "status" @@ -177,7 +177,7 @@ class SyncFunction(commands.CobblerFunction): class RepoSyncFunction(commands.CobblerFunction): def help_me(self): - return HELP_FORMAT % ("cobbler reposync","[ARGS|--help]") + return HELP_FORMAT % ("cobbler reposync","[ARGS]") def command_name(self): return "reposync" @@ -214,7 +214,7 @@ class BuildIsoFunction(commands.CobblerFunction): p.add_option("--tempdir", dest="tempdir", help="(OPTIONAL) working directory") def help_me(self): - return HELP_FORMAT % ("cobbler buildiso","") + return HELP_FORMAT % ("cobbler buildiso","[ARGS]") def command_name(self): return "buildiso" @@ -232,7 +232,7 @@ class BuildIsoFunction(commands.CobblerFunction): class ReplicateFunction(commands.CobblerFunction): def help_me(self): - return HELP_FORMAT % ("cobbler replicate","[ARGS|--help]") + return HELP_FORMAT % ("cobbler replicate","[ARGS]") def command_name(self): return "replicate" @@ -262,7 +262,7 @@ class ReplicateFunction(commands.CobblerFunction): class AclFunction(commands.CobblerFunction): def help_me(self): - return HELP_FORMAT % ("cobbler aclsetup","[ARGS|--help]") + return HELP_FORMAT % ("cobbler aclsetup","[ARGS]") def command_name(self): return "aclsetup" @@ -281,6 +281,48 @@ class AclFunction(commands.CobblerFunction): self.options.removegroup ) +######################################################## + +class VersionFunction(commands.CobblerFunction): + + def help_me(self): + return HELP_FORMAT % ("cobbler version","") + + def command_name(self): + return "version" + + def add_options(self, p, args): + pass + + def run(self): + + # --version output format borrowed from ls, so it must be right :) + + versions = self.api.version(extended=True) + + print "cobbler %s" % versions["version"] + print "" + + # print extended info if available, which is useful for devel branch testing + print "build date : %s" % versions["builddate"] + if versions.get("gitstamp","?") != "?": + print "git hash : %s" % versions["gitstamp"] + if versions.get("gitdate", "?") != "?": + print "git date : %s" % versions["gitdate"] + + print "" + + print "Copyright (C) 2006-2008 Red Hat, Inc." + print "License GPLv2+: GNU GPL version 2 or later " + print "This is free software: you are free to change and redistribute it." + print "There is NO WARRANTY, to the extent permitted by law." + + print "" + + print "Written by Michael DeHaan." + + return True + ######################################################## # MODULE HOOKS @@ -297,7 +339,8 @@ def cli_functions(api): CheckFunction(api), ImportFunction(api), ReserializeFunction(api), ListFunction(api), StatusFunction(api), SyncFunction(api), RepoSyncFunction(api), ValidateKsFunction(api), - ReplicateFunction(api), AclFunction(api) + ReplicateFunction(api), AclFunction(api), + VersionFunction(api) ] return [] diff --git a/cobbler/modules/cli_profile.py b/cobbler/modules/cli_profile.py index 3897efe9..dfc99479 100644 --- a/cobbler/modules/cli_profile.py +++ b/cobbler/modules/cli_profile.py @@ -35,13 +35,13 @@ import cexceptions class ProfileFunction(commands.CobblerFunction): def help_me(self): - return commands.HELP_FORMAT % ("cobbler profile"," [ARGS|--help]") + return commands.HELP_FORMAT % ("cobbler profile"," [ARGS]") def command_name(self): return "profile" def subcommands(self): - return ["add","copy","dumpvars","edit","find","list","remove","rename","report","getks"] + return ["add","copy","dumpvars","edit","find","getks","list","remove","rename","report"] def add_options(self, p, args): diff --git a/cobbler/modules/cli_repo.py b/cobbler/modules/cli_repo.py index fcba2b9f..d0666c63 100644 --- a/cobbler/modules/cli_repo.py +++ b/cobbler/modules/cli_repo.py @@ -35,7 +35,7 @@ import cexceptions class RepoFunction(commands.CobblerFunction): def help_me(self): - return commands.HELP_FORMAT % ("cobbler repo"," [ARGS|--help]") + return commands.HELP_FORMAT % ("cobbler repo"," [ARGS]") def command_name(self): return "repo" diff --git a/cobbler/modules/cli_report.py b/cobbler/modules/cli_report.py index 7a3e95a9..82060dda 100644 --- a/cobbler/modules/cli_report.py +++ b/cobbler/modules/cli_report.py @@ -28,7 +28,7 @@ HELP_FORMAT = commands.HELP_FORMAT class ReportFunction(commands.CobblerFunction): def help_me(self): - return HELP_FORMAT % ("cobbler report","[ARGS|--help]") + return HELP_FORMAT % ("cobbler report","[ARGS]") def command_name(self): return "report" diff --git a/cobbler/modules/cli_system.py b/cobbler/modules/cli_system.py index 6673c15c..220822c9 100644 --- a/cobbler/modules/cli_system.py +++ b/cobbler/modules/cli_system.py @@ -35,13 +35,13 @@ from cexceptions import * class SystemFunction(commands.CobblerFunction): def help_me(self): - return commands.HELP_FORMAT % ("cobbler system"," [ARGS|--help]") + return commands.HELP_FORMAT % ("cobbler system"," [ARGS]") def command_name(self): return "system" def subcommands(self): - return ["add","copy","dumpvars","edit","find","poweroff","poweron","list","reboot","remove","rename","report","getks"] + return ["add","copy","dumpvars","edit","find","getks","poweroff","poweron","list","reboot","remove","rename","report"] def add_options(self, p, args): diff --git a/setup.py b/setup.py index 05e0ab92..f7bf3f20 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,9 @@ import os.path from distutils.core import setup, Extension import string import cobbler.yaml as yaml +import cobbler.sub_process as subprocess import Cheetah.Template as Template +import time VERSION = "1.3.2" SHORT_DESC = "Network Boot and Update Server" @@ -23,6 +25,34 @@ def templatify(template, answers, output): t = Template.Template(file=template, searchList=answers) open(output,"w").write(t.respond()) +def gen_build_version(): + fd = open(os.path.join(OUTPUT_DIR, "version"),"w+") + gitdate = "?" + gitstamp = "?" + builddate = time.asctime() + if os.path.exists(".git"): + # for builds coming from git, include the date of the last commit + cmd = subprocess.Popen(["/usr/bin/git","log"],stdout=subprocess.PIPE) + data = cmd.communicate()[0].strip() + for line in data.split("\n"): + if line.startswith("commit"): + tokens = line.split(" ",1) + gitstamp = tokens[1].strip() + if line.startswith("Date:"): + tokens = line.split(":",1) + gitdate = tokens[1].strip() + break + data = { + "gitdate" : gitdate, + "gitstamp" : gitstamp, + "builddate" : builddate, + "version" : VERSION, + "version_tuple" : [ int(x) for x in VERSION.split(".")] + } + fd.write(yaml.dump(data)) + fd.close() + + def gen_config(): defaults = {} data = yaml.loadFile(DEFAULTS).next() @@ -31,6 +61,7 @@ def gen_config(): templatify(SETTINGS_TEMPLATE, defaults, os.path.join(OUTPUT_DIR, "settings")) if __name__ == "__main__": + gen_build_version() gen_config() # docspath="share/doc/koan-%s/" % VERSION bashpath = "/etc/bash_completion.d/" @@ -142,6 +173,9 @@ if __name__ == "__main__": # backups for upgrades (backpath, []), + # for --version support across distros + (cobpath, ['config/version']), + # bootloaders and syslinux support files (cobpath, ['loaders/elilo-3.8-ia64.efi']), (cobpath, ['loaders/menu.c32']), -- cgit