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