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