# with this program; if not, write to the Free Software Foundation, Inc., 59
# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-import os, sys
+import os, sys, re
import shutil
import subprocess
-import time
import string
+import collections
-from mic import msger
-from mic.utils import errors, misc, runner, fs_related as fs
-
+import pykickstart.sections as kssections
import pykickstart.commands as kscommands
import pykickstart.constants as ksconstants
import pykickstart.errors as kserrors
from pykickstart.handlers.control import commandMap
from pykickstart.handlers.control import dataMap
-import custom_commands.desktop as desktop
-import custom_commands.moblinrepo as moblinrepo
-import custom_commands.micboot as micboot
+from mic import msger
+from mic.utils import errors, misc, runner, fs_related as fs
+from custom_commands import desktop, micrepo, micboot, partition, installerfw
+from mic.utils.safeurl import SafeURL
+
+
+AUTH_URL_PTN = r"(?P<scheme>.*)://(?P<username>.*)(:?P<password>.*)?@(?P<url>.*)"
+
+
+class PrepackageSection(kssections.Section):
+ sectionOpen = "%prepackages"
+
+ def handleLine(self, line):
+ if not self.handler:
+ return
+
+ (h, s, t) = line.partition('#')
+ line = h.rstrip()
+
+ self.handler.prepackages.add([line])
+
+ def handleHeader(self, lineno, args):
+ kssections.Section.handleHeader(self, lineno, args)
+
+class AttachmentSection(kssections.Section):
+ sectionOpen = "%attachment"
+
+ def handleLine(self, line):
+ if not self.handler:
+ return
+
+ (h, s, t) = line.partition('#')
+ line = h.rstrip()
+
+ self.handler.attachment.add([line])
+
+ def handleHeader(self, lineno, args):
+ kssections.Section.handleHeader(self, lineno, args)
+
+def apply_wrapper(func):
+ def wrapper(*kargs, **kwargs):
+ try:
+ func(*kargs, **kwargs)
+ except (OSError, IOError, errors.KsError), err:
+ cfgcls = kargs[0].__class__.__name__
+ if msger.ask("Failed to apply %s, skip and continue?" % cfgcls):
+ msger.warning("%s" % err)
+ pass
+ else:
+ # just throw out the exception
+ raise
+ return wrapper
def read_kickstart(path):
"""Parse a kickstart file and return a KickstartParser instance.
be then passed to an ImageCreator constructor.
If an error occurs, a CreatorError exception is thrown.
-
"""
+
#version = ksversion.makeVersion()
#ks = ksparser.KickstartParser(version)
using_version = ksversion.DEVEL
- commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
- commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
- commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
- dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
+ commandMap[using_version]["desktop"] = desktop.Mic_Desktop
+ commandMap[using_version]["repo"] = micrepo.Mic_Repo
+ commandMap[using_version]["tpk_repo"] = micrepo.Mic_Tpk_Repo
+ commandMap[using_version]["bootloader"] = micboot.Mic_Bootloader
+ commandMap[using_version]["part"] = partition.Mic_Partition
+ commandMap[using_version]["partition"] = partition.Mic_Partition
+ commandMap[using_version]["installerfw_plugins"] = installerfw.Mic_installerfw
+ dataMap[using_version]["RepoData"] = micrepo.Mic_RepoData
+ dataMap[using_version]["Tpk_RepoData"] = micrepo.Mic_Tpk_RepoData
+ dataMap[using_version]["PartData"] = partition.Mic_PartData
superclass = ksversion.returnClassForVersion(version=using_version)
class KSHandlers(superclass):
- def __init__(self, mapping={}):
+ def __init__(self):
superclass.__init__(self, mapping=commandMap[using_version])
+ self.prepackages = ksparser.Packages()
+ self.attachment = ksparser.Packages()
- ks = ksparser.KickstartParser(KSHandlers())
+ ks = ksparser.KickstartParser(KSHandlers(), errorsAreFatal=False)
+ ks.registerSection(PrepackageSection(ks.handler))
+ ks.registerSection(AttachmentSection(ks.handler))
try:
ks.readKickstart(path)
- except kserrors.KickstartError, e:
- raise errors.KsError("'%s': %s" % (path, str(e)))
- except kserrors.KickstartParseError, e:
- raise errors.KsError("'%s': %s" % (path, str(e)))
- return ks
-
-def build_name(kscfg, prefix = None, suffix = None, maxlen = None):
- """Construct and return an image name string.
-
- This is a utility function to help create sensible name and fslabel
- strings. The name is constructed using the sans-prefix-and-extension
- kickstart filename and the supplied prefix and suffix.
-
- If the name exceeds the maxlen length supplied, the prefix is first dropped
- and then the kickstart filename portion is reduced until it fits. In other
- words, the suffix takes precedence over the kickstart portion and the
- kickstart portion takes precedence over the prefix.
-
- kscfg -- a path to a kickstart file
- prefix -- a prefix to prepend to the name; defaults to None, which causes
- no prefix to be used
- suffix -- a suffix to append to the name; defaults to None, which causes
- a YYYYMMDDHHMM suffix to be used
- maxlen -- the maximum length for the returned string; defaults to None,
- which means there is no restriction on the name length
-
- Note, if maxlen is less then the len(suffix), you get to keep both pieces.
-
- """
- name = os.path.basename(kscfg)
- idx = name.rfind('.')
- if idx >= 0:
- name = name[:idx]
-
- if prefix is None:
- prefix = ""
- if suffix is None:
- suffix = time.strftime("%Y%m%d%H%M")
-
- if name.startswith(prefix):
- name = name[len(prefix):]
-
- ret = prefix + name + "-" + suffix
- if not maxlen is None and len(ret) > maxlen:
- ret = name[:maxlen - len(suffix) - 1] + "-" + suffix
+ except (kserrors.KickstartParseError, kserrors.KickstartError), err:
+ if msger.ask("Errors occured on kickstart file, skip and continue?"):
+ msger.warning("%s" % err)
+ pass
+ else:
+ raise errors.KsError("%s" % err)
- return ret
+ return ks
class KickstartConfig(object):
"""A base class for applying kickstart configurations to a system."""
def call(self, args):
if not os.path.exists("%s/%s" %(self.instroot, args[0])):
- msger.warning("%s/%s" %(self.instroot, args[0]))
- raise errors.KsError("Unable to run %s!" %(args))
+ raise errors.KsError("Can't find %s in chroot" % args[0])
subprocess.call(args, preexec_fn = self.chroot)
def apply(self):
class LanguageConfig(KickstartConfig):
"""A class to apply a kickstart language configuration to a system."""
+ @apply_wrapper
def apply(self, kslang):
self._check_sysconfig()
if kslang.lang:
f.write("LANG=\"" + kslang.lang + "\"\n")
f.close()
+ f = open(self.path("/etc/locale.conf"), "w+")
+ f.write("LANG=\"" + kslang.lang + "\"\n")
+ f.close()
+
+ #cp ks lang setting to other file, then can access the file in %post section
+ fs.makedirs(self.path("/etc/config"))
+ f = open(self.path("/etc/config/mic_language"), "w+")
+ f.write("LANG=\"" + kslang.lang + "\"\n")
+ f.close()
+
class KeyboardConfig(KickstartConfig):
"""A class to apply a kickstart keyboard configuration to a system."""
+ @apply_wrapper
def apply(self, kskeyboard):
#
# FIXME:
class TimezoneConfig(KickstartConfig):
"""A class to apply a kickstart timezone configuration to a system."""
+ @apply_wrapper
def apply(self, kstimezone):
self._check_sysconfig()
tz = kstimezone.timezone or "America/New_York"
f.write("ZONE=\"" + tz + "\"\n")
f.write("UTC=" + utc + "\n")
f.close()
+ if not os.path.exists("/opt/etc"):
+ fs.makedirs("/opt/etc")
+ tz_source = "/usr/share/zoneinfo/%s" % (tz)
+ tz_midst = "/opt/etc/localtime"
+ tz_dest = "/etc/localtime"
try:
- shutil.copyfile(self.path("/usr/share/zoneinfo/%s" %(tz,)),
- self.path("/etc/localtime"))
+ lncmd = fs.find_binary_inchroot('ln', self.instroot)
+ if lncmd:
+ self.call([lncmd, "-s", tz_source, tz_midst])
+ self.call([lncmd, "-s", tz_midst, tz_dest])
+ else:
+ lncmd = fs.find_binary_path('ln')
+ subprocess.call([lncmd, "-s",
+ self.path(tz_source),
+ self.path(tz_midst)])
+ subprocess.call([lncmd, "-s",
+ self.path(tz_midst),
+ self.path(tz_dest)])
except (IOError, OSError), (errno, msg):
- raise errors.KsError("Error copying timezone info: %s" %(msg,))
-
+ raise errors.KsError("Timezone setting error: %s" % msg)
class AuthConfig(KickstartConfig):
"""A class to apply a kickstart authconfig configuration to a system."""
+ @apply_wrapper
def apply(self, ksauthconfig):
auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
class FirewallConfig(KickstartConfig):
"""A class to apply a kickstart firewall configuration to a system."""
+ @apply_wrapper
def apply(self, ksfirewall):
#
# FIXME: should handle the rest of the options
def set_unencrypted(self, password):
for p in ("/bin/echo", "/usr/sbin/chpasswd"):
if not os.path.exists("%s/%s" %(self.instroot, p)):
- raise errors.KsError("Unable to set unencrypted password due to lack of %s" % p)
+ raise errors.KsError("Unable to set unencrypted password due "
+ "to lack of %s" % p)
p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
stdout = subprocess.PIPE,
preexec_fn = self.chroot)
p2.communicate()
+ @apply_wrapper
def apply(self, ksrootpw):
if ksrootpw.isCrypted:
self.set_encrypted(ksrootpw.password)
def set_unencrypted_passwd(self, user, password):
for p in ("/bin/echo", "/usr/sbin/chpasswd"):
if not os.path.exists("%s/%s" %(self.instroot, p)):
- raise errors.KsError("Unable to set unencrypted password due to lack of %s" % p)
+ raise errors.KsError("Unable to set unencrypted password due "
+ "to lack of %s" % p)
p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
stdout = subprocess.PIPE,
if userconfig.groups:
args += [ "--groups", string.join(userconfig.groups, ",") ]
if userconfig.name:
+ args += [ "-m"]
+ args += [ "-d", "/home/%s" % userconfig.name ]
args.append(userconfig.name)
- dev_null = os.open("/dev/null", os.O_WRONLY)
- subprocess.call(args,
- stdout = dev_null,
- stderr = dev_null,
- preexec_fn = self.chroot)
- os.close(dev_null)
+ try:
+ dev_null = os.open("/dev/null", os.O_WRONLY)
+ msger.debug('adding user with %s' % args)
+ subprocess.call(args,
+ stdout = dev_null,
+ stderr = dev_null,
+ preexec_fn = self.chroot)
+ os.close(dev_null)
+ except:
+ msger.warning('Cannot add user using "useradd"')
+
if userconfig.password not in (None, ""):
if userconfig.isCrypted:
- self.set_encrypted_passwd(userconfig.name, userconfig.password)
+ self.set_encrypted_passwd(userconfig.name,
+ userconfig.password)
else:
- self.set_unencrypted_passwd(userconfig.name, userconfig.password)
+ self.set_unencrypted_passwd(userconfig.name,
+ userconfig.password)
else:
self.set_empty_passwd(userconfig.name)
else:
- raise errors.KsError("Invalid kickstart command: %s" % userconfig.__str__())
+ raise errors.KsError("Invalid kickstart command: %s" \
+ % userconfig.__str__())
+ @apply_wrapper
def apply(self, user):
for userconfig in user.userList:
- try:
- self.addUser(userconfig)
- except:
- raise
+ self.addUser(userconfig)
class ServicesConfig(KickstartConfig):
"""A class to apply a kickstart services configuration to a system."""
+ @apply_wrapper
def apply(self, ksservices):
if not os.path.exists(self.path("/sbin/chkconfig")):
return
class XConfig(KickstartConfig):
"""A class to apply a kickstart X configuration to a system."""
+ @apply_wrapper
def apply(self, ksxconfig):
if ksxconfig.startX and os.path.exists(self.path("/etc/inittab")):
f = open(self.path("/etc/inittab"), "rw+")
class DesktopConfig(KickstartConfig):
"""A class to apply a kickstart desktop configuration to a system."""
+ @apply_wrapper
def apply(self, ksdesktop):
if ksdesktop.defaultdesktop:
self._check_sysconfig()
baseurl = repo.baseurl
if repo.mirrorlist:
mirrorlist = repo.mirrorlist
+
elif type == "debuginfo":
if repo.baseurl:
if repo.baseurl.endswith("/"):
else:
baseurl = os.path.dirname(repo.baseurl)
baseurl += "/debug"
+
if repo.mirrorlist:
variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
mirrorlist += "debug" + "-" + variant
+
elif type == "source":
if repo.baseurl:
if repo.baseurl.endswith("/"):
- baseurl = os.path.dirname(os.path.dirname(os.path.dirname(repo.baseurl)))
+ baseurl = os.path.dirname(
+ os.path.dirname(
+ os.path.dirname(repo.baseurl)))
else:
baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
baseurl += "/source"
+
if repo.mirrorlist:
variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
fd.write("name=" + reponame + "\n")
fd.write("failovermethod=priority\n")
if baseurl:
+ auth_url = re.compile(AUTH_URL_PTN)
+ m = auth_url.match(baseurl)
+ if m:
+ baseurl = "%s://%s" % (m.group('scheme'), m.group('url'))
fd.write("baseurl=" + baseurl + "\n")
if mirrorlist:
fd.write("mirrorlist=" + mirrorlist + "\n")
self.__create_repo_section(repo, "source", f)
f.close()
- def apply(self, ksrepo, repodata):
+ @apply_wrapper
+ def apply(self, ksrepo, repodata, repourl):
for repo in ksrepo.repoList:
+ if repo.name in repourl:
+ repo.baseurl = repourl[repo.name]
if repo.save:
#self.__create_repo_file(repo, "/etc/yum.repos.d")
self.__create_repo_file(repo, "/etc/zypp/repos.d")
if repodata:
for repo in repodata:
if repo['repokey']:
- runner.quiet(['rpm', "--root=%s" % self.instroot, "--import", repo['repokey']])
+ runner.quiet(['rpm',
+ "--root=%s" % self.instroot,
+ "--import",
+ repo['repokey']])
class RPMMacroConfig(KickstartConfig):
"""A class to apply the specified rpm macros to the filesystem"""
+ @apply_wrapper
def apply(self, ks):
if not ks:
return
f.close()
+ @apply_wrapper
def apply(self, ksnet):
fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
self.write_hosts(hostname)
self.write_resolv(nodns, nameservers)
+def use_installerfw(ks, feature):
+ """ Check if the installer framework has to be used for a feature
+ "feature". """
+
+ features = ks.handler.installerfw.features
+ if features:
+ if feature in features or "all" in features:
+ return True
+ return False
def get_image_size(ks, default = None):
__size = 0
def get_image_fsopts(ks, default = None):
for p in ks.handler.partition.partitions:
if p.mountpoint == "/" and p.fsopts:
- return p.fstype
+ return p.fsopts
return default
def get_modules(ks):
return default
return int(ks.handler.bootloader.timeout)
-def get_kernel_args(ks, default = "ro liveimg"):
+def get_kernel_args(ks, default = "ro rd.live.image"):
if not hasattr(ks.handler.bootloader, "appendLine"):
return default
if ks.handler.bootloader.appendLine is None:
return default
return "%s %s" %(default, ks.handler.bootloader.appendLine)
-def get_menu_args(ks, default = "liveinst"):
+def get_menu_args(ks, default = ""):
if not hasattr(ks.handler.bootloader, "menus"):
return default
if ks.handler.bootloader.menus in (None, ""):
return default
return ks.handler.bootloader.default
-def get_repos(ks, repo_urls = {}):
- repos = {}
- for repo in ks.handler.repo.repoList:
- inc = []
- if hasattr(repo, "includepkgs"):
- inc.extend(repo.includepkgs)
-
- exc = []
- if hasattr(repo, "excludepkgs"):
- exc.extend(repo.excludepkgs)
-
- baseurl = repo.baseurl
- mirrorlist = repo.mirrorlist
-
- if repo.name in repo_urls:
- baseurl = repo_urls[repo.name]
- mirrorlist = None
-
- if repos.has_key(repo.name):
- msger.warning("Overriding already specified repo %s" %(repo.name,))
-
- proxy = None
- if hasattr(repo, "proxy"):
- proxy = repo.proxy
- proxy_username = None
- if hasattr(repo, "proxy_username"):
- proxy_username = repo.proxy_username
- proxy_password = None
- if hasattr(repo, "proxy_password"):
- proxy_password = repo.proxy_password
- if hasattr(repo, "debuginfo"):
- debuginfo = repo.debuginfo
- if hasattr(repo, "source"):
- source = repo.source
- if hasattr(repo, "gpgkey"):
- gpgkey = repo.gpgkey
- if hasattr(repo, "disable"):
- disable = repo.disable
-
- repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc, proxy, proxy_username, proxy_password, debuginfo, source, gpgkey, disable)
+RepoType = collections.namedtuple("Repo",
+ "name, baseurl, mirrorlist, includepkgs, excludepkgs, proxy, \
+ proxy_username, proxy_password, debuginfo, \
+ source, gpgkey, disable, ssl_verify, nocache, \
+ cost, priority")
+
+def Repo(name, baseurl, mirrorlist=None, includepkgs=[], excludepkgs=[], proxy=None,
+ proxy_username=None, proxy_password=None, debuginfo=None,
+ source=None, gpgkey=None, disable=None, ssl_verify=None,
+ nocache=False, cost=None, priority=None):
+ return RepoType(name, baseurl, mirrorlist, includepkgs, excludepkgs, proxy,
+ proxy_username, proxy_password, debuginfo,
+ source, gpgkey, disable, ssl_verify, nocache,
+ cost, priority)
+
+def get_repos(ks, repo_urls=None, ignore_ksrepo=False):
+ repos = {}
+ for repodata in ks.handler.repo.repoList:
+ repo = {}
+ for field in RepoType._fields:
+ if hasattr(repodata, field) and getattr(repodata, field):
+ repo[field] = getattr(repodata, field)
+
+ if hasattr(repodata, 'baseurl') and getattr(repodata, 'baseurl'):
+ repo['baseurl'] = SafeURL(getattr(repodata, 'baseurl'),
+ getattr(repodata, 'user', None),
+ getattr(repodata, 'passwd', None))
+
+ if 'name' in repo:
+ repos[repo['name']] = Repo(**repo)
+
+ if repo_urls:
+ if ignore_ksrepo:
+ repos = {}
+ for name, repo in repo_urls.items():
+ if 'baseurl' in repo:
+ repo['baseurl'] = SafeURL(repo.get('baseurl'),
+ repo.get('user', None),
+ repo.get('passwd', None))
+ repos[name] = Repo(**repo)
return repos.values()
+TpkRepoType = collections.namedtuple("TpkRepo",
+ "name, baseurl,proxy,proxy_username,proxy_password,ssl_verify")
+
+def TpkRepo(name, baseurl, proxy=None,proxy_username=None, proxy_password=None,ssl_verify=None):
+ return TpkRepoType(name, baseurl,proxy,proxy_username,proxy_password,ssl_verify)
+
+
+def get_tpkrepos(ks):
+ tpkrepos = {}
+ for tpkrepodata in ks.handler.tpk_repo.tpkRepoList:
+ tpkrepo = {}
+ for field in TpkRepoType._fields:
+ if hasattr(tpkrepodata, field) and getattr(tpkrepodata, field):
+ tpkrepo[field] = getattr(tpkrepodata, field)
+
+ if hasattr(tpkrepodata, 'baseurl') and getattr(tpkrepodata, 'baseurl'):
+ tpkrepo['baseurl'] = SafeURL(getattr(tpkrepodata, 'baseurl'),getattr(tpkrepodata, 'user',None),getattr(tpkrepodata, 'passwd',None))
+
+ if 'name' in tpkrepo:
+ tpkrepos[tpkrepo['name']] = TpkRepo(**tpkrepo)
+
+ return tpkrepos.values()
+
+
def convert_method_to_repo(ks):
try:
ks.handler.repo.methodToRepo()
except (AttributeError, kserrors.KickstartError):
pass
-def get_packages(ks, required = []):
- return ks.handler.packages.packageList + required
+def get_attachment(ks, required=()):
+ return ks.handler.attachment.packageList + list(required)
+
+def get_pre_packages(ks, required=()):
+ return ks.handler.prepackages.packageList + list(required)
-def get_groups(ks, required = []):
- return ks.handler.packages.groupList + required
+def get_packages(ks, required=()):
+ return ks.handler.packages.packageList + list(required)
-def get_excluded(ks, required = []):
- return ks.handler.packages.excludedList + required
+def get_tpkpackages(ks):
+ return ks.handler.tpk_packages.tpk_packageList
-def get_partitions(ks, required = []):
+def get_groups(ks, required=()):
+ return ks.handler.packages.groupList + list(required)
+
+def get_excluded(ks, required=()):
+ return ks.handler.packages.excludedList + list(required)
+
+def get_partitions(ks):
return ks.handler.partition.partitions
def ignore_missing(ks):
scripts.append(s)
return scripts
+def get_sign_scripts(ks):
+ scripts = []
+ for s in ks.handler.scripts:
+ if (s.type == ksparser.KS_SCRIPT_RUN or \
+ s.type == ksparser.KS_SCRIPT_UMOUNT):
+ scripts.append(s)
+ return scripts
+
def add_repo(ks, repostr):
args = repostr.split()
repoobj = ks.handler.repo.parse(args[1:])
ks = creatoropts['ks']
for repo in repometadata:
- """ Mustn't replace group with package list if repo is ready for the corresponding package manager """
+ """ Mustn't replace group with package list if repo is ready for the
+ corresponding package manager.
+ """
+
if iszypp and repo["patterns"]:
continue
if not iszypp and repo["comps"]:
continue
- """
- But we also must handle such cases, use zypp but repo only has comps,
- use yum but repo only has patterns, use zypp but use_comps is true,
- use yum but use_comps is false.
- """
+ # But we also must handle such cases, use zypp but repo only has comps,
+ # use yum but repo only has patterns, use zypp but use_comps is true,
+ # use yum but use_comps is false.
groupfile = None
if iszypp and repo["comps"]:
groupfile = repo["comps"]
while True:
if i >= len(ks.handler.packages.groupList):
break
- pkglist = get_pkglist_handler(ks.handler.packages.groupList[i].name, groupfile)
+ pkglist = get_pkglist_handler(
+ ks.handler.packages.groupList[i].name,
+ groupfile)
if pkglist:
del ks.handler.packages.groupList[i]
for pkg in pkglist: