bear syntax '%post' without '%end' in ks file, giving warning
[platform/upstream/mic.git] / mic / kickstart / __init__.py
1 #!/usr/bin/python -tt
2 #
3 # Copyright (c) 2007 Red Hat, Inc.
4 # Copyright (c) 2009, 2010, 2011 Intel, Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the Free
8 # Software Foundation; version 2 of the License
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 # for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc., 59
17 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 import os, sys, re
20 import shutil
21 import subprocess
22 import time
23 import string
24
25 from mic import msger
26 from mic.utils import errors, misc, runner, fs_related as fs
27
28 import pykickstart.commands as kscommands
29 import pykickstart.constants as ksconstants
30 import pykickstart.errors as kserrors
31 import pykickstart.parser as ksparser
32 import pykickstart.version as ksversion
33 from pykickstart.handlers.control import commandMap
34 from pykickstart.handlers.control import dataMap
35
36 import custom_commands.desktop as desktop
37 import custom_commands.moblinrepo as moblinrepo
38 import custom_commands.micboot as micboot
39
40 def read_kickstart(path):
41     """Parse a kickstart file and return a KickstartParser instance.
42
43     This is a simple utility function which takes a path to a kickstart file,
44     parses it and returns a pykickstart KickstartParser instance which can
45     be then passed to an ImageCreator constructor.
46
47     If an error occurs, a CreatorError exception is thrown.
48     """
49
50     #version = ksversion.makeVersion()
51     #ks = ksparser.KickstartParser(version)
52
53     using_version = ksversion.DEVEL
54     commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
55     commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
56     commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
57     dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
58     superclass = ksversion.returnClassForVersion(version=using_version)
59
60     class KSHandlers(superclass):
61         def __init__(self, mapping={}):
62             superclass.__init__(self, mapping=commandMap[using_version])
63
64     ks = ksparser.KickstartParser(KSHandlers())
65
66     try:
67         ks.readKickstart(path)
68     except kserrors.KickstartParseError, e:
69         msgptn = re.compile("^\D*(\d+).*(Section does not end with.*)$", re.S)
70         m = msgptn.match(str(e))
71         if m:
72             lineno = m.group(1)
73             wrnmsg = m.group(2)
74             msger.warning("'%s:%s': %s" % (path, lineno, wrnmsg))
75         else:
76             raise errors.KsError("'%s': %s" % (path, str(e)))
77     except kserrors.KickstartError, e:
78         raise errors.KsError("'%s': %s" % (path, str(e)))
79     return ks
80
81 def build_name(kscfg, prefix = None, suffix = None, maxlen = None):
82     """Construct and return an image name string.
83
84     This is a utility function to help create sensible name and fslabel
85     strings. The name is constructed using the sans-prefix-and-extension
86     kickstart filename and the supplied prefix and suffix.
87
88     If the name exceeds the maxlen length supplied, the prefix is first dropped
89     and then the kickstart filename portion is reduced until it fits. In other
90     words, the suffix takes precedence over the kickstart portion and the
91     kickstart portion takes precedence over the prefix.
92
93     kscfg -- a path to a kickstart file
94     prefix -- a prefix to prepend to the name; defaults to None, which causes
95               no prefix to be used
96     suffix -- a suffix to append to the name; defaults to None, which causes
97               a YYYYMMDDHHMM suffix to be used
98     maxlen -- the maximum length for the returned string; defaults to None,
99               which means there is no restriction on the name length
100
101     Note, if maxlen is less then the len(suffix), you get to keep both pieces.
102
103     """
104     name = os.path.basename(kscfg)
105     idx = name.rfind('.')
106     if idx >= 0:
107         name = name[:idx]
108
109     if prefix is None:
110         prefix = ""
111     if suffix is None:
112         suffix = time.strftime("%Y%m%d%H%M")
113
114     if name.startswith(prefix):
115         name = name[len(prefix):]
116
117     ret = prefix + name + "-" + suffix
118     if not maxlen is None and len(ret) > maxlen:
119         ret = name[:maxlen - len(suffix) - 1] + "-" + suffix
120
121     return ret
122
123 class KickstartConfig(object):
124     """A base class for applying kickstart configurations to a system."""
125     def __init__(self, instroot):
126         self.instroot = instroot
127
128     def path(self, subpath):
129         return self.instroot + subpath
130
131     def _check_sysconfig(self):
132         if not os.path.exists(self.path("/etc/sysconfig")):
133             fs.makedirs(self.path("/etc/sysconfig"))
134
135     def chroot(self):
136         os.chroot(self.instroot)
137         os.chdir("/")
138
139     def call(self, args):
140         if not os.path.exists("%s/%s" %(self.instroot, args[0])):
141             msger.warning("%s/%s" %(self.instroot, args[0]))
142             raise errors.KsError("Unable to run %s!" %(args))
143         subprocess.call(args, preexec_fn = self.chroot)
144
145     def apply(self):
146         pass
147
148 class LanguageConfig(KickstartConfig):
149     """A class to apply a kickstart language configuration to a system."""
150     def apply(self, kslang):
151         self._check_sysconfig()
152         if kslang.lang:
153             f = open(self.path("/etc/sysconfig/i18n"), "w+")
154             f.write("LANG=\"" + kslang.lang + "\"\n")
155             f.close()
156
157 class KeyboardConfig(KickstartConfig):
158     """A class to apply a kickstart keyboard configuration to a system."""
159     def apply(self, kskeyboard):
160         #
161         # FIXME:
162         #   should this impact the X keyboard config too?
163         #   or do we want to make X be able to do this mapping?
164         #
165         #k = rhpl.keyboard.Keyboard()
166         #if kskeyboard.keyboard:
167         #   k.set(kskeyboard.keyboard)
168         #k.write(self.instroot)
169         pass
170
171 class TimezoneConfig(KickstartConfig):
172     """A class to apply a kickstart timezone configuration to a system."""
173     def apply(self, kstimezone):
174         self._check_sysconfig()
175         tz = kstimezone.timezone or "America/New_York"
176         utc = str(kstimezone.isUtc)
177
178         f = open(self.path("/etc/sysconfig/clock"), "w+")
179         f.write("ZONE=\"" + tz + "\"\n")
180         f.write("UTC=" + utc + "\n")
181         f.close()
182         tz_source = self.path("/usr/share/zoneinfo/%s" % (tz))
183         tz_dest = self.path("/etc/localtime")
184         try:
185             shutil.copyfile(tz_source, tz_dest)
186         except (IOError, OSError), (errno, msg):
187             raise errors.KickstartError("Error copying timezone info from "
188                                         "'%s' to '%s': %s" \
189                                         % (tz_source, tz_dest, msg))
190
191
192 class AuthConfig(KickstartConfig):
193     """A class to apply a kickstart authconfig configuration to a system."""
194     def apply(self, ksauthconfig):
195         auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
196         args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
197         self.call(args + auth.split())
198
199 class FirewallConfig(KickstartConfig):
200     """A class to apply a kickstart firewall configuration to a system."""
201     def apply(self, ksfirewall):
202         #
203         # FIXME: should handle the rest of the options
204         #
205         if not os.path.exists(self.path("/usr/sbin/lokkit")):
206             return
207         if ksfirewall.enabled:
208             status = "--enabled"
209         else:
210             status = "--disabled"
211
212         self.call(["/usr/sbin/lokkit",
213                    "-f", "--quiet", "--nostart", status])
214
215 class RootPasswordConfig(KickstartConfig):
216     """A class to apply a kickstart root password configuration to a system."""
217     def unset(self):
218         self.call(["/usr/bin/passwd", "-d", "root"])
219
220     def set_encrypted(self, password):
221         self.call(["/usr/sbin/usermod", "-p", password, "root"])
222
223     def set_unencrypted(self, password):
224         for p in ("/bin/echo", "/usr/sbin/chpasswd"):
225             if not os.path.exists("%s/%s" %(self.instroot, p)):
226                 raise errors.KsError("Unable to set unencrypted password due "
227                                      "to lack of %s" % p)
228
229         p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
230                               stdout = subprocess.PIPE,
231                               preexec_fn = self.chroot)
232         p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
233                               stdin = p1.stdout,
234                               stdout = subprocess.PIPE,
235                               preexec_fn = self.chroot)
236         p2.communicate()
237
238     def apply(self, ksrootpw):
239         if ksrootpw.isCrypted:
240             self.set_encrypted(ksrootpw.password)
241         elif ksrootpw.password != "":
242             self.set_unencrypted(ksrootpw.password)
243         else:
244             self.unset()
245
246 class UserConfig(KickstartConfig):
247     def set_empty_passwd(self, user):
248         self.call(["/usr/bin/passwd", "-d", user])
249
250     def set_encrypted_passwd(self, user, password):
251         self.call(["/usr/sbin/usermod", "-p", "%s" % password, user])
252
253     def set_unencrypted_passwd(self, user, password):
254         for p in ("/bin/echo", "/usr/sbin/chpasswd"):
255             if not os.path.exists("%s/%s" %(self.instroot, p)):
256                 raise errors.KsError("Unable to set unencrypted password due "
257                                      "to lack of %s" % p)
258
259         p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
260                               stdout = subprocess.PIPE,
261                               preexec_fn = self.chroot)
262         p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
263                               stdin = p1.stdout,
264                               stdout = subprocess.PIPE,
265                               preexec_fn = self.chroot)
266         p2.communicate()
267
268     def addUser(self, userconfig):
269         args = [ "/usr/sbin/useradd" ]
270         if userconfig.groups:
271             args += [ "--groups", string.join(userconfig.groups, ",") ]
272         if userconfig.name:
273             args.append(userconfig.name)
274             dev_null = os.open("/dev/null", os.O_WRONLY)
275             subprocess.call(args,
276                              stdout = dev_null,
277                              stderr = dev_null,
278                              preexec_fn = self.chroot)
279             os.close(dev_null)
280             if userconfig.password not in (None, ""):
281                 if userconfig.isCrypted:
282                     self.set_encrypted_passwd(userconfig.name,
283                                               userconfig.password)
284                 else:
285                     self.set_unencrypted_passwd(userconfig.name,
286                                                 userconfig.password)
287             else:
288                 self.set_empty_passwd(userconfig.name)
289         else:
290             raise errors.KsError("Invalid kickstart command: %s" \
291                                  % userconfig.__str__())
292
293     def apply(self, user):
294         for userconfig in user.userList:
295             try:
296                 self.addUser(userconfig)
297             except:
298                 raise
299
300 class ServicesConfig(KickstartConfig):
301     """A class to apply a kickstart services configuration to a system."""
302     def apply(self, ksservices):
303         if not os.path.exists(self.path("/sbin/chkconfig")):
304             return
305         for s in ksservices.enabled:
306             self.call(["/sbin/chkconfig", s, "on"])
307         for s in ksservices.disabled:
308             self.call(["/sbin/chkconfig", s, "off"])
309
310 class XConfig(KickstartConfig):
311     """A class to apply a kickstart X configuration to a system."""
312     def apply(self, ksxconfig):
313         if ksxconfig.startX and os.path.exists(self.path("/etc/inittab")):
314             f = open(self.path("/etc/inittab"), "rw+")
315             buf = f.read()
316             buf = buf.replace("id:3:initdefault", "id:5:initdefault")
317             f.seek(0)
318             f.write(buf)
319             f.close()
320         if ksxconfig.defaultdesktop:
321             self._check_sysconfig()
322             f = open(self.path("/etc/sysconfig/desktop"), "w")
323             f.write("DESKTOP="+ksxconfig.defaultdesktop+"\n")
324             f.close()
325
326 class DesktopConfig(KickstartConfig):
327     """A class to apply a kickstart desktop configuration to a system."""
328     def apply(self, ksdesktop):
329         if ksdesktop.defaultdesktop:
330             self._check_sysconfig()
331             f = open(self.path("/etc/sysconfig/desktop"), "w")
332             f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n")
333             f.close()
334             if os.path.exists(self.path("/etc/gdm/custom.conf")):
335                 f = open(self.path("/etc/skel/.dmrc"), "w")
336                 f.write("[Desktop]\n")
337                 f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n")
338                 f.close()
339         if ksdesktop.session:
340             if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
341                 f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
342                 f.write("session="+ksdesktop.session.lower()+"\n")
343                 f.close()
344         if ksdesktop.autologinuser:
345             self._check_sysconfig()
346             f = open(self.path("/etc/sysconfig/desktop"), "a+")
347             f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n")
348             f.close()
349             if os.path.exists(self.path("/etc/gdm/custom.conf")):
350                 f = open(self.path("/etc/gdm/custom.conf"), "w")
351                 f.write("[daemon]\n")
352                 f.write("AutomaticLoginEnable=true\n")
353                 f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n")
354                 f.close()
355
356 class MoblinRepoConfig(KickstartConfig):
357     """A class to apply a kickstart desktop configuration to a system."""
358     def __create_repo_section(self, repo, type, fd):
359         baseurl = None
360         mirrorlist = None
361         reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"}
362         reponame = repo.name + reposuffix[type]
363         if type == "base":
364             if repo.baseurl:
365                 baseurl = repo.baseurl
366             if repo.mirrorlist:
367                 mirrorlist = repo.mirrorlist
368
369         elif type == "debuginfo":
370             if repo.baseurl:
371                 if repo.baseurl.endswith("/"):
372                     baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
373                 else:
374                     baseurl = os.path.dirname(repo.baseurl)
375                 baseurl += "/debug"
376
377             if repo.mirrorlist:
378                 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
379                 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
380                 mirrorlist += "debug" + "-" + variant
381
382         elif type == "source":
383             if repo.baseurl:
384                 if repo.baseurl.endswith("/"):
385                     baseurl = os.path.dirname(
386                                  os.path.dirname(
387                                     os.path.dirname(repo.baseurl)))
388                 else:
389                     baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
390                 baseurl += "/source"
391
392             if repo.mirrorlist:
393                 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
394                 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
395                 mirrorlist += "source" + "-" + variant
396
397         fd.write("[" + reponame + "]\n")
398         fd.write("name=" + reponame + "\n")
399         fd.write("failovermethod=priority\n")
400         if baseurl:
401             fd.write("baseurl=" + baseurl + "\n")
402         if mirrorlist:
403             fd.write("mirrorlist=" + mirrorlist + "\n")
404         """ Skip saving proxy settings """
405         #if repo.proxy:
406         #    fd.write("proxy=" + repo.proxy + "\n")
407         #if repo.proxy_username:
408         #    fd.write("proxy_username=" + repo.proxy_username + "\n")
409         #if repo.proxy_password:
410         #    fd.write("proxy_password=" + repo.proxy_password + "\n")
411         if repo.gpgkey:
412             fd.write("gpgkey=" + repo.gpgkey + "\n")
413             fd.write("gpgcheck=1\n")
414         else:
415             fd.write("gpgcheck=0\n")
416         if type == "source" or type == "debuginfo" or repo.disable:
417             fd.write("enabled=0\n")
418         else:
419             fd.write("enabled=1\n")
420         fd.write("\n")
421
422     def __create_repo_file(self, repo, repodir):
423         fs.makedirs(self.path(repodir))
424         f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
425         self.__create_repo_section(repo, "base", f)
426         if repo.debuginfo:
427             self.__create_repo_section(repo, "debuginfo", f)
428         if repo.source:
429             self.__create_repo_section(repo, "source", f)
430         f.close()
431
432     def apply(self, ksrepo, repodata):
433         for repo in ksrepo.repoList:
434             if repo.save:
435                 #self.__create_repo_file(repo, "/etc/yum.repos.d")
436                 self.__create_repo_file(repo, "/etc/zypp/repos.d")
437         """ Import repo gpg keys """
438         if repodata:
439             for repo in repodata:
440                 if repo['repokey']:
441                     runner.quiet(['rpm',
442                                   "--root=%s" % self.instroot,
443                                   "--import",
444                                   repo['repokey']])
445
446 class RPMMacroConfig(KickstartConfig):
447     """A class to apply the specified rpm macros to the filesystem"""
448     def apply(self, ks):
449         if not ks:
450             return
451         if not os.path.exists(self.path("/etc/rpm")):
452             os.mkdir(self.path("/etc/rpm"))
453         f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
454         if exclude_docs(ks):
455             f.write("%_excludedocs 1\n")
456         f.write("%__file_context_path %{nil}\n")
457         if inst_langs(ks) != None:
458             f.write("%_install_langs ")
459             f.write(inst_langs(ks))
460             f.write("\n")
461         f.close()
462
463 class NetworkConfig(KickstartConfig):
464     """A class to apply a kickstart network configuration to a system."""
465     def write_ifcfg(self, network):
466         p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
467
468         f = file(p, "w+")
469         os.chmod(p, 0644)
470
471         f.write("DEVICE=%s\n" % network.device)
472         f.write("BOOTPROTO=%s\n" % network.bootProto)
473
474         if network.bootProto.lower() == "static":
475             if network.ip:
476                 f.write("IPADDR=%s\n" % network.ip)
477             if network.netmask:
478                 f.write("NETMASK=%s\n" % network.netmask)
479
480         if network.onboot:
481             f.write("ONBOOT=on\n")
482         else:
483             f.write("ONBOOT=off\n")
484
485         if network.essid:
486             f.write("ESSID=%s\n" % network.essid)
487
488         if network.ethtool:
489             if network.ethtool.find("autoneg") == -1:
490                 network.ethtool = "autoneg off " + network.ethtool
491             f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
492
493         if network.bootProto.lower() == "dhcp":
494             if network.hostname:
495                 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
496             if network.dhcpclass:
497                 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
498
499         if network.mtu:
500             f.write("MTU=%s\n" % network.mtu)
501
502         f.close()
503
504     def write_wepkey(self, network):
505         if not network.wepkey:
506             return
507
508         p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
509         f = file(p, "w+")
510         os.chmod(p, 0600)
511         f.write("KEY=%s\n" % network.wepkey)
512         f.close()
513
514     def write_sysconfig(self, useipv6, hostname, gateway):
515         path = self.path("/etc/sysconfig/network")
516         f = file(path, "w+")
517         os.chmod(path, 0644)
518
519         f.write("NETWORKING=yes\n")
520
521         if useipv6:
522             f.write("NETWORKING_IPV6=yes\n")
523         else:
524             f.write("NETWORKING_IPV6=no\n")
525
526         if hostname:
527             f.write("HOSTNAME=%s\n" % hostname)
528         else:
529             f.write("HOSTNAME=localhost.localdomain\n")
530
531         if gateway:
532             f.write("GATEWAY=%s\n" % gateway)
533
534         f.close()
535
536     def write_hosts(self, hostname):
537         localline = ""
538         if hostname and hostname != "localhost.localdomain":
539             localline += hostname + " "
540             l = hostname.split(".")
541             if len(l) > 1:
542                 localline += l[0] + " "
543         localline += "localhost.localdomain localhost"
544
545         path = self.path("/etc/hosts")
546         f = file(path, "w+")
547         os.chmod(path, 0644)
548         f.write("127.0.0.1\t\t%s\n" % localline)
549         f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
550         f.close()
551
552     def write_resolv(self, nodns, nameservers):
553         if nodns or not nameservers:
554             return
555
556         path = self.path("/etc/resolv.conf")
557         f = file(path, "w+")
558         os.chmod(path, 0644)
559
560         for ns in (nameservers):
561             if ns:
562                 f.write("nameserver %s\n" % ns)
563
564         f.close()
565
566     def apply(self, ksnet):
567         fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
568
569         useipv6 = False
570         nodns = False
571         hostname = None
572         gateway = None
573         nameservers = None
574
575         for network in ksnet.network:
576             if not network.device:
577                 raise errors.KsError("No --device specified with "
578                                             "network kickstart command")
579
580             if (network.onboot and network.bootProto.lower() != "dhcp" and
581                 not (network.ip and network.netmask)):
582                 raise errors.KsError("No IP address and/or netmask "
583                                             "specified with static "
584                                             "configuration for '%s'" %
585                                             network.device)
586
587             self.write_ifcfg(network)
588             self.write_wepkey(network)
589
590             if network.ipv6:
591                 useipv6 = True
592             if network.nodns:
593                 nodns = True
594
595             if network.hostname:
596                 hostname = network.hostname
597             if network.gateway:
598                 gateway = network.gateway
599
600             if network.nameserver:
601                 nameservers = network.nameserver.split(",")
602
603         self.write_sysconfig(useipv6, hostname, gateway)
604         self.write_hosts(hostname)
605         self.write_resolv(nodns, nameservers)
606
607
608 def get_image_size(ks, default = None):
609     __size = 0
610     for p in ks.handler.partition.partitions:
611         if p.mountpoint == "/" and p.size:
612             __size = p.size
613     if __size > 0:
614         return int(__size) * 1024L * 1024L
615     else:
616         return default
617
618 def get_image_fstype(ks, default = None):
619     for p in ks.handler.partition.partitions:
620         if p.mountpoint == "/" and p.fstype:
621             return p.fstype
622     return default
623
624 def get_image_fsopts(ks, default = None):
625     for p in ks.handler.partition.partitions:
626         if p.mountpoint == "/" and p.fsopts:
627             return p.fstype
628     return default
629
630 def get_modules(ks):
631     devices = []
632     if isinstance(ks.handler.device, kscommands.device.FC3_Device):
633         devices.append(ks.handler.device)
634     else:
635         devices.extend(ks.handler.device.deviceList)
636
637     modules = []
638     for device in devices:
639         if not device.moduleName:
640             continue
641         modules.extend(device.moduleName.split(":"))
642
643     return modules
644
645 def get_timeout(ks, default = None):
646     if not hasattr(ks.handler.bootloader, "timeout"):
647         return default
648     if ks.handler.bootloader.timeout is None:
649         return default
650     return int(ks.handler.bootloader.timeout)
651
652 def get_kernel_args(ks, default = "ro liveimg"):
653     if not hasattr(ks.handler.bootloader, "appendLine"):
654         return default
655     if ks.handler.bootloader.appendLine is None:
656         return default
657     return "%s %s" %(default, ks.handler.bootloader.appendLine)
658
659 def get_menu_args(ks, default = ""):
660     if not hasattr(ks.handler.bootloader, "menus"):
661         return default
662     if ks.handler.bootloader.menus in (None, ""):
663         return default
664     return "%s" % ks.handler.bootloader.menus
665
666 def get_default_kernel(ks, default = None):
667     if not hasattr(ks.handler.bootloader, "default"):
668         return default
669     if not ks.handler.bootloader.default:
670         return default
671     return ks.handler.bootloader.default
672
673 def get_repos(ks, repo_urls = {}):
674     repos = {}
675     for repo in ks.handler.repo.repoList:
676         inc = []
677         if hasattr(repo, "includepkgs"):
678             inc.extend(repo.includepkgs)
679
680         exc = []
681         if hasattr(repo, "excludepkgs"):
682             exc.extend(repo.excludepkgs)
683
684         baseurl = repo.baseurl
685         mirrorlist = repo.mirrorlist
686
687         if repo.name in repo_urls:
688             baseurl = repo_urls[repo.name]
689             mirrorlist = None
690
691         if repos.has_key(repo.name):
692             msger.warning("Overriding already specified repo %s" %(repo.name,))
693
694         proxy = None
695         if hasattr(repo, "proxy"):
696             proxy = repo.proxy
697         proxy_username = None
698         if hasattr(repo, "proxy_username"):
699             proxy_username = repo.proxy_username
700         proxy_password = None
701         if hasattr(repo, "proxy_password"):
702             proxy_password = repo.proxy_password
703         if hasattr(repo, "debuginfo"):
704             debuginfo = repo.debuginfo
705         if hasattr(repo, "source"):
706             source = repo.source
707         if hasattr(repo, "gpgkey"):
708             gpgkey = repo.gpgkey
709         if hasattr(repo, "disable"):
710             disable = repo.disable
711         ssl_verify = True
712         if hasattr(repo, "ssl_verify"):
713             ssl_verify = repo.ssl_verify == "yes"
714         cost = None
715         if hasattr(repo, "cost"):
716             cost = repo.cost
717         priority = None
718         if hasattr(repo, "priority"):
719             priority = repo.priority
720
721         repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc,
722                             proxy, proxy_username, proxy_password, debuginfo,
723                             source, gpgkey, disable, ssl_verify, cost, priority)
724
725     return repos.values()
726
727 def convert_method_to_repo(ks):
728     try:
729         ks.handler.repo.methodToRepo()
730     except (AttributeError, kserrors.KickstartError):
731         pass
732
733 def get_packages(ks, required = []):
734     return ks.handler.packages.packageList + required
735
736 def get_groups(ks, required = []):
737     return ks.handler.packages.groupList + required
738
739 def get_excluded(ks, required = []):
740     return ks.handler.packages.excludedList + required
741
742 def get_partitions(ks, required = []):
743     return ks.handler.partition.partitions
744
745 def ignore_missing(ks):
746     return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
747
748 def exclude_docs(ks):
749     return ks.handler.packages.excludeDocs
750
751 def inst_langs(ks):
752     if hasattr(ks.handler.packages, "instLange"):
753         return ks.handler.packages.instLange
754     elif hasattr(ks.handler.packages, "instLangs"):
755         return ks.handler.packages.instLangs
756     return ""
757
758 def get_post_scripts(ks):
759     scripts = []
760     for s in ks.handler.scripts:
761         if s.type != ksparser.KS_SCRIPT_POST:
762             continue
763         scripts.append(s)
764     return scripts
765
766 def add_repo(ks, repostr):
767     args = repostr.split()
768     repoobj = ks.handler.repo.parse(args[1:])
769     if repoobj and repoobj not in ks.handler.repo.repoList:
770         ks.handler.repo.repoList.append(repoobj)
771
772 def remove_all_repos(ks):
773     while len(ks.handler.repo.repoList) != 0:
774         del ks.handler.repo.repoList[0]
775
776 def remove_duplicate_repos(ks):
777     i = 0
778     j = i + 1
779     while True:
780         if len(ks.handler.repo.repoList) < 2:
781             break
782         if i >= len(ks.handler.repo.repoList) - 1:
783             break
784         name = ks.handler.repo.repoList[i].name
785         baseurl = ks.handler.repo.repoList[i].baseurl
786         if j < len(ks.handler.repo.repoList):
787             if (ks.handler.repo.repoList[j].name == name or \
788                 ks.handler.repo.repoList[j].baseurl == baseurl):
789                 del ks.handler.repo.repoList[j]
790             else:
791                 j += 1
792             if j >= len(ks.handler.repo.repoList):
793                 i += 1
794                 j = i + 1
795         else:
796             i += 1
797             j = i + 1
798
799 def resolve_groups(creatoropts, repometadata):
800     iszypp = False
801     if 'zypp' == creatoropts['pkgmgr']:
802         iszypp = True
803     ks = creatoropts['ks']
804
805     for repo in repometadata:
806         """ Mustn't replace group with package list if repo is ready for the
807             corresponding package manager.
808         """
809
810         if iszypp and repo["patterns"]:
811             continue
812         if not iszypp and repo["comps"]:
813             continue
814
815         # But we also must handle such cases, use zypp but repo only has comps,
816         # use yum but repo only has patterns, use zypp but use_comps is true,
817         # use yum but use_comps is false.
818         groupfile = None
819         if iszypp and repo["comps"]:
820             groupfile = repo["comps"]
821             get_pkglist_handler = misc.get_pkglist_in_comps
822         if not iszypp and repo["patterns"]:
823             groupfile = repo["patterns"]
824             get_pkglist_handler = misc.get_pkglist_in_patterns
825
826         if groupfile:
827             i = 0
828             while True:
829                 if i >= len(ks.handler.packages.groupList):
830                     break
831                 pkglist = get_pkglist_handler(
832                                         ks.handler.packages.groupList[i].name,
833                                         groupfile)
834                 if pkglist:
835                     del ks.handler.packages.groupList[i]
836                     for pkg in pkglist:
837                         if pkg not in ks.handler.packages.packageList:
838                             ks.handler.packages.packageList.append(pkg)
839                 else:
840                     i = i + 1