summaryrefslogtreecommitdiffstats
path: root/cobbler
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@redhat.com>2007-11-19 14:06:29 -0500
committerMichael DeHaan <mdehaan@redhat.com>2007-11-19 14:06:29 -0500
commit7773de4b1d830f7306d2b55cd3ad9accf087d55b (patch)
tree3900a601c8d9d2953e67d14d78bddaa4d686087c /cobbler
parent6883cbe9f59f72a72001ab0862df8b78e18e632c (diff)
downloadthird_party-cobbler-7773de4b1d830f7306d2b55cd3ad9accf087d55b.tar.gz
third_party-cobbler-7773de4b1d830f7306d2b55cd3ad9accf087d55b.tar.xz
third_party-cobbler-7773de4b1d830f7306d2b55cd3ad9accf087d55b.zip
Initial bits of code to make CLI modular, and use new object system for defining new CLI commands.
Diffstat (limited to 'cobbler')
-rw-r--r--cobbler/api.py2
-rwxr-xr-xcobbler/cobbler.py782
-rw-r--r--cobbler/commands.py195
-rw-r--r--cobbler/item_distro.py9
-rw-r--r--cobbler/modules/cli_distro.py82
5 files changed, 307 insertions, 763 deletions
diff --git a/cobbler/api.py b/cobbler/api.py
index 4e26992..cecaa74 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -250,6 +250,6 @@ class BootAPI:
"""
Returns all modules in a given category, for instance "serializer", or "cli".
"""
- return get_modules_in_category(category)
+ return module_loader.get_modules_in_category(category)
diff --git a/cobbler/cobbler.py b/cobbler/cobbler.py
index 99a1935..bbfed97 100755
--- a/cobbler/cobbler.py
+++ b/cobbler/cobbler.py
@@ -19,748 +19,28 @@ import api
import os
import os.path
import traceback
-
-from cexceptions import *
+import optparse
+import commands
+import cexceptions
from rhpl.translate import _, N_, textdomain, utf8
I18N_DOMAIN = "cobbler"
-LOCKING_ENABLED = True
-LOCKFILE="/var/lib/cobbler/lock"
-
-USAGE = _("see 'man cobbler' for instructions")
+####################################################
class BootCLI:
-
- def __init__(self,args):
- """
- Build the command line parser and API instances, etc.
- """
+ def __init__(self):
textdomain(I18N_DOMAIN)
- self.args = args
self.api = api.BootAPI()
- self.commands = {}
- self.commands['distro'] = {
- 'add' : self.distro_add,
- 'edit' : self.distro_edit,
- 'copy' : self.distro_copy,
- 'rename' : self.distro_rename,
- 'delete' : self.distro_remove,
- 'remove' : self.distro_remove,
- 'list' : self.distro_list,
- 'report' : self.distro_report
- }
- self.commands['profile'] = {
- 'add' : self.profile_add,
- 'edit' : self.profile_edit,
- 'copy' : self.profile_copy,
- 'rename' : self.profile_rename,
- 'delete' : self.profile_remove,
- 'remove' : self.profile_remove,
- 'list' : self.profile_list,
- 'report' : self.profile_report
- }
- self.commands['system'] = {
- 'add' : self.system_add,
- 'edit' : self.system_edit,
- 'rename' : self.system_rename,
- 'copy' : self.system_copy,
- 'delete' : self.system_remove,
- 'remove' : self.system_remove,
- 'list' : self.system_list,
- 'report' : self.system_report
- }
- self.commands['repo'] = {
- 'auto-add' : self.repo_auto_add,
- 'add' : self.repo_add,
- 'edit' : self.repo_edit,
- 'rename' : self.repo_rename,
- 'copy' : self.repo_copy,
- 'delete' : self.repo_remove,
- 'remove' : self.repo_remove,
- 'list' : self.repo_list,
- 'report' : self.repo_report,
- 'sync' : self.reposync
- }
- self.commands['toplevel'] = {
- '-v' : self.version,
- '--version' : self.version,
- 'check' : self.check,
- 'validateks' : self.validateks,
- 'list' : self.list,
- 'report' : self.report,
- 'distros' : self.distro,
- 'distro' : self.distro,
- 'profiles' : self.profile,
- 'profile' : self.profile,
- 'systems' : self.system,
- 'system' : self.system,
- 'repos' : self.repo,
- 'repo' : self.repo,
- 'sync' : self.sync,
- 'reposync' : self.reposync,
- 'import' : self.import_tree,
- 'status' : self.status,
- 'reserialize' : self.reserialize,
- 'help' : self.usage,
- '--help' : self.usage,
- '/?' : self.usage
- }
-
- def run(self):
- """
- Run the command line and return system exit code
- """
- # deserialization is implicit with API construction
- # self.api.deserialize()
- self.relay_args(self.args[1:], self.commands['toplevel'])
-
- def usage(self,args):
- """
- Print out abbreviated help if user gives bad syntax
- """
- print USAGE
-
-
- ###########################################################
- # REPORTING FUNCTIONS
-
- def distro_report(self,args):
- if len(args) > 0:
- return self.__list_names2(self.api.distros(), args)
- else:
- return self.__print_sorted(self.api.distros())
-
- def system_report(self,args):
- if len(args) > 0:
- return self.__list_names2(self.api.systems(), args)
- else:
- return self.__print_sorted(self.api.systems())
-
- def profile_report(self,args):
- if len(args) > 0:
- return self.__list_names2(self.api.profiles(), args)
- else:
- return self.__print_sorted(self.api.profiles())
-
- def repo_report(self,args):
- if len(args) > 0:
- return self.__list_names2(self.api.repos(), args)
- else:
- return self.__print_sorted(self.api.repos())
-
- def report(self,args):
-
- args.append("") # filler
- match = False
- for a in args:
- if a == '--distros' or len(args) == 1:
- self.distro_report([])
- match = True
- if a == '--repos' or len(args) == 1:
- self.repo_report([])
- match = True
- if a == '--profiles' or len(args) == 1:
- self.profile_report([])
- match = True
- if a == '--systems' or len(args) == 1:
- self.system_report([])
- match = True
- if not match and a is not None and a != "":
- raise CX(_("cobbler does not understand '%(command)s'") % { "command" : a })
- match = False
-
- #############################################
- # LISTING FUNCTIONS
-
- def list(self,args):
- self.__tree(self.api.distros(),0)
- self.__tree(self.api.repos(),0)
-
- def __tree(self,collection,level):
- for item in collection:
- print _("%(indent)s%(type)s %(name)s") % {
- "indent" : " " * level,
- "type" : item.TYPE_NAME,
- "name" : item.name
- }
- kids = item.get_children()
- if kids is not None and len(kids) > 0:
- self.__tree(kids,level+1)
-
- def __list_names(self, collection):
- names = [ x.name for x in collection]
- names.sort() # sorted() is 2.4 only
- for name in names:
- str = _(" %(name)s") % { "name" : name }
- print str
- return True
-
- def __list_names2(self, collection, args):
- for p in args:
- obj = collection.find(name=p)
- if obj is not None:
- print obj.printable()
- return True
-
- def system_list(self, args):
- if len(args) > 0:
- self.__list_names2(self.api.systems(), args)
- else:
- return self.__list_names(self.api.systems())
-
- def distro_list(self, args):
- if len(args) > 0:
- return self.__list_names2(self.api.distros(),args)
- else:
- return self.__list_names(self.api.distros())
-
- def profile_list(self, args):
- if len(args) > 0:
- return self.__list_names2(self.api.profiles(),args)
- else:
- return self.__list_names(self.api.profiles())
-
- def repo_list(self, args):
- if len(args) > 0:
- return self.__list_names2(self.api.repos(),args)
- else:
- return self.__list_names(self.api.repos())
-
- ###############################################################
- # UTILITY FUNCTIONS
-
- def find_arg(self,haystack,needle):
- for arg in haystack:
- arg2 = arg.replace('"','')
- if arg2.startswith(needle):
- tokens = arg2.split("=")
- if len(tokens) >= 1:
- return "".join(tokens[1:])
- return None
-
- def replace_names(self,haystack,newname):
- args2 = []
- for arg in haystack:
- if arg.startswith("--name"):
- args2.append("--name=%s" % newname)
- else:
- args2.append(arg)
- return args2
-
-
- def __sorter(self, a, b):
- return cmp(a.name, b.name)
-
- def __print_sorted(self, collection):
- collection = [x for x in collection]
- collection.sort(self.__sorter)
- for x in collection:
- print x.printable()
- return True
-
- ######################################################################
- # BASIC FRAMEWORK
-
- def __generic_add(self,args,new_fn,control_fn,does_inherit):
- obj = new_fn(is_subobject=does_inherit)
- control_fn(args,obj)
-
- def __generic_edit(self,args,collection_fn,control_fn,exc_msg):
- obj = collection_fn().find(name=self.find_arg(args,"--name"))
- name2 = self.find_arg(args,"--newname")
- if name2 is not None:
- raise CX("objects cannot be renamed with the edit command, use 'rename'")
- if obj is None:
- raise CX(exc_msg)
- control_fn(args,obj)
-
- def __generic_copy(self,args,collection_fn,control_fn,exc_msg):
- obj = collection_fn().find(name=self.find_arg(args,"--name"))
- obj2 = self.find_arg(args,"--newname")
- if obj is None:
- raise CX(exc_msg)
- args = self.replace_names(args, obj2)
- obj3 = obj.make_clone()
- obj3.set_name(obj2)
- control_fn(args,obj3)
-
- def __generic_rename(self,args,collection_fn,control_fn,exc_msg):
- objname = self.find_arg(args,"--name")
- if objname is None:
- raise CX(_("at least one required parameter is missing. See 'man cobbler'."))
- objname2 = self.find_arg(args,"--newname")
- if objname2 is None:
- raise CX(_("at least one required parameter is missing. See 'man cobbler'."))
- self.__generic_copy(args,collection_fn,control_fn,exc_msg)
- if objname != objname2:
- collection_fn().remove(objname, with_delete=True)
-
- # new cobbler does not require explicit serialize calls
- # self.api.serialize()
-
- def __generic_remove(self,args,alias1,alias2,collection_fn):
- commands = {
- "--%s" % alias1 : lambda(a): collection_fn().remove(a, with_delete=True),
- "--%s" % alias2 : lambda(a): collection_fn().remove(a, with_delete=True)
- }
- on_ok = lambda: True
- return self.apply_args(args,commands,on_ok)
-
- ####################################################################
- # REMOVAL FUNCTIONS
-
- def distro_remove(self,args):
- return self.__generic_remove(args,"distro","name",self.api.distros)
-
- def profile_remove(self,args):
- return self.__generic_remove(args,"profile","name",self.api.profiles)
-
- def system_remove(self,args):
- return self.__generic_remove(args,"system","name",self.api.systems)
-
- def repo_remove(self,args):
- return self.__generic_remove(args,"repo","name",self.api.repos)
-
- ####################################################################
- # COPY FUNCTIONS
-
- def distro_copy(self,args):
- exc = _("distribution does not exist")
- self.__generic_copy(args,self.api.distros,self.__distro_control,exc)
-
- def profile_copy(self,args):
- exc = _("profile does not exist")
- self.__generic_copy(args,self.api.profiles,self.__profile_control,exc)
-
- def system_copy(self,args):
- exc = _("system does not exist")
- self.__generic_copy(args,self.api.systems,self.__system_control,exc)
-
- def repo_copy(self,args):
- exc = _("repository does not exist")
- self.__generic_copy(args,self.api.repos,self.__repo_control,exc)
-
- #####################################################################
- # RENAME FUNCTIONS
-
- def distro_rename(self,args):
- exc = _("distribution does not exist")
- self.__generic_rename(args,self.api.distros,self.__distro_control,exc)
-
- def profile_rename(self,args):
- exc = _("profile does not exist")
- self.__generic_rename(args,self.api.profiles,self.__profile_control,exc)
-
- def system_rename(self,args):
- exc = _("system does not exist")
- self.__generic_rename(args,self.api.systems,self.__system_control,exc)
-
- def repo_rename(self,args):
- exc = _("repository does not exist")
- self.__generic_rename(args,self.api.repos,self.__repo_control,exc)
-
- #####################################################################
- # EDIT FUNCTIONS
-
- def distro_edit(self,args):
- exc = _("distribution does not exist")
- self.__generic_edit(args,self.api.distros,self.__distro_control,exc)
-
- def profile_edit(self,args):
- exc = _("profile does not exist")
- self.__generic_edit(args,self.api.profiles,self.__profile_control,exc)
-
- def system_edit(self,args):
- exc = _("system does not exist")
- self.__generic_edit(args,self.api.systems,self.__system_control,exc)
-
- def repo_edit(self,args):
- exc = _("repository does not exist")
- self.__generic_edit(args,self.api.repos,self.__repo_control,exc)
-
- #####################################################################
- # ADD FUNCTIONS
-
- def __prescan_for_inheritance_args(self,args):
- """
- Normally we just feed all the arguments through to the functions
- in question, but here, we need to send a special flag to the foo_add
- functions if we are creating a subobject, because that needs to affect
- what function calls we make. So, this checks to see if the user
- is creating a subobject by looking for --inherit in the arguments list,
- before we actually parse the --inherit arg. Complicated :)
- """
- for x in args:
- try:
- key, value = x.split("=",1)
- value = value.replace('"','').replace("'",'')
- if key == "--inherit":
- return True
- except:
- pass
- return False
-
- def distro_add(self,args):
- does_inherit = self.__prescan_for_inheritance_args(args)
- self.__generic_add(args,self.api.new_distro,self.__distro_control,does_inherit)
-
- def profile_add(self,args):
- does_inherit = self.__prescan_for_inheritance_args(args)
- self.__generic_add(args,self.api.new_profile,self.__profile_control,does_inherit)
-
- def system_add(self,args):
- does_inherit = self.__prescan_for_inheritance_args(args)
- self.__generic_add(args,self.api.new_system,self.__system_control,does_inherit)
-
- def repo_auto_add(self, args):
- self.api.auto_add_repos()
-
- def repo_add(self,args):
- does_inherit = self.__prescan_for_inheritance_args(args)
- self.__generic_add(args,self.api.new_repo,self.__repo_control,does_inherit)
-
-
- ###############################################################
- # CONTROL IMPLEMENTATIONS
-
- def __profile_control(self,args,profile,newname=None):
- """
- Create/Edit a profile: 'cobbler profile edit --name='foo' ...
- """
- commands = {
- '--name' : lambda(a) : profile.set_name(a),
- '--inherit' : lambda(a) : profile.set_parent(a),
- '--newname' : lambda(a) : True,
- '--profile' : lambda(a) : profile.set_name(a),
- '--distro' : lambda(a) : profile.set_distro(a),
- '--kickstart' : lambda(a) : profile.set_kickstart(a),
- '--kick-start' : lambda(a) : profile.set_kickstart(a),
- '--answers' : lambda(a) : profile.set_kickstart(a),
- '--kopts' : lambda(a) : profile.set_kernel_options(a),
- '--virt-file-size' : lambda(a) : profile.set_virt_file_size(a),
- '--virt-ram' : lambda(a) : profile.set_virt_ram(a),
- '--virt-bridge' : lambda(a) : profile.set_virt_bridge(a),
- '--virt-cpus' : lambda(a) : profile.set_virt_cpus(a),
- '--ksmeta' : lambda(a) : profile.set_ksmeta(a),
- '--repos' : lambda(a) : profile.set_repos(a),
- '--virt-path' : lambda(a) : profile.set_virt_path(a),
- '--virt-type' : lambda(a) : profile.set_virt_type(a),
- '--dhcp-tag' : lambda(a) : profile.set_dhcp_tag(a),
- '--server-override' : lambda(a) : profile.set_server(a)
- }
- def on_ok():
- if newname is not None:
- profile.set_name(newname)
- self.api.profiles().add(profile, with_copy=True)
- return self.apply_args(args,commands,on_ok)
-
- def __repo_control(self,args,repo,newname=None):
- """
- Create/edit a repo: 'cobbler repo add --name='foo' ...
- """
- commands = {
- '--name' : lambda(a): repo.set_name(a),
- '--newname' : lambda(a): True,
- '--mirror-name' : lambda(a): repo.set_name(a),
- '--mirror' : lambda(a): repo.set_mirror(a),
- '--keep-updated' : lambda(a): repo.set_keep_updated(a),
- '--rpm-list' : lambda(a): repo.set_rpm_list(a),
- '--createrepo-flags' : lambda(a): repo.set_createrepo_flags(a),
- '--arch' : lambda(a): repo.set_arch(a)
- }
- def on_ok():
- if newname is not None:
- repo.set_name(newname)
- self.api.repos().add(repo, with_copy=True)
- return self.apply_args(args,commands,on_ok)
-
- def __distro_control(self,args,distro):
- """
- Create/Edit a distro: 'cobbler distro edit --name='foo' ...
- """
- commands = {
- '--name' : lambda(a) : distro.set_name(a),
- '--newname' : lambda(a) : True,
- '--distro' : lambda(a) : distro.set_name(a),
- '--kernel' : lambda(a) : distro.set_kernel(a),
- '--initrd' : lambda(a) : distro.set_initrd(a),
- '--kopts' : lambda(a) : distro.set_kernel_options(a),
- '--arch' : lambda(a) : distro.set_arch(a),
- '--ksmeta' : lambda(a) : distro.set_ksmeta(a),
- '--breed' : lambda(a) : distro.set_breed(a)
- }
- def on_ok():
- self.api.distros().add(distro, with_copy=True)
- return self.apply_args(args,commands,on_ok)
-
- def __system_control(self,args,sys):
- """
- Create/Edit a system: 'cobbler system edit --name='foo' ...
- """
-
- # This copy/paste is heinous evil, please forgive me.
- # there was a loop here but the python optimizers seemed to do wierd
- # things with the lambda handling. To be resolved when this
- # command line gets refactored to use optparse.
-
- commands = {
- '--name' : lambda(a) : sys.set_name(a),
- '--newname' : lambda(a) : True,
- '--system' : lambda(a) : sys.set_name(a),
- '--profile' : lambda(a) : sys.set_profile(a),
- '--kopts' : lambda(a) : sys.set_kernel_options(a),
- '--ksmeta' : lambda(a) : sys.set_ksmeta(a),
- '--hostname' : lambda(a) : sys.set_hostname(a,"intf0"),
- '--hostname0' : lambda(a) : sys.set_hostname(a,"intf0"),
- '--hostname1' : lambda(a) : sys.set_hostname(a,"intf1"),
- '--hostname2' : lambda(a) : sys.set_hostname(a,"intf2"),
- '--hostname3' : lambda(a) : sys.set_hostname(a,"intf3"),
- '--hostname4' : lambda(a) : sys.set_hostname(a,"intf4"),
- '--hostname5' : lambda(a) : sys.set_hostname(a,"intf5"),
- '--hostname6' : lambda(a) : sys.set_hostname(a,"intf6"),
- '--hostname7' : lambda(a) : sys.set_hostname(a,"intf7"),
- '--ip' : lambda(a) : sys.set_ip_address(a,"intf0"),
- '--ip0' : lambda(a) : sys.set_ip_address(a,"intf0"),
- '--ip1' : lambda(a) : sys.set_ip_address(a,"intf1"),
- '--ip2' : lambda(a) : sys.set_ip_address(a,"intf2"),
- '--ip3' : lambda(a) : sys.set_ip_address(a,"intf3"),
- '--ip4' : lambda(a) : sys.set_ip_address(a,"intf4"),
- '--ip5' : lambda(a) : sys.set_ip_address(a,"intf5"),
- '--ip6' : lambda(a) : sys.set_ip_address(a,"intf6"),
- '--ip7' : lambda(a) : sys.set_ip_address(a,"intf7"),
- '--mac' : lambda(a) : sys.set_mac_address(a,"intf0"),
- '--mac0' : lambda(a) : sys.set_mac_address(a,"intf0"),
- '--mac1' : lambda(a) : sys.set_mac_address(a,"intf1"),
- '--mac2' : lambda(a) : sys.set_mac_address(a,"intf2"),
- '--mac3' : lambda(a) : sys.set_mac_address(a,"intf3"),
- '--mac4' : lambda(a) : sys.set_mac_address(a,"intf4"),
- '--mac5' : lambda(a) : sys.set_mac_address(a,"intf5"),
- '--mac6' : lambda(a) : sys.set_mac_address(a,"intf6"),
- '--mac7' : lambda(a) : sys.set_mac_address(a,"intf7"),
- '--gateway' : lambda(a) : sys.set_gateway(a,"intf0"),
- '--gateway0' : lambda(a) : sys.set_gateway(a,"intf0"),
- '--gateway1' : lambda(a) : sys.set_gateway(a,"intf1"),
- '--gateway2' : lambda(a) : sys.set_gateway(a,"intf2"),
- '--gateway3' : lambda(a) : sys.set_gateway(a,"intf3"),
- '--gateway4' : lambda(a) : sys.set_gateway(a,"intf4"),
- '--gateway5' : lambda(a) : sys.set_gateway(a,"intf5"),
- '--gateway6' : lambda(a) : sys.set_gateway(a,"intf6"),
- '--gateway7' : lambda(a) : sys.set_gateway(a,"intf7"),
- '--subnet' : lambda(a) : sys.set_subnet(a,"intf0"),
- '--subnet0' : lambda(a) : sys.set_subnet(a,"intf1"),
- '--subnet1' : lambda(a) : sys.set_subnet(a,"intf2"),
- '--subnet2' : lambda(a) : sys.set_subnet(a,"intf3"),
- '--subnet3' : lambda(a) : sys.set_subnet(a,"intf4"),
- '--subnet4' : lambda(a) : sys.set_subnet(a,"intf5"),
- '--subnet5' : lambda(a) : sys.set_subnet(a,"intf6"),
- '--subnet6' : lambda(a) : sys.set_subnet(a,"intf7"),
- '--subnet7' : lambda(a) : sys.set_subnet(a,"intf8"),
- '--virt-bridge' : lambda(a) : sys.set_virt_bridge(a,"intf0"),
- '--virt-bridge0' : lambda(a) : sys.set_virt_bridge(a,"intf0"),
- '--virt-bridge1' : lambda(a) : sys.set_virt_bridge(a,"intf1"),
- '--virt-bridge2' : lambda(a) : sys.set_virt_bridge(a,"intf2"),
- '--virt-bridge3' : lambda(a) : sys.set_virt_bridge(a,"intf3"),
- '--virt-bridge4' : lambda(a) : sys.set_virt_bridge(a,"intf4"),
- '--virt-bridge5' : lambda(a) : sys.set_virt_bridge(a,"intf5"),
- '--virt-bridge6' : lambda(a) : sys.set_virt_bridge(a,"intf6"),
- '--virt-bridge7' : lambda(a) : sys.set_virt_bridge(a,"intf7"),
- '--dhcp-tag' : lambda(a) : sys.set_dhcp_tag(a,"intf0"),
- '--dhcp-tag0' : lambda(a) : sys.set_dhcp_tag(a,"intf0"),
- '--dhcp-tag1' : lambda(a) : sys.set_dhcp_tag(a,"intf1"),
- '--dhcp-tag2' : lambda(a) : sys.set_dhcp_tag(a,"intf2"),
- '--dhcp-tag3' : lambda(a) : sys.set_dhcp_tag(a,"intf3"),
- '--dhcp-tag4' : lambda(a) : sys.set_dhcp_tag(a,"intf4"),
- '--dhcp-tag5' : lambda(a) : sys.set_dhcp_tag(a,"intf5"),
- '--dhcp-tag6' : lambda(a) : sys.set_dhcp_tag(a,"intf6"),
- '--dhcp-tag7' : lambda(a) : sys.set_dhcp_tag(a,"intf7"),
- '--kickstart' : lambda(a) : sys.set_kickstart(a),
- '--netboot-enabled' : lambda(a) : sys.set_netboot_enabled(a),
- '--virt-path' : lambda(a) : sys.set_virt_path(a),
- '--virt-type' : lambda(a) : sys.set_virt_type(a),
- '--server-override' : lambda(a) : sys.set_server(a)
- }
-
- def on_ok():
- self.api.systems().add(sys, with_copy=True)
- return self.apply_args(args,commands,on_ok)
-
- ###################################################################################
- # PARSING FUNCTIONS
-
- def apply_args(self,args,input_routines,on_ok):
- """
- Custom CLI handling, instead of getopt/optparse.
- Parses arguments of the form --foo=bar.
- 'on_ok' is a callable that is run if all arguments are parsed
- successfully. 'input_routines' is a dispatch table of routines
- that parse the arguments. See distro_edit for an example.
- """
- if len(args) == 0:
- raise CX(_("this command requires arguments"))
- for x in args:
- try:
- key, value = x.split("=",1)
- value = value.replace('"','').replace("'",'')
- except:
- raise CX(_("Cobbler was expecting an equal sign in argument '%(argument)s'") % { "argument" : x })
- if input_routines.has_key(key):
- input_routines[key](value)
- else:
- raise CX(_("this command doesn't take an option called '%(argument)s'") % { "argument" : key })
- on_ok()
-
- # new cobbler does not require explicit serialize calls
- # self.api.serialize()
-
- def relay_args(self, args, commands):
- """
- Lookup command args[0] in the dispatch table and
- feed it the remaining args[1:-1] as arguments.
- """
- if args is None or len(args) == 0:
- print USAGE
- return True
- if args[0] in commands:
- commands[args[0]](args[1:])
- else:
- raise CX(_("Cobbler does not understand '%(command)s'") % { "command" : args[0] })
- return True
-
- ################################################
- # GENERAL FUNCTIONS
-
- def reserialize(self, args):
- """
- This command is intentionally not documented in the manpage.
- Basically it loads the cobbler config and then reserialize's it's internal state.
- It can be used for testing config format upgrades and other development related things.
- It has very little purpose in the real world.
- """
- self.api.serialize()
- return True
-
- def sync(self, args):
- """
- Sync the config file with the system config: 'cobbler sync'
- """
- self.api.sync()
- return True
-
- def reposync(self, args):
- """
- Sync the repo-specific portions of the config with the filesystem.
- 'cobbler reposync'. Intended to be run on cron.
- """
- self.api.reposync(args)
- return True
-
- def validateks(self,args):
- """
- Scan rendered kickstarts for potential errors, before actual install
- """
- return self.api.validateks()
-
- def version(self,args):
- print self.api.version()
- return True
-
- def check(self,args):
- """
- Check system for network boot decency/prereqs: 'cobbler check'
- """
- status = self.api.check()
- if len(status) == 0:
- print _("No setup problems found")
- print _("Manual review and editing of /var/lib/cobbler/settings is recommended to tailor cobbler to your particular configuration.")
- print _("Good luck.")
-
- return True
- else:
- print _("The following potential problems were detected:")
- for i,x in enumerate(status):
- print _("#%(number)d: %(problem)s") % { "number" : i, "problem" : x }
- return False
-
- def status(self,args):
- """
- Show the kickstart status for logged kickstart activity.
- 'cobbler status [--mode=text|somethingelse]'
- """
- self.mode = "text"
- if args is None or len(args) == 0:
- return self.api.status(self.mode)
- def set_mode(a):
- if a.lower in [ "text" ]:
- self.mode = a
- return True
- else:
- return False
- commands = {
- '--mode' : set_mode
- }
- def go_status():
- return self.api.status(self.mode)
- return self.apply_args(args, commands, go_status)
-
- def import_tree(self,args):
- """
- Import a directory tree and auto-create distros & profiles.
- 'cobbler
- """
- self.temp_mirror = None
- self.temp_mirror_name = None
- self.temp_network_root = None
- def set_mirror_name(a):
- self.temp_mirror_name = a
- def set_mirror(a):
- self.temp_mirror = a
- def set_network_root(a):
- self.temp_network_root = a
- def go_import():
- return self.api.import_tree(
- self.temp_mirror,
- self.temp_mirror_name,
- network_root=self.temp_network_root
- )
- commands = {
- '--path' : lambda(a): set_mirror(a),
- '--mirror' : lambda(a): set_mirror(a),
- '--mirror-name' : lambda(a): set_mirror_name(a),
- '--name' : lambda(a): set_mirror_name(a),
- '--available-as' : lambda(a): set_network_root(a)
- }
- on_ok = lambda: go_import()
- return self.apply_args(args,commands,on_ok)
-
-
- #########################################################
- # TOPLEVEL MAPPINGS
-
- def distro(self,args):
- """
- Handles any of the 'cobbler distro' subcommands
- """
- return self.relay_args(args, self.commands['distro'])
-
- def profile(self,args):
- """
- Handles any of the 'cobbler profile' subcommands
- """
- return self.relay_args(args, self.commands['profile'])
-
- def system(self,args):
- """
- Handles any of the 'cobbler system' subcommands
- """
- return self.relay_args(args, self.commands['system'])
-
- def repo(self,args):
- """
- Handles any of the 'cobbler repo' subcommands
- """
- return self.relay_args(args, self.commands['repo'])
+ self.loader = commands.FunctionLoader()
+ climods = self.api.get_modules_in_category("cli")
+ for mod in climods:
+ for fn in mod.cli_functions(self.api):
+ self.loader.add_func(fn)
+
+ def run(self,args):
+ return self.loader.run(args)
####################################################
@@ -769,33 +49,17 @@ def main():
CLI entry point
"""
exitcode = 0
- lock_hit = False
try:
- if LOCKING_ENABLED:
- if os.path.exists(LOCKFILE):
- lock_hit = True
- raise CX(_("Locked. If cobbler is currently running, wait for termination, otherwise remove /var/lib/cobbler/lock"))
- try:
- lockfile = open(LOCKFILE,"w+")
- except:
- raise CX(_("Cobbler could not create the lockfile %(lockfile)s. Are you root?") % { "lockfile" : LOCKFILE })
- lockfile.close()
- BootCLI(sys.argv).run()
- except CobblerException, exc:
- print str(exc)[1:-1] # remove framing air quotes
- exitcode = 1
- except KeyboardInterrupt:
- print _("interrupted.")
- exitcode = 1
- except Exception, other:
- traceback.print_exc()
- exitcode = 1
- if LOCKING_ENABLED and not lock_hit:
- try:
- os.remove(LOCKFILE)
- except:
- pass
- return exitcode
+ # FIXME: redo locking code?
+ return BootCLI().run(sys.argv)
+ except cexceptions.CX, exc:
+ if str(type(exc)).find("CX") != -1:
+ print str(exc)[1:-1] # remove framing air quotes
+ else:
+ (t, val, tb) = sys.exc_info()
+ print tb.extract_tb().join("\n")
+ return 1
+
if __name__ == "__main__":
sys.exit(main())
diff --git a/cobbler/commands.py b/cobbler/commands.py
new file mode 100644
index 0000000..59c1fb2
--- /dev/null
+++ b/cobbler/commands.py
@@ -0,0 +1,195 @@
+"""
+Command line handling for Cobbler.
+
+Copyright 2007, 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 optparse
+from cexceptions import *
+from rhpl.translate import _, N_, textdomain, utf8
+
+#=============================================================
+
+class FunctionLoader:
+
+ """
+ The F'n Loader controls processing of cobbler commands.
+ """
+
+ def __init__(self):
+ """
+ When constructed the loader has no functions.
+ """
+ self.functions = {}
+
+ def add_func(self, obj):
+ """
+ Adds a CobblerFunction instance to the loader.
+ """
+ self.functions[obj.command_name()] = obj
+
+ def run(self, args):
+ """
+ Runs a command line sequence through the loader.
+ """
+
+ # if no args given, show all loaded fns
+ if len(args) == 1:
+ return self.show_options()
+ called_name = args[1]
+
+ # also show avail options if command name is bogus
+ if not called_name in self.functions.keys():
+ return self.show_options()
+ fn = self.functions[called_name]
+
+ # some functions require args, if none given, show subcommands
+ if len(args) == 2:
+ no_args_rc = fn.no_args_handler()
+ if no_args_rc:
+ return True
+
+ # finally let the object parse its own args
+ loaded_ok = fn.parse_args(args)
+ if not loaded_ok:
+ raise CX(_("Invalid arguments"))
+ return fn.run()
+
+ def show_options(self):
+ """
+ Prints out all loaded functions.
+ """
+
+ print "usage:"
+ print "======"
+ for name in self.functions.keys():
+ print " cobbler %s ... | --help" % name
+
+#=============================================================
+
+class CobblerFunction:
+
+ def __init__(self,api):
+ """
+ Constructor requires a Cobbler API handle.
+ """
+ self.api = api
+
+ def no_args_handler(self):
+ """
+ Called when no additional args are given to a command. False implies
+ this is ok, returning True indicates an error condition.
+ """
+ return False
+
+ def command_name(self):
+ """
+ The name of the command, as to be entered by users.
+ """
+ return "unspecified"
+
+ def subcommands(self):
+ """
+ The names of any subcommands, such as "add", "edit", etc
+ """
+ return [ ]
+
+ def run(self):
+ """
+ Called after arguments are parsed. Return True for success.
+ """
+ return True
+
+ def add_options(self, parser, args):
+ """
+ Used by subclasses to add options. See subclasses for examples.
+ """
+ pass
+
+ def parse_args(self,args):
+ """
+ Processes arguments, called prior to run ... do not override.
+ """
+
+ accum = ""
+ for x in args[1:]:
+ if not x.startswith("-"):
+ accum = accum + "%s " % x
+ else:
+ break
+ p = optparse.OptionParser(usage="cobbler %s [ARGS]" % accum)
+ self.add_options(p, args)
+ if len(args) > 2:
+ for x in args[2:]:
+ if x.startswith("-"):
+ break
+ if x not in self.subcommands():
+ raise CX(_("Argument (%s) not recognized") % x)
+
+ (self.options, self.args) = p.parse_args(args)
+ return True
+
+ def object_manipulator_start(self,new_fn,collect_fn):
+ """
+ Boilerplate for objects that offer add/edit/delete/remove/copy functionality.
+ """
+
+ if "add" in self.args:
+ obj = new_fn()
+ else:
+ if not self.options.name:
+ raise CX(_("name is required"))
+ if "delete" in self.args:
+ collect_fn().remove(self.options.name, with_delete=True)
+ return None
+ obj = collect_fn().find(self.options.name)
+
+ if not "copy" in self.args and not "rename" in self.args and self.options.name:
+ obj.set_name(self.options.name)
+
+ return obj
+
+ def object_manipulator_finish(self,obj,collect_fn):
+ """
+ Boilerplate for objects that offer add/edit/delete/remove/copy functionality.
+ """
+
+ if "copy" in self.args or "rename" in self.args:
+ if self.options.newname:
+ obj = obj.make_clone()
+ obj.set_name(self.options.newname)
+ else:
+ raise CX(_("--newname is required"))
+
+ rc = collect_fn().add(obj, with_copy=True)
+
+ if "rename" in self.args:
+ return collect_fn().remove(self.options.name, with_delete=True)
+
+ return rc
+
+
+ def no_args_handler(self):
+
+ """
+ Used to accept/reject/explain subcommands. Do not override.
+ """
+
+ subs = self.subcommands()
+ if len(subs) == 0:
+ return False
+ for x in subs:
+ print " cobbler %s %s --help" % (self.command_name(), x)
+ return True # stop here
+
+
+
+
diff --git a/cobbler/item_distro.py b/cobbler/item_distro.py
index 60b4cfc..f52ad0b 100644
--- a/cobbler/item_distro.py
+++ b/cobbler/item_distro.py
@@ -143,9 +143,12 @@ class Distro(item.Item):
"""
# NOTE: this code does not support inheritable distros at this time.
# this is by design because inheritable distros do not make sense.
- for x in (self.name,self.kernel,self.initrd):
- if x is None:
- return False
+ if self.name is None:
+ raise CX(_("name is required"))
+ if self.kernel is None:
+ raise CX(_("kernel is required"))
+ if self.initrd is None:
+ raise CX(_("initrd is required"))
return True
def to_datastruct(self):
diff --git a/cobbler/modules/cli_distro.py b/cobbler/modules/cli_distro.py
new file mode 100644
index 0000000..dd9ba4e
--- /dev/null
+++ b/cobbler/modules/cli_distro.py
@@ -0,0 +1,82 @@
+"""
+Distro CLI module.
+
+Copyright 2007, 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 distutils.sysconfig
+import sys
+
+plib = distutils.sysconfig.get_python_lib()
+mod_path="%s/cobbler" % plib
+sys.path.insert(0, mod_path)
+
+from rhpl.translate import _, N_, textdomain, utf8
+import commands
+import cexceptions
+
+
+class DistroFunction(commands.CobblerFunction):
+
+ def command_name(self):
+ return "distro"
+
+ def subcommands(self):
+ return [ "add", "edit", "copy", "rename", "delete" ]
+
+ def add_options(self, p, args):
+ p.add_option("--name", dest="name")
+ if not "delete" in args:
+ p.add_option("--kernel", dest="kernel")
+ p.add_option("--initrd", dest="initrd")
+ p.add_option("--kopts", dest="kopts")
+ p.add_option("--ksmeta", dest="ksmeta")
+ p.add_option("--arch", dest="arch")
+ p.add_option("--breed", dest="breed")
+ if "copy" in args or "rename" in args:
+ p.add_option("--newname", dest="newname")
+
+ def run(self):
+
+ obj = self.object_manipulator_start(self.api.new_distro,self.api.distros)
+ if obj is None:
+ return True
+
+ if self.options.kernel:
+ obj.set_kernel(self.options.kernel)
+ if self.options.initrd:
+ obj.set_initrd(self.options.initrd)
+ if self.options.kopts:
+ obj.set_kernel_options(self.options.kopts)
+ if self.options.ksmeta:
+ obj.set_ksmeta(self.options.ksmeta)
+ if self.options.breed:
+ obj.set_breed(self.options.breed)
+
+ return self.object_manipulator_finish(obj, self.api.distros)
+
+
+
+########################################################
+# MODULE HOOKS
+
+def register():
+ """
+ The mandatory cobbler module registration hook.
+ """
+ return "cli"
+
+def cli_functions(api):
+ return [
+ DistroFunction(api)
+ ]
+
+