summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael DeHaan <mdehaan@redhat.com>2009-01-13 15:18:54 -0500
committerMichael DeHaan <mdehaan@redhat.com>2009-01-13 15:18:54 -0500
commit0c0439d0a5e6f9b736f4dec60436799257e7dfde (patch)
treee63922ffb9e2f500ea00224ffac72e8bf7d16156
parent26d002f29b1473472fc231d2856d6710e273194f (diff)
downloadcobbler-0c0439d0a5e6f9b736f4dec60436799257e7dfde.tar.gz
cobbler-0c0439d0a5e6f9b736f4dec60436799257e7dfde.tar.xz
cobbler-0c0439d0a5e6f9b736f4dec60436799257e7dfde.zip
Bill Peck's patch adding a new power type.
-rw-r--r--cobbler/action_import.py135
-rw-r--r--cobbler/action_power.py1
-rw-r--r--cobbler/action_reposync.py18
-rw-r--r--cobbler/action_sync.py3
-rw-r--r--cobbler/config.py7
-rw-r--r--cobbler/item_image.py21
-rw-r--r--cobbler/item_profile.py6
-rw-r--r--cobbler/item_system.py8
-rw-r--r--cobbler/modules/cli_system.py2
-rw-r--r--cobbler/pxegen.py227
-rw-r--r--cobbler/settings.py3
-rw-r--r--cobbler/test_basic.py11
-rw-r--r--cobbler/utils.py44
-rw-r--r--setup.py1
-rw-r--r--triggers/restart-services.trigger5
-rw-r--r--webui_templates/system_edit.tmpl2
16 files changed, 464 insertions, 30 deletions
diff --git a/cobbler/action_import.py b/cobbler/action_import.py
index 07fce390..24738cb0 100644
--- a/cobbler/action_import.py
+++ b/cobbler/action_import.py
@@ -104,7 +104,7 @@ class Importer:
if self.kickstart_file and not self.breed:
raise CX(_("Kickstart file can only be specified when a specific breed is selected"))
- if self.breed and self.breed.lower() not in [ "redhat", "debian", "ubuntu" ]:
+ if self.breed and self.breed.lower() not in [ "redhat", "debian", "ubuntu", "windows" ]:
raise CX(_("Supplied import breed is not supported"))
# if --arch is supplied, make sure the user is not importing a path with a different
@@ -376,10 +376,10 @@ class Importer:
"""
This is an os.path.walk routine that looks for potential yum repositories
- to be added to the configuration for post-install usage.
+ to be added to the configuration for post-install usage.
"""
- matches = {}
+ matches = {}
for x in fnames:
if x == "base" or x == "repodata":
print "- processing repo at : %s" % dirname
@@ -398,7 +398,7 @@ class Importer:
# =======================================================================================
-
+
def process_comps_file(self, comps_path, distro):
"""
@@ -448,9 +448,9 @@ class Importer:
fname = os.path.join(self.settings.webdir, "ks_mirror", "config", "%s-%s.repo" % (distro.name, counter))
repo_url = "http://@@http_server@@/cobbler/ks_mirror/config/%s-%s.repo" % (distro.name, counter)
-
- repo_url2 = "http://@@http_server@@/cobbler/ks_mirror/%s" % (urlseg)
-
+
+ repo_url2 = "http://@@http_server@@/cobbler/ks_mirror/%s" % (urlseg)
+
distro.source_repos.append([repo_url,repo_url2])
# NOTE: the following file is now a Cheetah template, so it can be remapped
@@ -487,29 +487,33 @@ class Importer:
except:
print _("- error launching createrepo, ignoring...")
traceback.print_exc()
-
+
# ========================================================================
def distro_adder(self,foo,dirname,fnames):
-
+
"""
This is an os.path.walk routine that finds distributions in the directory
to be scanned and then creates them.
"""
- # FIXME: If there are more than one kernel or initrd image on the same directory,
+ # FIXME: If there are more than one kernel or initrd image on the same directory,
# results are unpredictable
initrd = None
kernel = None
-
+
+ # Windows Variables
+ startrom = None
+ setupldr = None
+
for x in fnames:
fullname = os.path.join(dirname,x)
if os.path.islink(fullname) and os.path.isdir(fullname):
if fullname.startswith(self.path):
- # Prevent infinite loop with Sci Linux 5
+ # Prevent infinite loop with Sci Linux 5
print "- warning: avoiding symlink loop"
continue
print "- following symlink: %s" % fullname
@@ -519,6 +523,10 @@ class Importer:
initrd = os.path.join(dirname,x)
if ( x.startswith("vmlinuz") or x.startswith("kernel.img") ) and x.find("initrd") == -1:
kernel = os.path.join(dirname,x)
+ if x.lower().startswith("startrom.n1_"):
+ startrom = os.path.join(dirname,x)
+ if x.lower().startswith("setupldr.ex_"):
+ setupldr = os.path.join(dirname,x)
if initrd is not None and kernel is not None and dirname.find("isolinux") == -1:
adtl = self.add_entry(dirname,kernel,initrd)
if adtl != None:
@@ -526,6 +534,10 @@ class Importer:
# Not resetting these values causes problems importing debian media because there are remaining items in fnames
initrd = None
kernel = None
+ elif startrom is not None and setupldr is not None:
+ self.add_win_entry(dirname,startrom,setupldr)
+ startrom = None
+ setupldr = None
# ========================================================================
@@ -649,6 +661,86 @@ class Importer:
# ========================================================================
+ def add_win_entry(self, dirname, startrom, setupldr):
+
+ proposed_name = self.get_proposed_name(dirname)
+ proposed_arch = self.get_proposed_arch(dirname)
+ if self.arch and proposed_arch and self.arch != proposed_arch:
+ raise CX(_("Arch from pathname (%s) does not match with supplied one %s")%(proposed_arch,self.arch))
+
+ importer = import_factory(dirname,self.path)
+ if self.breed and self.breed != importer.breed:
+ raise CX( _("Requested breed (%s); breed found is %s") % ( self.breed , breed ) )
+
+ #archs = importer.learn_arch_from_tree()
+ #if self.arch and self.arch not in archs:
+ # raise CX(_("Given arch (%s) not found on imported tree %s")%(self.arch,importer.get_pkgdir()))
+ #if proposed_arch:
+ # if proposed_arch not in archs:
+ # print _("Warning: arch from pathname (%s) not found on imported tree %s") % (proposed_arch,importer.get_pkgdir())
+ # return
+ #
+ # archs = [ proposed_arch ]
+
+ archs = [ proposed_arch ]
+
+ if len(archs)>1:
+ print _("- Warning : Multiple archs found : %s") % (archs)
+
+ distros_added = []
+
+ for pxe_arch in archs:
+
+ name = proposed_name + "-" + pxe_arch
+ existing_distro = self.distros.find(name=name)
+
+ if existing_distro is not None:
+ print _("- warning: skipping import, as distro name already exists: %s") % name
+ continue
+
+ else:
+ print _("- creating new distro: %s") % name
+ distro = self.config.new_distro()
+
+ distro.set_name(name)
+ distro.set_kernel(startrom)
+ distro.set_initrd(setupldr)
+ distro.set_arch(pxe_arch)
+ distro.set_breed(importer.breed)
+ distro.source_repos = []
+
+ self.distros.add(distro,save=True)
+ distros_added.append(distro)
+
+ existing_profile = self.profiles.find(name=name)
+
+ # see if the profile name is already used, if so, skip it and
+ # do not modify the existing profile
+
+ if existing_profile is None:
+ print _("- creating new profile: %s") % name
+ profile = self.config.new_profile()
+ else:
+ print _("- skipping existing profile, name already exists: %s") % name
+ continue
+
+ # save our minimal profile which just points to the distribution and a good
+ # default answer file
+
+ profile.set_name(name)
+ profile.set_distro(name)
+ if self.kickstart_file:
+ profile.set_kickstart(self.kickstart_file)
+
+ # save our new profile to the collection
+
+ self.profiles.add(profile,save=True)
+
+ self.api.serialize()
+ return distros_added
+
+ # ========================================================================
+
def get_proposed_name(self,dirname):
"""
@@ -737,6 +829,7 @@ def guess_breed(kerneldir,path):
[ 'Fedora' , "redhat" ],
[ 'Server' , "redhat" ],
[ 'Client' , "redhat" ],
+ [ 'setup.exe' , "windows" ],
]
guess = None
@@ -789,6 +882,8 @@ def import_factory(kerneldir,path):
return DebianImporter(rootdir)
elif breed == "ubuntu":
return UbuntuImporter(rootdir)
+ elif breed == "windows":
+ return WindowsImporter(rootdir)
elif breed:
raise CX(_("Unknown breed %s")%breed)
else:
@@ -1160,3 +1255,19 @@ class UbuntuImporter ( DebianImporter ) :
return os_version , "/var/lib/cobbler/kickstarts/sample.seed"
+
+class WindowsImporter( BaseImporter ):
+ def __init__(self,(rootdir,pkgdir)):
+ self.breed = "windows"
+ self.rootdir = rootdir
+ self.pkgdir = "i386"
+
+ def set_variance(self, flavor, major, minor, arch):
+ # TODO: figure out how to determine if this media is SP1/SP2/etc.
+ # Assuming no service pack for now (SP zero?)
+
+ if flavor == "XP":
+ return "SP0", "/var/lib/cobbler/kickstarts/winxp-default.sif"
+
+ def match_kernelarch_file(self, filename):
+ return True
diff --git a/cobbler/action_power.py b/cobbler/action_power.py
index 9db4535c..8f92cc58 100644
--- a/cobbler/action_power.py
+++ b/cobbler/action_power.py
@@ -137,6 +137,7 @@ class PowerTool:
"lpar" : os.path.join(powerdir,"power_lpar.template"),
"bladecenter": os.path.join(powerdir,"power_bladecenter.template"),
"virsh" : os.path.join(powerdir,"power_virsh.template"),
+ "integrity" : os.path.join(powerdir,"power_integrity.template"),
}
result = map.get(self.system.power_type, "")
diff --git a/cobbler/action_reposync.py b/cobbler/action_reposync.py
index 4bb484c5..14ef1112 100644
--- a/cobbler/action_reposync.py
+++ b/cobbler/action_reposync.py
@@ -345,6 +345,24 @@ class RepoSync:
if rc !=0:
raise CX(_("cobbler reposync failed"))
+ repodata_path = os.path.join(dest_path, "repodata")
+
+ if not os.path.exists("/usr/bin/wget"):
+ raise CX(_("no /usr/bin/wget found, please install wget"))
+
+ cmd2 = "/usr/bin/wget -q %s/repodata/comps.xml -O /dev/null" % (repo_mirror)
+ rc = sub_process.call(cmd2, shell=True, close_fds=True)
+ if rc == 0:
+ if not os.path.isdir(repodata_path):
+ os.makedirs(repodata_path)
+
+ cmd2 = "/usr/bin/wget -q %s/repodata/comps.xml -O %s/comps.xml" % (repo_mirror, repodata_path)
+ print _("- %s") % cmd2
+
+ rc = sub_process.call(cmd2, shell=True, close_fds=True)
+ if rc !=0:
+ raise CX(_("wget failed"))
+
# now run createrepo to rebuild the index
if repo.mirror_locally:
diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py
index 4b334044..1ba91656 100644
--- a/cobbler/action_sync.py
+++ b/cobbler/action_sync.py
@@ -116,7 +116,7 @@ class BootSync:
if self.verbose:
print "- copying images"
self.pxegen.copy_images()
-
+ self.pxegen.generate_windows_files()
for x in self.systems:
if self.verbose:
print "- copying files for system: %s" % x.name
@@ -136,6 +136,7 @@ class BootSync:
if self.verbose:
print "- generating PXE menu structure"
self.pxegen.make_pxe_menu()
+ self.pxegen.write_tftpd_rules(True)
# run post-triggers
if self.verbose:
diff --git a/cobbler/config.py b/cobbler/config.py
index ec43f4a9..78a4d7ca 100644
--- a/cobbler/config.py
+++ b/cobbler/config.py
@@ -24,6 +24,7 @@ import os
import weakref
import time
import random
+import string
import binascii
import item_distro as distro
@@ -87,6 +88,12 @@ class Config:
data = "%s%s" % (time.time(), random.uniform(1,9999999))
return binascii.b2a_base64(data).replace("=","").strip()
+ def generate_random_id(self,length=8):
+ """
+ Return a random string using ASCII 0..9 and A..z
+ """
+ return string.join(random.Random().sample(string.letters+string.digits, length),'')
+
def __cmp(self,a,b):
return cmp(a.name,b.name)
diff --git a/cobbler/item_image.py b/cobbler/item_image.py
index b11dc9c0..5a4123b2 100644
--- a/cobbler/item_image.py
+++ b/cobbler/item_image.py
@@ -119,26 +119,33 @@ class Image(item.Item):
* hostname:/path/to/the/filename.ext
* /path/to/the/filename.ext
"""
- print "STARTING WITH FILENAME: %s" % filename
uri = ""
- auth = hostname = path = ""
+ scheme = auth = hostname = path = ""
# we'll discard the protocol if it's supplied, for legacy support
if filename.find("://") != -1:
- ignored, uri = filename.split("://")
+ scheme, uri = filename.split("://")
filename = uri
else:
uri = filename
if filename.find("@") != -1:
auth, filename = filename.split("@")
+ # extract the hostname
+ # 1. if we have a colon, then everything before it is a hostname
+ # 2. if we don't have a colon, then check if we had a scheme; if
+ # we did, then grab all before the first forward slash as the
+ # hostname; otherwise, we've got a bad file
if filename.find(":") != -1:
hostname, filename = filename.split(":")
elif filename[0] != '/':
- index = filename.find("/")
- hostname = filename[:index]
- filename = filename[index:]
+ if len(scheme) > 0:
+ index = filename.find("/")
+ hostname = filename[:index]
+ filename = filename[index:]
+ else:
+ raise CX(_("invalid file: %s" % filename))
# raise an exception if we don't have a valid path
- if filename[0] != '/':
+ if len(filename) > 0 and filename[0] != '/':
raise CX(_("file contains an invalid path: %s" % filename))
if filename.find("/") != -1:
path, filename = filename.rsplit("/", 1)
diff --git a/cobbler/item_profile.py b/cobbler/item_profile.py
index cdc8fce2..d30eb52a 100644
--- a/cobbler/item_profile.py
+++ b/cobbler/item_profile.py
@@ -44,6 +44,7 @@ class Profile(item.Item):
"""
self.name = None
self.uid = ""
+ self.random_id = ""
self.owners = self.settings.default_ownership
self.distro = (None, '<<inherit>>')[is_subobject]
self.enable_menu = (self.settings.enable_menu, '<<inherit>>')[is_subobject]
@@ -132,6 +133,10 @@ class Profile(item.Item):
if self.uid == '':
self.uid = self.config.generate_uid()
+ self.random_id = self.load_item(seed_data,'random_id','')
+ if self.random_id == '' or len(self.random_id) != 4:
+ self.random_id = self.config.generate_random_id(4)
+
return self
def set_parent(self,parent_name):
@@ -300,6 +305,7 @@ class Profile(item.Item):
'mtime' : self.mtime,
'name_servers' : self.name_servers,
'uid' : self.uid,
+ 'random_id' : self.random_id,
'redhat_management_key' : self.redhat_management_key
}
diff --git a/cobbler/item_system.py b/cobbler/item_system.py
index a4cbd2a5..4939fa02 100644
--- a/cobbler/item_system.py
+++ b/cobbler/item_system.py
@@ -65,6 +65,7 @@ class System(item.Item):
self.ctime = 0
self.mtime = 0
self.uid = ""
+ self.random_id = ""
self.power_type = self.settings.power_management_default_type
self.power_address = ""
self.power_user = ""
@@ -197,6 +198,10 @@ interface.
self.uid = self.load_item(seed_data,'uid','')
if self.uid == '':
self.uid = self.config.generate_uid()
+
+ self.random_id = self.load_item(seed_data,'random_id','')
+ if self.random_id == '' or len(self.random_id) != 4:
+ self.random_id = self.config.generate_random_id(4)
# power management integration features
@@ -592,7 +597,7 @@ interface.
if power_type is None:
power_type = ""
power_type = power_type.lower()
- valid = "bullpap wti apc_snmp ether-wake ipmilan drac ipmitool ilo rsai lpar bladecenter virsh none"
+ valid = "bullpap wti apc_snmp ether-wake ipmilan drac ipmitool ilo rsai lpar bladecenter virsh integrity none"
choices = valid.split(" ")
choices.sort()
if power_type not in choices:
@@ -632,6 +637,7 @@ interface.
return {
'name' : self.name,
'uid' : self.uid,
+ 'random_id' : self.random_id,
'kernel_options' : self.kernel_options,
'kernel_options_post' : self.kernel_options_post,
'depth' : self.depth,
diff --git a/cobbler/modules/cli_system.py b/cobbler/modules/cli_system.py
index 4805f9c9..0c6499d7 100644
--- a/cobbler/modules/cli_system.py
+++ b/cobbler/modules/cli_system.py
@@ -101,7 +101,7 @@ class SystemFunction(commands.CobblerFunction):
if not self.matches_args(args,["dumpvars","remove","report","getks","list"]):
p.add_option("--power-pass", dest="power_pass", help="password for power management interface")
if not self.matches_args(args,["dumpvars","poweron","poweroff","reboot","remove","report","getks","list"]):
- p.add_option("--power-type", dest="power_type", help="one of: none, apc_snmp, bullpap, drac, ether-wake, ilo, ipmilan, ipmitool, wti, lpar, bladecenter, virsh")
+ p.add_option("--power-type", dest="power_type", help="one of: none, apc_snmp, bullpap, drac, ether-wake, ilo, ipmilan, ipmitool, wti, lpar, bladecenter, virsh, integrity")
if not self.matches_args(args,["dumpvars","remove","report","getks","list"]):
p.add_option("--power-user", dest="power_user", help="username for power management interface, if required")
diff --git a/cobbler/pxegen.py b/cobbler/pxegen.py
index a55f7c6a..7d256d95 100644
--- a/cobbler/pxegen.py
+++ b/cobbler/pxegen.py
@@ -29,6 +29,8 @@ import sys
import glob
import traceback
import errno
+import sub_process
+import string
import utils
from cexceptions import *
@@ -109,6 +111,9 @@ class PXEGen:
"""
errors = list()
for d in self.distros:
+ # we don't copy the files for windows distros
+ if d.breed == "windows":
+ continue
try:
if self.verbose:
print "- copying files for distro: %s" % d.name
@@ -174,6 +179,149 @@ class PXEGen:
utils.linkfile(filename, newfile, api=self.api, verbose=self.verbose)
return True
+ def generate_windows_files(self):
+ utils.mkdir("/tftpboot/profiles")
+ for p in self.profiles:
+ distro = p.get_conceptual_parent()
+ if distro and distro.breed != "windows":
+ continue
+ else:
+ self.generate_windows_profile_pxe(p)
+ utils.mkdir("/tftpboot/systems")
+ for s in self.systems:
+ profile = s.get_conceptual_parent()
+ if profile:
+ distro = profile.get_conceptual_parent()
+ if distro and distro.breed != "windows":
+ continue
+ else:
+ self.generate_windows_system_pxe(s)
+
+ def generate_windows_profile_pxe(self, profile):
+ distro = profile.get_conceptual_parent()
+
+ dest_dir = os.path.join("/tftpboot/profiles", profile.name)
+ utils.mkdir(dest_dir)
+
+ utils.cabextract(distro.kernel, dest_dir, self.api)
+
+ src_file = os.path.join(dest_dir, "startrom.n12")
+ dest_file = os.path.join(dest_dir, "winpxe.0")
+ cmd = [ "/bin/sed", "-i", "-e", "s/ntldr/L%s/gi" % profile.random_id, src_file ]
+ sub_process.call(cmd, shell=False, close_fds=False)
+ os.rename(src_file, dest_file)
+
+ utils.cabextract(distro.initrd, dest_dir, self.api)
+
+ src_file = os.path.join(dest_dir, "setupldr.exe")
+ dest_file = os.path.join(dest_dir, "NTLDR")
+ cmd = [ "/bin/sed", "-i", "-e", "s/winnt\\.sif/w%s.sif/gi" % profile.random_id, src_file ]
+ sub_process.call(cmd, shell=False, close_fds=False)
+ cmd = [ "/bin/sed", "-i", "-e", "s/ntdetect\\.com/ntd_%s.com/gi" % profile.random_id, src_file ]
+ sub_process.call(cmd, shell=False, close_fds=False)
+ os.rename(src_file, dest_file)
+
+ src_dir = os.path.dirname(distro.kernel)
+ src_file = os.path.join(src_dir, "ntdetect.com")
+ file_name = os.path.join(dest_dir, "ntdetect.com")
+ utils.copyfile(src_file, file_name, self.api)
+
+ template = profile.kickstart
+ fd = open(template, "r")
+ template_data = fd.read()
+ fd.close()
+
+ blended = utils.blender(self.api, False, profile)
+ blended['next_server'] = self.settings.next_server
+
+ ksmeta = blended.get("ks_meta",{})
+ del blended["ks_meta"]
+ blended.update(ksmeta) # make available at top level
+
+ # this is a workaround for a dumb bug in cheetah...
+ # a backslash before a variable is always treated as
+ # escaping the $, so things are not rendered correctly.
+ # The only option is to use another variable for
+ # backslashes when leading another variable. i.e.:
+ # \\$variable
+ blended['bsp'] = '\\'
+
+ data = self.templar.render(template_data, blended, None)
+
+ # write .sif to /tftpboot/profiles/$name/winnt.sif
+ file_name = os.path.join(dest_dir, "winnt.sif")
+ fd = open(file_name, "w")
+ fd.write(data)
+ fd.close()
+
+ return True
+
+ def generate_windows_system_pxe(self, system):
+ profile = system.get_conceptual_parent()
+ if not profile:
+ return False
+
+ distro = profile.get_conceptual_parent()
+
+ dest_dir = os.path.join("/tftpboot/systems", system.name)
+ utils.mkdir(dest_dir)
+
+ utils.cabextract(distro.kernel, dest_dir, self.api)
+
+ src_file = os.path.join(dest_dir, "startrom.n12")
+ dest_file = os.path.join(dest_dir, "winpxe.0")
+ cmd = [ "/bin/sed", "-i", "-e", "s/ntldr/L%s/gi" % system.random_id, src_file ]
+ sub_process.call(cmd, shell=False, close_fds=False)
+ os.rename(src_file, dest_file)
+
+ utils.cabextract(distro.initrd, dest_dir, self.api)
+
+ src_file = os.path.join(dest_dir, "setupldr.exe")
+ dest_file = os.path.join(dest_dir, "NTLDR")
+ cmd = [ "/bin/sed", "-i", "-e", "s/winnt\\.sif/w%s.sif/gi" % system.random_id, src_file ]
+ sub_process.call(cmd, shell=False, close_fds=False)
+ cmd = [ "/bin/sed", "-i", "-e", "s/ntdetect\\.com/ntd_%s.com/gi" % system.random_id, src_file ]
+ sub_process.call(cmd, shell=False, close_fds=False)
+ os.rename(src_file, dest_file)
+
+ src_dir = os.path.dirname(distro.kernel)
+ src_file = os.path.join(src_dir, "ntdetect.com")
+ file_name = os.path.join(dest_dir, "ntdetect.com")
+ utils.copyfile(src_file, file_name, self.api)
+
+ template = system.kickstart
+ if template == "<<inherit>>":
+ template = profile.kickstart
+
+ fd = open(template, "r")
+ template_data = fd.read()
+ fd.close()
+
+ blended = utils.blender(self.api, False, system)
+ blended['next_server'] = self.settings.next_server
+
+ ksmeta = blended.get("ks_meta",{})
+ del blended["ks_meta"]
+ blended.update(ksmeta) # make available at top level
+
+ # this is a workaround for a dumb bug in cheetah...
+ # a backslash before a variable is always treated as
+ # escaping the $, so things are not rendered correctly.
+ # The only option is to use another variable for
+ # backslashes when leading another variable. i.e.:
+ # \\$variable
+ blended['bsp'] = '\\'
+
+ data = self.templar.render(template_data, blended, None)
+
+ # write .sif to /tftpboot/systems/$name/winnt.sif
+ file_name = os.path.join(dest_dir, "winnt.sif")
+ fd = open(file_name, "w")
+ fd.write(data)
+ fd.close()
+
+ return True
+
def write_all_system_files(self,system):
profile = system.get_conceptual_parent()
@@ -308,7 +456,7 @@ class PXEGen:
distro = profile.get_conceptual_parent()
# xen distros can be ruled out as they won't boot
if distro.name.find("-xen") != -1:
- # can't PXE Xen
+ # can't PXE Xen
continue
contents = self.write_pxe_file(None,None,profile,distro,distro.arch,include_header=False)
if contents is not None:
@@ -400,8 +548,14 @@ class PXEGen:
if image is None:
# profile or system+profile based, not image based, or system+image based
- kernel_path = os.path.join("/images",distro.name,os.path.basename(distro.kernel))
- initrd_path = os.path.join("/images",distro.name,os.path.basename(distro.initrd))
+ if distro is not None and distro.breed == "windows":
+ if system:
+ kernel_path = "systems/%s/winpxe.0" % system.name
+ elif profile:
+ kernel_path = "profiles/%s/winpxe.0" % profile.name
+ else:
+ kernel_path = os.path.join("/images",distro.name,os.path.basename(distro.kernel))
+ initrd_path = os.path.join("/images",distro.name,os.path.basename(distro.initrd))
# Find the kickstart if we inherit from another profile
if system:
@@ -423,7 +577,11 @@ class PXEGen:
# choose a template
if system:
if system.netboot_enabled:
- template = os.path.join(self.settings.pxe_template_dir,"pxesystem.template")
+ if distro is not None and distro.breed == "windows":
+ template = os.path.join(self.settings.pxe_template_dir,"pxesystem_win.template")
+ else:
+ template = os.path.join(self.settings.pxe_template_dir,"pxesystem.template")
+
if arch == "s390x":
template = os.path.join(self.settings.pxe_template_dir,"pxesystem_s390x.template")
elif arch == "ia64":
@@ -455,7 +613,10 @@ class PXEGen:
else:
template = os.path.join(self.settings.pxe_template_dir,"pxelocal.template")
else:
- template = os.path.join(self.settings.pxe_template_dir,"pxeprofile.template")
+ if distro is not None and distro.breed == "windows":
+ template = os.path.join(self.settings.pxe_template_dir,"pxeprofile_win.template")
+ else:
+ template = os.path.join(self.settings.pxe_template_dir,"pxeprofile.template")
# now build the kernel command line
@@ -508,6 +669,9 @@ class PXEGen:
metadata["menu_label"] = "MENU LABEL %s" % image.name
metadata["profile_name"] = image.name
+ if system:
+ metadata["system_name"] = system.name
+
if kernel_path is not None:
metadata["kernel_path"] = kernel_path
if initrd_path is not None:
@@ -626,4 +790,57 @@ class PXEGen:
return results
+ def write_tftpd_rules(self,write_file=False):
+ buffer = ""
+ template = "/etc/cobbler/tftpd-rules.template"
+ rules_dst = self.settings.tftpd_rules
+ ldr_rule_line = "re\t^L$random_id\t\t%s/$name/NTLDR\n"
+ sif_rule_line = "re\t/.*w$random_id\\.sif\t%s/$name/winnt.sif\n"
+ ntd_rule_line = "re\t/.*ntd_$random_id\.com\t%s/$name/ntdetect.com\n"
+
+ fd = open(template, "r")
+ buffer = fd.read()
+ fd.close()
+
+ buffer += "\n"
+
+ for profile in self.profiles:
+ try:
+ effective_distro = profile.get_conceptual_parent()
+ if effective_distro.breed != "windows":
+ continue
+ except:
+ print _(" warning: could not determine distro for profile %s") % profile.name
+ continue
+ try:
+ blended = utils.blender(self.api, False, profile)
+ buffer += self.templar.render(ldr_rule_line % "profiles", blended, None)
+ buffer += self.templar.render(sif_rule_line % "profiles", blended, None)
+ buffer += self.templar.render(ntd_rule_line % "profiles", blended, None)
+ except:
+ print _(" warning: could not generate tftpd rules for profile %s") % profile.name
+
+ for system in self.systems:
+ try:
+ effective_distro = system.get_conceptual_parent().get_conceptual_parent()
+ if effective_distro.breed != "windows":
+ continue
+ except:
+ print _(" warning: could not determine distro for system %s") % system.name
+ continue
+ try:
+ blended = utils.blender(self.api, False, system)
+ buffer += self.templar.render(ldr_rule_line % "systems", blended, None)
+ buffer += self.templar.render(sif_rule_line % "systems", blended, None)
+ buffer += self.templar.render(ntd_rule_line % "systems", blended, None)
+ except:
+ print _(" warning: could not generate tftpd rules for system %s") % system.name
+
+ if write_file:
+ fd = open(rules_dst, "w")
+ fd.write(buffer)
+ fd.close()
+
+ return buffer
+
diff --git a/cobbler/settings.py b/cobbler/settings.py
index ec0e254d..54b5b0b9 100644
--- a/cobbler/settings.py
+++ b/cobbler/settings.py
@@ -70,6 +70,7 @@ DEFAULTS = {
},
"manage_dhcp" : 0,
"manage_dns" : 0,
+ "manage_xinetd" : 0,
"manage_forward_zones" : [],
"manage_reverse_zones" : [],
"mgmt_classes" : [],
@@ -89,12 +90,14 @@ DEFAULTS = {
"register_new_installs" : 0,
"restart_dns" : 1,
"restart_dhcp" : 1,
+ "restart_xinetd" : 1,
"run_install_triggers" : 1,
"server" : "127.0.0.1",
"snippetsdir" : "/var/lib/cobbler/snippets",
"syslog_port" : 25150,
"tftpd_bin" : "/usr/sbin/in.tftpd",
"tftpd_conf" : "/etc/xinetd.d/tftp",
+ "tftpd_rules" : "/etc/tftpd.rules",
"vsftpd_bin" : "/usr/sbin/vsftpd",
"webdir" : "/var/www/cobbler",
"xmlrpc_port" : 25151,
diff --git a/cobbler/test_basic.py b/cobbler/test_basic.py
index 7f6a062b..27ecabe0 100644
--- a/cobbler/test_basic.py
+++ b/cobbler/test_basic.py
@@ -873,8 +873,15 @@ class TestImage(BootTest):
# ensure that only valid names are accepted and invalid ones are rejected
image = self.api.new_image()
self.assertTrue(image.set_file("nfs://hostname/path/to/filename.iso"))
- self.assertTrue(image.set_file("/mcpierce@hostname:/path/to/filename.iso"))
- self.assertTrue(image.set_file("path/to/filename.iso"))
+ self.assertTrue(image.set_file("nfs://mcpierce@hostname:/path/to/filename.iso"))
+ self.assertTrue(image.set_file("nfs://hostname:/path/to/filename.iso"))
+ self.assertTrue(image.set_file("nfs://hostname/filename.iso"))
+ self.assertTrue(image.set_file("hostname:/path/to/the/filename.iso"))
+ self.failUnlessRaises(CX, image.set_file, "hostname:filename.iso")
+ self.failUnlessRaises(CX, image.set_file, "path/to/filename.iso")
+ self.failUnlessRaises(CX, image.set_file, "hostname:")
+ # port is not allowed
+ self.failUnlessRaises(CX, image.set_file, "nfs://hostname:1234/path/to/the/filename.iso")
#class TestCLIBasic(BootTest):
#
diff --git a/cobbler/utils.py b/cobbler/utils.py
index 8603d0eb..94543d9c 100644
--- a/cobbler/utils.py
+++ b/cobbler/utils.py
@@ -898,6 +898,50 @@ def copyfile(src,dst,api=None,verbose=False):
# traceback.print_exc()
# raise CX(_("Error copying %(src)s to %(dst)s") % { "src" : src, "dst" : dst})
+def cabextract(src,dst,api=None):
+ """
+ Extract a cab file, used for importing off of Windows based cds
+ """
+ try:
+ if not os.path.isdir(dst):
+ raise CX(_("Error in cabextract: the destination (%s) must be a directory") % dst)
+ cmd = [ "/usr/bin/cabextract", "-d", dst, src ]
+ rc = sub_process.call(cmd, shell=False, close_fds=True)
+ return rc
+ except:
+ if not os.access(src,os.R_OK):
+ raise CX(_("Cannot read: %s") % src)
+ if not os.path.samefile(src,dst):
+ # accomodate for the possibility that we already copied
+ # the file as a symlink/hardlink
+ raise
+ # traceback.print_exc()
+ # raise CX(_("Error copying %(src)s to %(dst)s") % { "src" : src, "dst" : dst})
+
+def bindmount(src,dst):
+ """
+ Use mount --bind as an alternative to linking. This is required
+ for things in the tftp root since in.tftpd will not follow symlinks
+ and you cannot hard link directories (or across partitions).
+ """
+ try:
+ if not os.path.isdir(src):
+ raise CX(_("Error in bindmount: the source (%s) must be a directory") % src)
+ if not os.path.isdir(dst):
+ raise CX(_("Error in bindmount: the destination (%s) must be a directory") % dst)
+ cmd = [ "/bin/mount", "--bind", dst, src ]
+ rc = sub_process.call(cmd, shell=False, close_fds=True)
+ return rc
+ except:
+ if not os.access(src,os.R_OK):
+ raise CX(_("Cannot read: %s") % src)
+ if not os.path.samefile(src,dst):
+ # accomodate for the possibility that we already copied
+ # the file as a symlink/hardlink
+ raise
+ # traceback.print_exc()
+ # raise CX(_("Error bind-mounting %(src)s to %(dst)s") % { "src" : src, "dst" : dst})
+
def copyfile_pattern(pattern,dst,require_match=True,symlink_ok=False,api=None, verbose=False):
files = glob.glob(pattern)
if require_match and not len(files) > 0:
diff --git a/setup.py b/setup.py
index c2a6987b..eb5818b2 100644
--- a/setup.py
+++ b/setup.py
@@ -229,6 +229,7 @@ if __name__ == "__main__":
# templates for power management
(powerpath, ['templates/power_apc_snmp.template']),
+ (powerpath, ['templates/power_integrity.template']),
(powerpath, ['templates/power_ipmilan.template']),
(powerpath, ['templates/power_bullpap.template']),
(powerpath, ['templates/power_ipmitool.template']),
diff --git a/triggers/restart-services.trigger b/triggers/restart-services.trigger
index 82dc0427..553aec01 100644
--- a/triggers/restart-services.trigger
+++ b/triggers/restart-services.trigger
@@ -8,8 +8,10 @@ bootapi = capi.BootAPI()
settings = bootapi.settings()
manage_dhcp = str(settings.manage_dhcp).lower()
manage_dns = str(settings.manage_dns).lower()
+manage_xinetd = str(settings.manage_xinetd).lower()
restart_dhcp = str(settings.restart_dhcp).lower()
restart_dns = str(settings.restart_dns).lower()
+restart_xinetd = str(settings.restart_xinetd).lower()
omapi_enabled = settings.omapi_enabled
omapi_port = settings.omapi_port
@@ -43,5 +45,8 @@ if manage_dns != "0" and restart_dns != "0":
elif bootapi.dns.what() == "dnsmasq" and not has_restarted_dnsmasq:
rc = os.ssytem("/sbin/service dnsmasq restart")
+if manage_xinetd != "0" and restart_xinetd != "0":
+ rc = os.system("/sbin/service xinetd restart")
+
sys.exit(rc)
diff --git a/webui_templates/system_edit.tmpl b/webui_templates/system_edit.tmpl
index 37d0bf29..15c3fc6b 100644
--- a/webui_templates/system_edit.tmpl
+++ b/webui_templates/system_edit.tmpl
@@ -684,7 +684,7 @@ redhatmanagementkey"
</td>
<td class="poweredit">
<select name="power_type" id="power_type">
- #set valid_power = [ "bullpap", "wti", "apc_snmp", "ether-wake", "ipmilan", "drac", "ipmitool", "ilo", "rsa", "lpar", "bladecenter", "virsh" ]
+ #set valid_power = [ "bullpap", "wti", "apc_snmp", "ether-wake", "ipmilan", "drac", "ipmitool", "ilo", "rsa", "lpar", "bladecenter", "virsh" ,"integrity"]
#set nothing = valid_power.sort()
#for $value in $valid_power: