summaryrefslogtreecommitdiffstats
path: root/cobbler
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@redhat.com>2006-05-05 15:41:27 -0400
committerJim Meyering <jim@meyering.net>2006-05-05 15:41:27 -0400
commit036eb874ffbaf70236427ebe7c10fb883706e291 (patch)
treecd01d8978c977f5283eb72d4fdc0fe5ad05d2026 /cobbler
parent09da6bb67e890de6dc5ba2716c79b0446f861b55 (diff)
downloadthird_party-cobbler-036eb874ffbaf70236427ebe7c10fb883706e291.tar.gz
third_party-cobbler-036eb874ffbaf70236427ebe7c10fb883706e291.tar.xz
third_party-cobbler-036eb874ffbaf70236427ebe7c10fb883706e291.zip
Interim commit while refactoring. This breaks lots of stuff.
Diffstat (limited to 'cobbler')
-rw-r--r--cobbler/api.py548
-rw-r--r--cobbler/collection.py89
-rw-r--r--cobbler/distro.py76
-rw-r--r--cobbler/distros.py26
-rw-r--r--cobbler/item.py37
-rw-r--r--cobbler/msg.py2
-rw-r--r--cobbler/profile.py158
-rw-r--r--cobbler/profiles.py28
-rw-r--r--cobbler/serializer_tools.py (renamed from cobbler/config.py)33
-rw-r--r--cobbler/sync.py81
-rw-r--r--cobbler/system.py62
-rw-r--r--cobbler/systems.py22
-rw-r--r--cobbler/util.py275
-rw-r--r--cobbler/~api.py0
14 files changed, 718 insertions, 719 deletions
diff --git a/cobbler/api.py b/cobbler/api.py
index e48fe7a..0c0141f 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -22,77 +22,73 @@ class BootAPI:
"""
Constructor...
"""
- self.last_error = ''
- self.config = config.BootConfig(self)
- self.utils = util.BootUtil(self,self.config)
# if the file already exists, load real data now
try:
- if self.config.files_exist():
- self.config.deserialize()
+ if config.files_exist():
+ config.deserialize()
except Exception, e:
# parse errors, attempt to recover
- print self.last_error
- if self.last_error == m("parse_error"):
- # the error came from /etc/cobbler.conf, and must be fixed
- # manually, CLI can't do it for policy reasons on /etc
+ print runtime.last_error()
+ if runtime.last_error() == m("parse_error"):
+ # it's bad, raise it so we can croak
raise Exception, "parse_error"
try:
- self.config.serialize()
+ config.serialize()
except:
- # shouldn't get here. File permissions issue, perhaps?
+ # it's bad, raise it so we can croak
traceback.print_exc()
raise Exception, "parse_error2"
- if not self.config.files_exist():
- self.config.serialize()
+ if not config.files_exist():
+ config.serialize()
def clear(self):
"""
Forget about current list of profiles, distros, and systems
"""
- self.config.clear()
+ config.clear()
def get_systems(self):
"""
Return the current list of systems
"""
- return self.config.get_systems()
+ return config.get_systems()
def get_profiles(self):
"""
Return the current list of profiles
"""
- return self.config.get_profiles()
+ return config.get_profiles()
def get_distros(self):
"""
Return the current list of distributions
"""
- return self.config.get_distros()
+ return config.get_distros()
def new_system(self):
"""
Return a blank, unconfigured system, unattached to a collection
"""
- return System(self,None)
+ return system.System(self,None)
def new_distro(self):
"""
Create a blank, unconfigured distro, unattached to a collection.
"""
- return Distro(self,None)
+ return distro.Distro(self,None)
def new_profile(self):
"""
Create a blank, unconfigured profile, unattached to a collection
"""
- return Profile(self,None)
+ return profile.Profile(self,None)
def check(self):
@@ -104,7 +100,7 @@ class BootAPI:
for human admins, who may, for instance, forget to properly set up
their TFTP servers for PXE, etc.
"""
- return check.BootCheck(self).run()
+ return check.bootcheck().run()
def sync(self,dry_run=True):
@@ -114,521 +110,19 @@ class BootAPI:
/tftpboot. Any operations done in the API that have not been
saved with serialize() will NOT be synchronized with this command.
"""
- self.config.deserialize();
- configurator = sync.BootSync(self)
- return configurator.sync(dry_run)
+ config.deserialize();
+ return sync.bootsync(self).sync(dry_run)
def serialize(self):
"""
Save the config file(s) to disk.
"""
- self.config.serialize()
+ config.serialize()
def deserialize(self):
"""
Load the current configuration from config file(s)
"""
- self.config.deserialize()
-
-#-----------------------------------------
-
-"""
-An Item is a serializable thing that can appear in a Collection
-"""
-class Item:
-
-
- def set_name(self,name):
- """
- All objects have names, and with the exception of System
- they aren't picky about it.
- """
- self.name = name
- return True
-
- def set_kernel_options(self,options_string):
- """
- Kernel options are a comma delimited list of key value pairs,
- like 'a=b,c=d,e=f'
- """
- self.kernel_options = options_string
- return True
-
- def to_datastruct(self):
- """
- Returns an easily-marshalable representation of the collection.
- i.e. dictionaries/arrays/scalars.
- """
- raise exceptions.NotImplementedError
-
- def is_valid(self):
- """
- The individual set_ methods will return failure if any set is
- rejected, but the is_valid method is intended to indicate whether
- the object is well formed ... i.e. have all of the important
- items been set, are they free of conflicts, etc.
- """
- return False
-
-#------------------------------------------
-
-class Distro(Item):
-
- def __init__(self,api,seed_data):
- self.api = api
- self.name = None
- self.kernel = None
- self.initrd = None
- self.kernel_options = ""
- if seed_data is not None:
- self.name = seed_data['name']
- self.kernel = seed_data['kernel']
- self.initrd = seed_data['initrd']
- self.kernel_options = seed_data['kernel_options']
-
- def set_kernel(self,kernel):
- """
- Specifies a kernel. The kernel parameter is a full path, a filename
- in the configured kernel directory (set in /etc/cobbler.conf) or a
- directory path that would contain a selectable kernel. Kernel
- naming conventions are checked, see docs in the utils module
- for find_kernel.
- """
- if self.api.utils.find_kernel(kernel):
- self.kernel = kernel
- return True
- self.api.last_error = m("no_kernel")
- return False
-
- def set_initrd(self,initrd):
- """
- Specifies an initrd image. Path search works as in set_kernel.
- File must be named appropriately.
- """
- if self.api.utils.find_initrd(initrd):
- self.initrd = initrd
- return True
- self.api.last_error = m("no_initrd")
- return False
-
- def is_valid(self):
- """
- A distro requires that the kernel and initrd be set. All
- other variables are optional.
- """
- for x in (self.name,self.kernel,self.initrd):
- if x is None: return False
- return True
-
- def to_datastruct(self):
- return {
- 'name': self.name,
- 'kernel': self.kernel,
- 'initrd' : self.initrd,
- 'kernel_options' : self.kernel_options
- }
-
- def printable(self):
- """
- Human-readable representation.
- """
- kstr = self.api.utils.find_kernel(self.kernel)
- istr = self.api.utils.find_initrd(self.initrd)
- if kstr is None:
- kstr = "%s (NOT FOUND!)" % self.kernel
- elif os.path.isdir(self.kernel):
- kstr = "%s (FOUND BY SEARCH)" % kstr
- if istr is None:
- istr = "%s (NOT FOUND)" % self.initrd
- elif os.path.isdir(self.initrd):
- istr = "%s (FOUND BY SEARCH)" % istr
- buf = ""
- buf = buf + "distro : %s\n" % self.name
- buf = buf + "kernel : %s\n" % kstr
- buf = buf + "initrd : %s\n" % istr
- buf = buf + "kernel opts : %s" % self.kernel_options
- return buf
-
-#---------------------------------------------
-
-class Profile(Item):
-
- def __init__(self,api,seed_data):
- self.api = api
- self.name = None
- self.distro = None # a name, not a reference
- self.kickstart = None
- self.kernel_options = ''
- self.xen_name = 'xenguest'
- self.xen_file_size = 5 # GB
- self.xen_ram = 2048 # MB
- self.xen_mac = ''
- self.xen_paravirt = True
- if seed_data is not None:
- self.name = seed_data['name']
- self.distro = seed_data['distro']
- self.kickstart = seed_data['kickstart']
- self.kernel_options = seed_data['kernel_options']
- self.xen_name = seed_data['xen_name']
- if not self.xen_name or self.xen_name == '':
- self.xen_name = self.name
- self.xen_ram = seed_data['xen_ram']
- self.xen_file_size = seed_data['xen_file_size']
- self.xen_mac = seed_data['xen_mac']
- self.xen_paravirt = seed_data['xen_paravirt']
-
- def set_distro(self,distro_name):
- """
- Sets the distro. This must be the name of an existing
- Distro object in the Distros collection.
- """
- if self.api.get_distros().find(distro_name):
- self.distro = distro_name
- return True
- self.last_error = m("no_distro")
- return False
-
- def set_kickstart(self,kickstart):
- """
- Sets the kickstart. This must be a NFS, HTTP, or FTP URL.
- Minor checking of the URL is performed here.
- """
- if self.api.utils.find_kickstart(kickstart):
- self.kickstart = kickstart
- return True
- self.last_error = m("no_kickstart")
- return False
-
- def set_xen_name(self,str):
- """
- For Xen only.
- Specifies what xenguest install should use for --name.
- xen-net-install may do conflict resolution, so this is mostly
- a hint... To keep the shell happy, the 'str' cannot
- contain wildcards or slashes and may be subject to some other
- untainting later.
- """
- # no slashes or wildcards
- for bad in [ '/', '*', '?' ]:
- if str.find(bad) != -1:
- return False
- self.xen_name = str
- return True
-
- def set_xen_file_size(self,num):
- """
- For Xen only.
- Specifies the size of the Xen image in gigabytes. xen-net-install
- may contain some logic to ignore 'illogical' values of this size,
- though there are no guarantees. 0 tells xen-net-install to just
- let it pick a semi-reasonable size. When in doubt, specify the
- size you want.
- """
- # num is a non-negative integer (0 means default)
- try:
- inum = int(num)
- if inum != float(num):
- return False
- self.xen_file_size = inum
- if inum >= 0:
- return True
- return False
- except:
- return False
-
- def set_xen_mac(self,mac):
- """
- For Xen only.
- Specifies the mac address (or possibly later, a range) that
- xen-net-install should try to set on the domU. Seeing these
- have a good chance of conflicting with other domU's, especially
- on a network, this setting is fairly experimental at this time.
- It's recommended that it *not* be used until we can get
- decent use cases for how this might work.
- """
- # mac needs to be in mac format AA:BB:CC:DD:EE:FF or a range
- # ranges currently *not* supported, so we'll fail them
- if self.api.utils.is_mac(mac):
- self.xen_mac = mac
- return True
- else:
- return False
-
- def set_xen_paravirt(self,truthiness):
- """
- For Xen only.
- Specifies whether the system is a paravirtualized system or not.
- For ordinary computers, you want to pick 'true'. Method accepts string
- 'true'/'false' in all cases, or Python True/False.
- """
- # truthiness needs to be True or False, or (lcased) string equivalents
- try:
- if (truthiness == False or truthiness.lower() == 'false'):
- self.xen_paravirt = False
- elif (truthiness == True or truthiness.lower() == 'true'):
- self.xen_paravirt = True
- else:
- return False
- except:
- return False
- return True
-
- def is_valid(self):
- """
- A profile only needs a name and a distro. Kickstart info,
- as well as Xen info, are optional. (Though I would say provisioning
- without a kickstart is *usually* not a good idea).
- """
- for x in (self.name, self.distro):
- if x is None:
- return False
- return True
-
- def to_datastruct(self):
- return {
- 'name' : self.name,
- 'distro' : self.distro,
- 'kickstart' : self.kickstart,
- 'kernel_options' : self.kernel_options,
- 'xen_name' : self.xen_name,
- 'xen_file_size' : self.xen_file_size,
- 'xen_ram' : self.xen_ram,
- 'xen_mac' : self.xen_mac,
- 'xen_paravirt' : self.xen_paravirt
- }
-
- def printable(self):
- buf = ""
- buf = buf + "profile : %s\n" % self.name
- buf = buf + "distro : %s\n" % self.distro
- buf = buf + "kickstart : %s\n" % self.kickstart
- buf = buf + "kernel opts : %s" % self.kernel_options
- buf = buf + "xen name : %s" % self.xen_name
- buf = buf + "xen file size : %s" % self.xen_file_size
- buf = buf + "xen ram : %s" % self.xen_ram
- buf = buf + "xen mac : %s" % self.xen_mac
- buf = buf + "xen paravirt : %s" % self.xen_paravirt
- return buf
-
-#---------------------------------------------
-
-class System(Item):
-
- def __init__(self,api,seed_data):
- self.api = api
- self.name = None
- self.profile = None # a name, not a reference
- self.kernel_options = ""
- if seed_data is not None:
- self.name = seed_data['name']
- self.profile = seed_data['profile']
- self.kernel_options = seed_data['kernel_options']
-
-
- def set_name(self,name):
- """
- A name can be a resolvable hostname (it instantly resolved and replaced with the IP),
- any legal ipv4 address, or any legal mac address. ipv6 is not supported yet but _should_ be.
- See utils.py
- """
- new_name = self.api.utils.find_system_identifier(name)
- if new_name is None or new_name == False:
- self.api.last_error = m("bad_sys_name")
- return False
- self.name = name # we check it add time, but store the original value.
- return True
-
- def set_profile(self,profile_name):
- """
- Set the system to use a certain named profile. The profile
- must have already been loaded into the Profiles collection.
- """
- if self.api.get_profiles().find(profile_name):
- self.profile = profile_name
- return True
- return False
-
- def is_valid(self):
- """
- A system is valid when it contains a valid name and a profile.
- """
- if self.name is None:
- self.api.last_error = m("bad_sys_name")
- return False
- if self.profile is None:
- return False
- return True
-
- def to_datastruct(self):
- return {
- 'name' : self.name,
- 'profile' : self.profile,
- 'kernel_options' : self.kernel_options
- }
-
- def printable(self):
- buf = ""
- buf = buf + "system : %s\n" % self.name
- buf = buf + "profile : %s\n" % self.profile
- buf = buf + "kernel opts : %s" % self.kernel_options
- return buf
-
-#--------------------------------------
-
-"""
-Base class for any serializable lists of things...
-"""
-class Collection:
- _item_factory = None
-
- def __init__(self, api, seed_data):
- """
- Constructor. Requires an API reference. seed_data
- is a hash of data to feed into the collection, that would
- come from the config file in /var.
- """
- self.api = api
- self.listing = {}
- if seed_data is not None:
- for x in seed_data:
- self.add(self._item_factory(self.api, x))
-
- def find(self,name):
- """
- Return anything named 'name' in the collection, else return None if
- no objects can be found.
- """
- if name in self.listing.keys():
- return self.listing[name]
- return None
-
-
- def to_datastruct(self):
- """
- Return datastructure representation of this collection suitable
- for feeding to a serializer (such as YAML)
- """
- return [x.to_datastruct() for x in self.listing.values()]
-
-
- def add(self,ref):
- """
- Add an object to the collection, if it's valid. Returns True
- if the object was added to the collection. Returns False if the
- object specified by ref deems itself invalid (and therefore
- won't be added to the collection).
- """
- if ref is None or not ref.is_valid():
- if self.api.last_error is None or self.api.last_error == "":
- self.api.last_error = m("bad_param")
- return False
- self.listing[ref.name] = ref
- return True
-
-
- def printable(self):
- """
- Creates a printable representation of the collection suitable
- for reading by humans or parsing from scripts. Actually scripts
- would be better off reading the YAML in the config files directly.
- """
- values = map(lambda(a): a.printable(), sorted(self.listing.values()))
- if len(values) > 0:
- return "\n\n".join(values)
- else:
- return m("empty_list")
-
- #def contents(self):
- # """
- # Access the raw contents of the collection. Classes shouldn't
- # be doing this (preferably) and should use the __iter__ interface.
- # Deprecrated.
- # """
- # return self.listing.values()
-
- def __iter__(self):
- """
- Iterator for the collection. Allows list comprehensions, etc
- """
- for a in self.listing.values():
- yield a
-
- def __len__(self):
- """
- Returns size of the collection
- """
- return len(self.listing.values())
-
-
-#--------------------------------------------
-
-"""
-A distro represents a network bootable matched set of kernels
-and initrd files
-"""
-class Distros(Collection):
- _item_factory = Distro
-
- def remove(self,name):
- """
- Remove element named 'name' from the collection
- """
- # first see if any Groups use this distro
- for k,v in self.api.get_profiles().listing.items():
- if v.distro == name:
- self.api.last_error = m("orphan_profiles")
- return False
- if self.find(name):
- del self.listing[name]
- return True
- self.api.last_error = m("delete_nothing")
- return False
-
-
-#--------------------------------------------
-
-"""
-A profile represents a distro paired with a kickstart file.
-For instance, FC5 with a kickstart file specifying OpenOffice
-might represent a 'desktop' profile. For Xen, there are many
-additional options, with client-side defaults (not kept here).
-"""
-class Profiles(Collection):
- _item_factory = Profile
-
- def remove(self,name):
- """
- Remove element named 'name' from the collection
- """
- for k,v in self.api.get_systems().listing.items():
- if v.profile == name:
- self.api.last_error = m("orphan_system")
- return False
- if self.find(name):
- del self.listing[name]
- return True
- self.api.last_error = m("delete_nothing")
- return False
-
-
-#--------------------------------------------
-
-"""
-Systems are hostnames/MACs/IP names and the associated profile
-they belong to.
-"""
-class Systems(Collection):
- _item_factory = System
-
- def remove(self,name):
- """
- Remove element named 'name' from the collection
- """
- if self.find(name):
- del self.listing[name]
- return True
- self.api.last_error = m("delete_nothing")
- return False
-
+ config.deserialize()
diff --git a/cobbler/collection.py b/cobbler/collection.py
new file mode 100644
index 0000000..196bf7d
--- /dev/null
+++ b/cobbler/collection.py
@@ -0,0 +1,89 @@
+
+"""
+Base class for any serializable lists of things...
+"""
+class Collection:
+ _item_factory = None
+
+ def __init__(self):
+ """
+ Constructor. Requires an API reference. seed_data
+ is a hash of data to feed into the collection, that would
+ come from the config file in /var.
+ """
+ self.listing = {}
+ # no longer done at construct time, use from_datastruct
+ #if seed_data is not None:
+ # for x in seed_data:
+ # self.add(self._item_factory(self.api, x))
+
+ def find(self,name):
+ """
+ Return anything named 'name' in the collection, else return None if
+ no objects can be found.
+ """
+ if name in self.listing.keys():
+ return self.listing[name]
+ return None
+
+
+ def to_datastruct(self):
+ """
+ Return datastructure representation of this collection suitable
+ for feeding to a serializer (such as YAML)
+ """
+ return [x.to_datastruct() for x in self.listing.values()]
+
+
+ def from_datastruct(self,datastruct):
+ for x in datastruct:
+ self._item_factory(x)
+
+ def add(self,ref):
+ """
+ Add an object to the collection, if it's valid. Returns True
+ if the object was added to the collection. Returns False if the
+ object specified by ref deems itself invalid (and therefore
+ won't be added to the collection).
+ """
+ if ref is None or not ref.is_valid():
+ if runtime.last_error() is None or runtime.last_error() == "":
+ runtime.set_error("bad_param")
+ return False
+ self.listing[ref.name] = ref
+ return True
+
+
+ def printable(self):
+ """
+ Creates a printable representation of the collection suitable
+ for reading by humans or parsing from scripts. Actually scripts
+ would be better off reading the YAML in the config files directly.
+ """
+ values = map(lambda(a): a.printable(), sorted(self.listing.values()))
+ if len(values) > 0:
+ return "\n\n".join(values)
+ else:
+ return m("empty_list")
+
+ #def contents(self):
+ # """
+ # Access the raw contents of the collection. Classes shouldn't
+ # be doing this (preferably) and should use the __iter__ interface.
+ # Deprecrated.
+ # """
+ # return self.listing.values()
+
+ def __iter__(self):
+ """
+ Iterator for the collection. Allows list comprehensions, etc
+ """
+ for a in self.listing.values():
+ yield a
+
+ def __len__(self):
+ """
+ Returns size of the collection
+ """
+ return len(self.listing.values())
+
diff --git a/cobbler/distro.py b/cobbler/distro.py
new file mode 100644
index 0000000..ddfed2b
--- /dev/null
+++ b/cobbler/distro.py
@@ -0,0 +1,76 @@
+class Distro(Item):
+
+ def __init__(self,seed_data):
+ self.name = None
+ self.kernel = None
+ self.initrd = None
+ self.kernel_options = ""
+ if seed_data is not None:
+ self.name = seed_data['name']
+ self.kernel = seed_data['kernel']
+ self.initrd = seed_data['initrd']
+ self.kernel_options = seed_data['kernel_options']
+
+ def set_kernel(self,kernel):
+ """
+ Specifies a kernel. The kernel parameter is a full path, a filename
+ in the configured kernel directory (set in /etc/cobbler.conf) or a
+ directory path that would contain a selectable kernel. Kernel
+ naming conventions are checked, see docs in the utils module
+ for find_kernel.
+ """
+ if utils.find_kernel(kernel):
+ self.kernel = kernel
+ return True
+ runtime.set_error("no_kernel")
+ return False
+
+ def set_initrd(self,initrd):
+ """
+ Specifies an initrd image. Path search works as in set_kernel.
+ File must be named appropriately.
+ """
+ if utils.find_initrd(initrd):
+ self.initrd = initrd
+ return True
+ runtime.set_error("no_initrd")
+ return False
+
+ def is_valid(self):
+ """
+ A distro requires that the kernel and initrd be set. All
+ other variables are optional.
+ """
+ for x in (self.name,self.kernel,self.initrd):
+ if x is None: return False
+ return True
+
+ def to_datastruct(self):
+ return {
+ 'name': self.name,
+ 'kernel': self.kernel,
+ 'initrd' : self.initrd,
+ 'kernel_options' : self.kernel_options
+ }
+
+ def printable(self):
+ """
+ Human-readable representation.
+ """
+ kstr = utils.find_kernel(self.kernel)
+ istr = utils.find_initrd(self.initrd)
+ if kstr is None:
+ kstr = "%s (NOT FOUND!)" % self.kernel
+ elif os.path.isdir(self.kernel):
+ kstr = "%s (FOUND BY SEARCH)" % kstr
+ if istr is None:
+ istr = "%s (NOT FOUND)" % self.initrd
+ elif os.path.isdir(self.initrd):
+ istr = "%s (FOUND BY SEARCH)" % istr
+ buf = ""
+ buf = buf + "distro : %s\n" % self.name
+ buf = buf + "kernel : %s\n" % kstr
+ buf = buf + "initrd : %s\n" % istr
+ buf = buf + "kernel opts : %s" % self.kernel_options
+ return buf
+
diff --git a/cobbler/distros.py b/cobbler/distros.py
new file mode 100644
index 0000000..a6b4b14
--- /dev/null
+++ b/cobbler/distros.py
@@ -0,0 +1,26 @@
+import distro
+import runtime
+import profiles
+
+"""
+A distro represents a network bootable matched set of kernels
+and initrd files
+"""
+class Distros(Collection):
+ _item_factory = distro.Distro
+
+ def remove(self,name):
+ """
+ Remove element named 'name' from the collection
+ """
+ # first see if any Groups use this distro
+ for k,v in profile.profiles().listing.items():
+ if v.distro == name:
+ runtime.set_error("orphan_files")
+ return False
+ if self.find(name):
+ del self.listing[name]
+ return True
+ runtime.set_error("delete_nothing")
+ return False
+
diff --git a/cobbler/item.py b/cobbler/item.py
new file mode 100644
index 0000000..e30ada5
--- /dev/null
+++ b/cobbler/item.py
@@ -0,0 +1,37 @@
+"""
+An Item is a serializable thing that can appear in a Collection
+"""
+class Item:
+
+
+ def set_name(self,name):
+ """
+ All objects have names, and with the exception of System
+ they aren't picky about it.
+ """
+ self.name = name
+ return True
+
+ def set_kernel_options(self,options_string):
+ """
+ Kernel options are a comma delimited list of key value pairs,
+ like 'a=b,c=d,e=f'
+ """
+ self.kernel_options = options_string
+ return True
+
+ def to_datastruct(self):
+ """
+ Returns an easily-marshalable representation of the collection.
+ i.e. dictionaries/arrays/scalars.
+ """
+ raise exceptions.NotImplementedError
+
+ def is_valid(self):
+ """
+ The individual set_ methods will return failure if any set is
+ rejected, but the is_valid method is intended to indicate whether
+ the object is well formed ... i.e. have all of the important
+ items been set, are they free of conflicts, etc.
+ """
+ return False
diff --git a/cobbler/msg.py b/cobbler/msg.py
index 1eeb8e6..a9e1676 100644
--- a/cobbler/msg.py
+++ b/cobbler/msg.py
@@ -63,5 +63,5 @@ def m(key):
# localization could use different tables or just gettext.
return msg_table[key]
else:
- return "?%s?" % key
+ return key
diff --git a/cobbler/profile.py b/cobbler/profile.py
new file mode 100644
index 0000000..19db0e8
--- /dev/null
+++ b/cobbler/profile.py
@@ -0,0 +1,158 @@
+class Profile(Item):
+
+ def __init__(self,api,seed_data):
+ self.api = api
+ self.name = None
+ self.distro = None # a name, not a reference
+ self.kickstart = None
+ self.kernel_options = ''
+ self.xen_name = 'xenguest'
+ self.xen_file_size = 5 # GB
+ self.xen_ram = 2048 # MB
+ self.xen_mac = ''
+ self.xen_paravirt = True
+ if seed_data is not None:
+ self.name = seed_data['name']
+ self.distro = seed_data['distro']
+ self.kickstart = seed_data['kickstart']
+ self.kernel_options = seed_data['kernel_options']
+ self.xen_name = seed_data['xen_name']
+ if not self.xen_name or self.xen_name == '':
+ self.xen_name = self.name
+ self.xen_ram = seed_data['xen_ram']
+ self.xen_file_size = seed_data['xen_file_size']
+ self.xen_mac = seed_data['xen_mac']
+ self.xen_paravirt = seed_data['xen_paravirt']
+
+ def set_distro(self,distro_name):
+ """
+ Sets the distro. This must be the name of an existing
+ Distro object in the Distros collection.
+ """
+ if self.api.get_distros().find(distro_name):
+ self.distro = distro_name
+ return True
+ self.last_error = m("no_distro")
+ return False
+
+ def set_kickstart(self,kickstart):
+ """
+ Sets the kickstart. This must be a NFS, HTTP, or FTP URL.
+ Minor checking of the URL is performed here.
+ """
+ if self.api.utils.find_kickstart(kickstart):
+ self.kickstart = kickstart
+ return True
+ self.last_error = m("no_kickstart")
+ return False
+
+ def set_xen_name(self,str):
+ """
+ For Xen only.
+ Specifies what xenguest install should use for --name.
+ xen-net-install may do conflict resolution, so this is mostly
+ a hint... To keep the shell happy, the 'str' cannot
+ contain wildcards or slashes and may be subject to some other
+ untainting later.
+ """
+ # no slashes or wildcards
+ for bad in [ '/', '*', '?' ]:
+ if str.find(bad) != -1:
+ return False
+ self.xen_name = str
+ return True
+
+ def set_xen_file_size(self,num):
+ """
+ For Xen only.
+ Specifies the size of the Xen image in gigabytes. xen-net-install
+ may contain some logic to ignore 'illogical' values of this size,
+ though there are no guarantees. 0 tells xen-net-install to just
+ let it pick a semi-reasonable size. When in doubt, specify the
+ size you want.
+ """
+ # num is a non-negative integer (0 means default)
+ try:
+ inum = int(num)
+ if inum != float(num):
+ return False
+ self.xen_file_size = inum
+ if inum >= 0:
+ return True
+ return False
+ except:
+ return False
+
+ def set_xen_mac(self,mac):
+ """
+ For Xen only.
+ Specifies the mac address (or possibly later, a range) that
+ xen-net-install should try to set on the domU. Seeing these
+ have a good chance of conflicting with other domU's, especially
+ on a network, this setting is fairly experimental at this time.
+ It's recommended that it *not* be used until we can get
+ decent use cases for how this might work.
+ """
+ # mac needs to be in mac format AA:BB:CC:DD:EE:FF or a range
+ # ranges currently *not* supported, so we'll fail them
+ if self.api.utils.is_mac(mac):
+ self.xen_mac = mac
+ return True
+ else:
+ return False
+
+ def set_xen_paravirt(self,truthiness):
+ """
+ For Xen only.
+ Specifies whether the system is a paravirtualized system or not.
+ For ordinary computers, you want to pick 'true'. Method accepts string
+ 'true'/'false' in all cases, or Python True/False.
+ """
+ # truthiness needs to be True or False, or (lcased) string equivalents
+ try:
+ if (truthiness == False or truthiness.lower() == 'false'):
+ self.xen_paravirt = False
+ elif (truthiness == True or truthiness.lower() == 'true'):
+ self.xen_paravirt = True
+ else:
+ return False
+ except:
+ return False
+ return True
+
+ def is_valid(self):
+ """
+ A profile only needs a name and a distro. Kickstart info,
+ as well as Xen info, are optional. (Though I would say provisioning
+ without a kickstart is *usually* not a good idea).
+ """
+ for x in (self.name, self.distro):
+ if x is None:
+ return False
+ return True
+
+ def to_datastruct(self):
+ return {
+ 'name' : self.name,
+ 'distro' : self.distro,
+ 'kickstart' : self.kickstart,
+ 'kernel_options' : self.kernel_options,
+ 'xen_name' : self.xen_name,
+ 'xen_file_size' : self.xen_file_size,
+ 'xen_ram' : self.xen_ram,
+ 'xen_mac' : self.xen_mac,
+ 'xen_paravirt' : self.xen_paravirt
+ }
+
+ def printable(self):
+ buf = ""
+ buf = buf + "profile : %s\n" % self.name
+ buf = buf + "distro : %s\n" % self.distro
+ buf = buf + "kickstart : %s\n" % self.kickstart
+ buf = buf + "kernel opts : %s" % self.kernel_options
+ buf = buf + "xen name : %s" % self.xen_name
+ buf = buf + "xen file size : %s" % self.xen_file_size
+ buf = buf + "xen ram : %s" % self.xen_ram
+ buf = buf + "xen mac : %s" % self.xen_mac
+ buf = buf + "xen paravirt : %s" % self.xen_paravirt
+ return buf
diff --git a/cobbler/profiles.py b/cobbler/profiles.py
new file mode 100644
index 0000000..c73e8c3
--- /dev/null
+++ b/cobbler/profiles.py
@@ -0,0 +1,28 @@
+import profile
+import runtime
+
+#--------------------------------------------
+
+"""
+A profile represents a distro paired with a kickstart file.
+For instance, FC5 with a kickstart file specifying OpenOffice
+might represent a 'desktop' profile. For Xen, there are many
+additional options, with client-side defaults (not kept here).
+"""
+class Profiles(Collection):
+ _item_factory = profile.Profile
+
+ def remove(self,name):
+ """
+ Remove element named 'name' from the collection
+ """
+ for k,v in self.api.get_systems().listing.items():
+ if v.profile == name:
+ runtime.set_error("orphan_system")
+ return False
+ if self.find(name):
+ del self.listing[name]
+ return True
+ runtime.set_error("delete_nothing")
+ return False
+
diff --git a/cobbler/config.py b/cobbler/serializer_tools.py
index ac08ca5..7c32ce5 100644
--- a/cobbler/config.py
+++ b/cobbler/serializer_tools.py
@@ -6,9 +6,12 @@
import api
import util
from msg import *
+import distros
+import profiles
+import systems
+
import weakref
import syck # pysyck > 0.61, so it has dump()
-
import os
import traceback
@@ -18,7 +21,14 @@ global_state_file = "/var/lib/cobbler/cobbler.conf"
class BootConfig:
- def __init__(self,api):
+ __instance
+
+ def __new__(typ, *args, **kwargs):
+ if __instance is None:
+ __instance = object.__new__(type,*args,**kwargs)
+ return __instance
+
+ def __init__(self):
"""
Constructor. This class maintains both the logical
configuration for Cobbler and the file representation thereof.
@@ -26,7 +36,6 @@ class BootConfig:
users of this class need to call deserialize() to load config
file values. See cobbler.py for how the CLI does it.
"""
- self.api = weakref.proxy(api)
self.settings_file = global_settings_file
self.state_file = global_state_file
self.set_defaults()
@@ -42,9 +51,9 @@ class BootConfig:
"""
Establish an empty list of profiles distros, and systems.
"""
- self.profiles = api.Profiles(self.api,None)
- self.distros = api.Distros(self.api,None)
- self.systems = api.Systems(self.api,None)
+ profiles.clear()
+ distros.clear()
+ systems.clear()
def set_defaults(self):
"""
@@ -135,9 +144,9 @@ class BootConfig:
if is_etc:
self.config_from_hash(hash['config'])
else:
- self.distros = api.Distros(self.api, hash['distros'])
- self.profiles = api.Profiles(self.api, hash['profiles'])
- self.systems = api.Systems(self.api, hash['systems'])
+ distros.from_datastruct(hash['distros'])
+ profiles.from_datastruct(hash['profiles'])
+ systems.from_datastruct(hash['systems'])
# ------------------------------------------------------
# we don't care about file formats until below this line
@@ -163,7 +172,7 @@ class BootConfig:
try:
state = open(self.state_file,"w+")
except:
- self.api.last_error = m("cant_create: %s" % self.state_file)
+ runtime.set_error(m("cant_create: %s" % self.state_file))
return False
data = self.to_hash(False)
state.write(syck.dump(data))
@@ -188,7 +197,7 @@ class BootConfig:
self.last_error = m("parse_error")
raise Exception("parse_error")
except:
- self.api.last_error = m("parse_error")
+ runtime.set_error("parse_error")
raise Exception("parse_error")
# -----
@@ -201,7 +210,7 @@ class BootConfig:
self.last_error = m("parse_error2")
raise Exception("parse_error2")
except:
- self.api.last_error = m("parse_error2")
+ runtime.set_error("parse_error2")
raise Exception("parse_error2")
# all good
diff --git a/cobbler/sync.py b/cobbler/sync.py
index f555b4f..31ab481 100644
--- a/cobbler/sync.py
+++ b/cobbler/sync.py
@@ -21,8 +21,7 @@ Handles conversion of internal state to the tftpboot tree layout
class BootSync:
- def __init__(self,api):
- self.api = weakref.proxy(api)
+ def __init__(self):
self.verbose = True
@@ -49,7 +48,7 @@ class BootSync:
"""
Copy syslinux to the configured tftpboot directory
"""
- self.copy(self.api.config.pxelinux, os.path.join(self.api.config.tftpboot, "pxelinux.0"))
+ self.copy(config.config().pxelinux, os.path.join(config.config().tftpboot, "pxelinux.0"))
def configure_httpd(self):
"""
@@ -72,7 +71,7 @@ class BootSync:
Allow from all
</Directory>
"""
- config.replace("/tftpboot",self.api.config.tftpboot)
+ config.replace("/tftpboot",config.config().tftpboot)
self.tee(f, config)
self.close_file(f)
@@ -81,7 +80,7 @@ class BootSync:
Delete any previously built pxelinux.cfg tree and xen tree info.
"""
for x in ["pxelinux.cfg","images","systems","distros","profiles","kickstarts"]:
- path = os.path.join(self.api.config.tftpboot,x)
+ path = os.path.join(config.config().tftpboot,x)
self.rmtree(path, True)
self.mkdir(path)
@@ -94,19 +93,19 @@ class BootSync:
mounted.
"""
# copy is a 4-letter word but tftpboot runs chroot, thus it's required.
- distros = os.path.join(self.api.config.tftpboot, "images")
- for d in self.api.get_distros():
+ distros = os.path.join(config.config().tftpboot, "images")
+ for d in distros.get_distros():
distro_dir = os.path.join(distros,d.name)
self.mkdir(distro_dir)
- kernel = self.api.utils.find_kernel(d.kernel) # full path
- initrd = self.api.utils.find_initrd(d.initrd) # full path
+ kernel = utils.find_kernel(d.kernel) # full path
+ initrd = utils.find_initrd(d.initrd) # full path
if kernel is None or not os.path.isfile(kernel):
- self.api.last_error = "Kernel for distro (%s) cannot be found and needs to be fixed: %s" % (d.name, d.kernel)
- print self.api.last_error
- raise "error"
+ runtime.set_error("Kernel for distro (%s) cannot be found and needs to be fixed: %s" % (d.name, d.kernel))
+ print runtime.last_error()
+ raise Exception("error")
if initrd is None or not os.path.isfile(initrd):
- self.api.last_error = "Initrd for distro (%s) cannot be found and needs to be fixed: %s" % (d.name, d.initrd)
- raise "error"
+ runtime.last_error("Initrd for distro (%s) cannot be found and needs to be fixed: %s" % (d.name, d.initrd))
+ raise Exception("error")
b_kernel = os.path.basename(kernel)
b_initrd = os.path.basename(initrd)
self.copyfile(kernel, os.path.join(distro_dir, b_kernel))
@@ -124,18 +123,18 @@ class BootSync:
# these are served by either NFS, Apache, or some ftpd, so we don't need to copy them
# it's up to the user to make sure they are nicely served by their URLs
- for g in self.api.get_profiles():
+ for g in profiles.get_profiles():
self.sync_log("mirroring any local kickstarts: %s" % g.name)
- kickstart_path = self.api.utils.find_kickstart(g.kickstart)
+ kickstart_path = utils.find_kickstart(g.kickstart)
if kickstart_path and os.path.exists(kickstart_path):
# the input is an *actual* file, hence we have to copy it
- copy_path = os.path.join(self.api.config.tftpboot, "kickstarts", g.name)
+ copy_path = os.path.join(config.config().tftpboot, "kickstarts", g.name)
self.mkdir(copy_path)
dest = os.path.join(copy_path, "ks.cfg")
try:
self.copyfile(g.kickstart, dest)
except:
- self.api.last_error = m("err_kickstart2") % (g.kickstart,dest)
+ runtime.set_error("err_kickstart2")
raise "error"
def build_trees(self):
@@ -148,16 +147,16 @@ class BootSync:
print "building trees..."
# create pxelinux.cfg under tftpboot
# and file for each MAC or IP (hex encoded 01-XX-XX-XX-XX-XX-XX)
- systems = self.api.get_systems()
- profiles = self.api.get_profiles()
- distros = self.api.get_distros()
+ systems = systems.get_systems()
+ profiles = profiles.get_profiles()
+ distros = distros.get_distros()
for d in distros:
self.sync_log("processing distro: %s" % d.name)
# TODO: add check to ensure all distros have profiles (=warning)
- filename = os.path.join(self.api.config.tftpboot,"distros",d.name)
+ filename = os.path.join(config.config().tftpboot,"distros",d.name)
d.kernel_options = self.blend_kernel_options((
- self.api.config.kernel_options,
+ config.config().kernel_options,
d.kernel_options
))
self.write_distro_file(filename,d)
@@ -166,11 +165,11 @@ class BootSync:
self.sync_log("processing profile: %s" % p.name)
# TODO: add check to ensure all profiles have distros (=error)
# TODO: add check to ensure all profiles have systems (=warning)
- filename = os.path.join(self.api.config.tftpboot,"profiles",p.name)
- distro = self.api.get_distros().find(p.distro)
+ filename = os.path.join(config.config().tftpboot,"profiles",p.name)
+ distro = distros.get_distros().find(p.distro)
if distro is not None:
p.kernel_options = self.blend_kernel_options((
- self.api.config.kernel_options,
+ config.config().kernel_options,
distro.kernel_options,
p.kernel_options
))
@@ -180,15 +179,15 @@ class BootSync:
self.sync_log("processing system: %s" % system.name)
profile = profiles.find(system.profile)
if profile is None:
- self.api.last_error = m("orphan_profile2")
+ runtime.set_error("orphan_profile2")
raise "error"
distro = distros.find(profile.distro)
if distro is None:
- self.api.last_error = m("orphan_system2")
+ runtime.set_error("orphan_system2")
raise "error"
f1 = self.get_pxelinux_filename(system.name)
- f2 = os.path.join(self.api.config.tftpboot, "pxelinux.cfg", f1)
- f3 = os.path.join(self.api.config.tftpboot, "systems", f1)
+ f2 = os.path.join(config.config().tftpboot, "pxelinux.cfg", f1)
+ f3 = os.path.join(config.config().tftpboot, "systems", f1)
self.write_pxelinux_file(f2,system,profile,distro)
self.write_system_file(f3,system)
@@ -201,13 +200,13 @@ class BootSync:
is either a system name, an IP, or the MAC, so figure it out, resolve
the host if needed, and return the pxelinux directory name.
"""
- name = self.api.utils.find_system_identifier(name_input)
- if self.api.utils.is_ip(name):
+ name = utils.find_system_identifier(name_input)
+ if utils.is_ip(name):
return IPy.IP(name).strHex()[2:]
- elif self.api.utils.is_mac(name):
+ elif utils.is_mac(name):
return "01-" + "-".join(name.split(":")).lower()
else:
- self.api.last_error = m("err_resolv") % name
+ runtime.set_error(m("err_resolv" % name)
raise "error"
@@ -229,7 +228,7 @@ class BootSync:
self.tee(fd,"label linux\n")
self.tee(fd," kernel %s\n" % kernel_path)
kopts = self.blend_kernel_options((
- self.api.config.kernel_options,
+ config.config().kernel_options,
profile.kernel_options,
distro.kernel_options,
system.kernel_options
@@ -239,7 +238,7 @@ class BootSync:
# if kickstart path is local, we've already copied it into
# the HTTP mirror, so make it something anaconda can get at
if kickstart_path.startswith("/"):
- kickstart_path = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (self.api.config.server, profile.name)
+ kickstart_path = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (config.config().server, profile.name)
nextline = nextline + " ks=%s" % kickstart_path
self.tee(fd, nextline)
self.close_file(fd)
@@ -252,8 +251,8 @@ class BootSync:
"""
fd = self.open_file(filename,"w+")
# resolve to current values
- distro.kernel = self.api.utils.find_kernel(distro.kernel)
- distro.initrd = self.api.utils.find_initrd(distro.initrd)
+ distro.kernel = utils.find_kernel(distro.kernel)
+ distro.initrd = utils.find_initrd(distro.initrd)
self.tee(fd,syck.dump(distro.to_datastruct()))
self.close_file(fd)
@@ -266,7 +265,7 @@ class BootSync:
# if kickstart path is local, we've already copied it into
# the HTTP mirror, so make it something anaconda can get at
if profile.kickstart and profile.kickstart.startswith("/"):
- profile.kickstart = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (self.api.config.server, profile.name)
+ profile.kickstart = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (config.config().server, profile.name)
self.tee(fd,syck.dump(profile.to_datastruct()))
self.close_file(fd)
@@ -386,4 +385,8 @@ class BootSync:
# end result is a new fragment of a kernel options string
return " ".join(results)
+__instance = BootSync()
+
+def bootsync()
+ return __instance
diff --git a/cobbler/system.py b/cobbler/system.py
new file mode 100644
index 0000000..dc1a2f7
--- /dev/null
+++ b/cobbler/system.py
@@ -0,0 +1,62 @@
+
+class System(Item):
+
+ def __init__(self,api,seed_data):
+ self.api = api
+ self.name = None
+ self.profile = None # a name, not a reference
+ self.kernel_options = ""
+ if seed_data is not None:
+ self.name = seed_data['name']
+ self.profile = seed_data['profile']
+ self.kernel_options = seed_data['kernel_options']
+
+
+ def set_name(self,name):
+ """
+ A name can be a resolvable hostname (it instantly resolved and replaced with the IP),
+ any legal ipv4 address, or any legal mac address. ipv6 is not supported yet but _should_ be.
+ See utils.py
+ """
+ new_name = self.api.utils.find_system_identifier(name)
+ if new_name is None or new_name == False:
+ self.api.last_error = m("bad_sys_name")
+ return False
+ self.name = name # we check it add time, but store the original value.
+ return True
+
+ def set_profile(self,profile_name):
+ """
+ Set the system to use a certain named profile. The profile
+ must have already been loaded into the Profiles collection.
+ """
+ if self.api.get_profiles().find(profile_name):
+ self.profile = profile_name
+ return True
+ return False
+
+ def is_valid(self):
+ """
+ A system is valid when it contains a valid name and a profile.
+ """
+ if self.name is None:
+ self.api.last_error = m("bad_sys_name")
+ return False
+ if self.profile is None:
+ return False
+ return True
+
+ def to_datastruct(self):
+ return {
+ 'name' : self.name,
+ 'profile' : self.profile,
+ 'kernel_options' : self.kernel_options
+ }
+
+ def printable(self):
+ buf = ""
+ buf = buf + "system : %s\n" % self.name
+ buf = buf + "profile : %s\n" % self.profile
+ buf = buf + "kernel opts : %s" % self.kernel_options
+ return buf
+
diff --git a/cobbler/systems.py b/cobbler/systems.py
new file mode 100644
index 0000000..47dc042
--- /dev/null
+++ b/cobbler/systems.py
@@ -0,0 +1,22 @@
+import system
+import runtime
+
+#--------------------------------------------
+
+"""
+Systems are hostnames/MACs/IP names and the associated profile
+they belong to.
+"""
+class Systems(Collection):
+ _item_factory = system.System
+
+ def remove(self,name):
+ """
+ Remove element named 'name' from the collection
+ """
+ if self.find(name):
+ del self.listing[name]
+ return True
+ runtime.set_error("delete_nothing")
+ return False
+
diff --git a/cobbler/util.py b/cobbler/util.py
index 3a4d852..ed18869 100644
--- a/cobbler/util.py
+++ b/cobbler/util.py
@@ -10,148 +10,143 @@ import socket
import glob
import weakref
-class BootUtil:
-
- def __init__(self,api,config):
- # BootAPI is the preferred holder of all references
- self.api = weakref.proxy(api)
- self.config = weakref.proxy(config)
- self.re_kernel = re.compile(r'vmlinuz-(\d+)\.(\d+)\.(\d+)-(.*)')
- self.re_initrd = re.compile(r'initrd-(\d+)\.(\d+)\.(\d+)-(.*).img')
-
-
- def find_system_identifier(self,strdata):
- """
- If the input is a MAC or an IP, return that.
- If it's not, resolve the hostname and return the IP.
- pxelinux doesn't work in hostnames
- """
- if self.is_mac(strdata):
- return strdata
- if self.is_ip(strdata):
- return strdata
- return self.resolve_ip(strdata)
-
-
- def is_ip(self,strdata):
- """
- Return whether the argument is an IP address. ipv6 needs
- to be added...
- """
- # needs testcase
- if re.search(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}',strdata):
- return True
- return False
-
-
- def is_mac(self,strdata):
- """
- Return whether the argument is a mac address.
- """
- # needs testcase
- if re.search(r'[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F:0-9]{2}:[A-F:0-9]{2}',strdata):
- return True
- return False
-
-
- def resolve_ip(self,strdata):
- """
- Resolve the IP address and handle errors...
- """
- try:
- return socket.gethostbyname(strdata)
- except:
- return None
-
-
- def find_matching_files(self,directory,regex):
- """
- Find all files in a given directory that match a given regex.
- Can't use glob directly as glob doesn't take regexen.
- """
- files = glob.glob(os.path.join(directory,"*"))
- results = []
- for f in files:
- if regex.match(os.path.basename(f)):
- results.append(f)
- return results
-
-
- def find_highest_files(self,directory,unversioned,regex):
- """
- Find the highest numbered file (kernel or initrd numbering scheme)
- in a given directory that matches a given pattern. Used for
- auto-booting the latest kernel in a directory.
- """
- files = self.find_matching_files(directory, regex)
- get_numbers = re.compile(r'(\d+).(\d+).(\d+)')
- def max2(a, b):
- """Returns the larger of the two values"""
- av = get_numbers.search(os.path.basename(a)).groups()
- bv = get_numbers.search(os.path.basename(b)).groups()
-
- ret = cmp(av[0], bv[0]) or cmp(av[1], bv[1]) or cmp(av[2], bv[2])
- if ret < 0:
- return b
- return a
-
- if len(files) > 0:
- return reduce(max2, files)
-
- # couldn't find a highest numbered file, but maybe there
- # is just a 'vmlinuz' or an 'initrd.img' in this directory?
- last_chance = os.path.join(directory,unversioned)
- if os.path.exists(last_chance):
- return last_chance
+re_kernel = re.compile(r'vmlinuz-(\d+)\.(\d+)\.(\d+)-(.*)')
+re_initrd = re.compile(r'initrd-(\d+)\.(\d+)\.(\d+)-(.*).img')
+
+
+def find_system_identifier(self,strdata):
+ """
+ If the input is a MAC or an IP, return that.
+ If it's not, resolve the hostname and return the IP.
+ pxelinux doesn't work in hostnames
+ """
+ if self.is_mac(strdata):
+ return strdata
+ if self.is_ip(strdata):
+ return strdata
+ return self.resolve_ip(strdata)
+
+
+def is_ip(self,strdata):
+ """
+ Return whether the argument is an IP address. ipv6 needs
+ to be added...
+ """
+ # needs testcase
+ if re.search(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}',strdata):
+ return True
+ return False
+
+
+def is_mac(self,strdata):
+ """
+ Return whether the argument is a mac address.
+ """
+ # needs testcase
+ if re.search(r'[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F:0-9]{2}:[A-F:0-9]{2}',strdata):
+ return True
+ return False
+
+
+def resolve_ip(self,strdata):
+ """
+ Resolve the IP address and handle errors...
+ """
+ try:
+ return socket.gethostbyname(strdata)
+ except:
+ return None
+
+
+def find_matching_files(self,directory,regex):
+ """
+ Find all files in a given directory that match a given regex.
+ Can't use glob directly as glob doesn't take regexen.
+ """
+ files = glob.glob(os.path.join(directory,"*"))
+ results = []
+ for f in files:
+ if regex.match(os.path.basename(f)):
+ results.append(f)
+ return results
+
+
+def find_highest_files(self,directory,unversioned,regex):
+ """
+ Find the highest numbered file (kernel or initrd numbering scheme)
+ in a given directory that matches a given pattern. Used for
+ auto-booting the latest kernel in a directory.
+ """
+ files = self.find_matching_files(directory, regex)
+ get_numbers = re.compile(r'(\d+).(\d+).(\d+)')
+ def max2(a, b):
+ """Returns the larger of the two values"""
+ av = get_numbers.search(os.path.basename(a)).groups()
+ bv = get_numbers.search(os.path.basename(b)).groups()
+
+ ret = cmp(av[0], bv[0]) or cmp(av[1], bv[1]) or cmp(av[2], bv[2])
+ if ret < 0:
+ return b
+ return a
+
+ if len(files) > 0:
+ return reduce(max2, files)
+
+ # couldn't find a highest numbered file, but maybe there
+ # is just a 'vmlinuz' or an 'initrd.img' in this directory?
+ last_chance = os.path.join(directory,unversioned)
+ if os.path.exists(last_chance):
+ return last_chance
+ return None
+
+
+def find_kernel(self,path):
+ """
+ Given a directory or a filename, find if the path can be made
+ to resolve into a kernel, and return that full path if possible.
+ """
+ if os.path.isfile(path):
+ filename = os.path.basename(path)
+ if self.re_kernel.match(filename):
+ return path
+ elif filename == "vmlinuz":
+ return path
+ elif os.path.isdir(path):
+ return self.find_highest_files(path,"vmlinuz",self.re_kernel)
return None
- def find_kernel(self,path):
- """
- Given a directory or a filename, find if the path can be made
- to resolve into a kernel, and return that full path if possible.
- """
- if os.path.isfile(path):
- filename = os.path.basename(path)
- if self.re_kernel.match(filename):
- return path
- if filename == "vmlinuz":
- return path
- elif os.path.isdir(path):
- return self.find_highest_files(path,"vmlinuz",self.re_kernel)
- return None
-
-
- def find_initrd(self,path):
- """
- Given a directory or a filename, see if the path can be made
- to resolve into an intird, return that full path if possible.
- """
- # FUTURE: add another function to see if kernel and initrd have matched numbers (and throw a warning?)
- if os.path.isfile(path):
- filename = os.path.basename(path)
- if self.re_initrd.match(filename):
- return path
- if filename == "initrd.img" or filename == "initrd":
- return path
- elif os.path.isdir(path):
- return self.find_highest_files(path,"initrd.img",self.re_initrd)
- return None
-
+def find_initrd(self,path):
+ """
+ Given a directory or a filename, see if the path can be made
+ to resolve into an intird, return that full path if possible.
+ """
+ # FUTURE: try to match kernel/initrd pairs?
+ if os.path.isfile(path):
+ filename = os.path.basename(path)
+ if self.re_initrd.match(filename):
+ return path
+ if filename == "initrd.img" or filename == "initrd":
+ return path
+ elif os.path.isdir(path):
+ return self.find_highest_files(path,"initrd.img",self.re_initrd)
+ return None
+
+
+def find_kickstart(self,url):
+ """
+ Check if a kickstart url looks like an http, ftp, nfs or local path.
+ If a local path is used, cobbler will copy the kickstart and serve
+ it over http.
+ """
+ if url is None:
+ return None
+ x = url.lower()
+ for y in ["http://","nfs://","ftp://","/"]:
+ if x.startswith(y):
+ if x.startswith("/") and not os.path.isfile(x):
+ return None
+ return url
+ return None
- def find_kickstart(self,url):
- """
- Check if a kickstart url looks like an http, ftp, nfs or local path.
- If a local path is used, cobbler will copy the kickstart and serve
- it over http.
- """
- if url is None:
- return None
- x = url.lower()
- for y in ["http://","nfs://","ftp://","/"]:
- if x.startswith(y):
- if x.startswith("/") and not os.path.isfile(x):
- return None
- return url
- return None
diff --git a/cobbler/~api.py b/cobbler/~api.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cobbler/~api.py