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