Change default parameters from mutable type into immutable.
[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 string
23
24 import pykickstart.sections as kssections
25 import pykickstart.commands as kscommands
26 import pykickstart.constants as ksconstants
27 import pykickstart.errors as kserrors
28 import pykickstart.parser as ksparser
29 import pykickstart.version as ksversion
30 from pykickstart.handlers.control import commandMap
31 from pykickstart.handlers.control import dataMap
32
33 from mic import msger
34 from mic.utils import errors, misc, runner, fs_related as fs
35 from custom_commands import desktop, micrepo, micboot, partition
36
37
38 AUTH_URL_PTN = r"(?P<scheme>.*)://(?P<username>.*)(:?P<password>.*)?@(?P<url>.*)"
39
40
41 class PrepackageSection(kssections.Section):
42     sectionOpen = "%prepackages"
43
44     def handleLine(self, line):
45         if not self.handler:
46             return
47
48         (h, s, t) = line.partition('#')
49         line = h.rstrip()
50
51         self.handler.prepackages.add([line])
52
53     def handleHeader(self, lineno, args):
54         kssections.Section.handleHeader(self, lineno, args)
55
56 class AttachmentSection(kssections.Section):
57     sectionOpen = "%attachment"
58
59     def handleLine(self, line):
60         if not self.handler:
61             return
62
63         (h, s, t) = line.partition('#')
64         line = h.rstrip()
65
66         self.handler.attachment.add([line])
67
68     def handleHeader(self, lineno, args):
69         kssections.Section.handleHeader(self, lineno, args)
70
71 def apply_wrapper(func):
72     def wrapper(*kargs, **kwargs):
73         try:
74             func(*kargs, **kwargs)
75         except (OSError, IOError, errors.KsError), err:
76             cfgcls = kargs[0].__class__.__name__
77             if msger.ask("Failed to apply %s, skip and continue?" % cfgcls):
78                 msger.warning("%s" % err)
79                 pass
80             else:
81                 # just throw out the exception
82                 raise
83     return wrapper
84
85 def read_kickstart(path):
86     """Parse a kickstart file and return a KickstartParser instance.
87
88     This is a simple utility function which takes a path to a kickstart file,
89     parses it and returns a pykickstart KickstartParser instance which can
90     be then passed to an ImageCreator constructor.
91
92     If an error occurs, a CreatorError exception is thrown.
93     """
94
95     #version = ksversion.makeVersion()
96     #ks = ksparser.KickstartParser(version)
97
98     using_version = ksversion.DEVEL
99     commandMap[using_version]["desktop"] = desktop.Mic_Desktop
100     commandMap[using_version]["repo"] = micrepo.Mic_Repo
101     commandMap[using_version]["bootloader"] = micboot.Mic_Bootloader
102     commandMap[using_version]["part"] = partition.Mic_Partition
103     commandMap[using_version]["partition"] = partition.Mic_Partition
104     dataMap[using_version]["RepoData"] = micrepo.Mic_RepoData
105     dataMap[using_version]["PartData"] = partition.Mic_PartData
106     superclass = ksversion.returnClassForVersion(version=using_version)
107
108     class KSHandlers(superclass):
109         def __init__(self):
110             superclass.__init__(self, mapping=commandMap[using_version])
111             self.prepackages = ksparser.Packages()
112             self.attachment = ksparser.Packages()
113
114     ks = ksparser.KickstartParser(KSHandlers(), errorsAreFatal=False)
115     ks.registerSection(PrepackageSection(ks.handler))
116     ks.registerSection(AttachmentSection(ks.handler))
117
118     try:
119         ks.readKickstart(path)
120     except (kserrors.KickstartParseError, kserrors.KickstartError), err:
121         if msger.ask("Errors occured on kickstart file, skip and continue?"):
122             msger.warning("%s" % err)
123             pass
124         else:
125             raise errors.KsError("%s" % err)
126
127     return ks
128
129 class KickstartConfig(object):
130     """A base class for applying kickstart configurations to a system."""
131     def __init__(self, instroot):
132         self.instroot = instroot
133
134     def path(self, subpath):
135         return self.instroot + subpath
136
137     def _check_sysconfig(self):
138         if not os.path.exists(self.path("/etc/sysconfig")):
139             fs.makedirs(self.path("/etc/sysconfig"))
140
141     def chroot(self):
142         os.chroot(self.instroot)
143         os.chdir("/")
144
145     def call(self, args):
146         if not os.path.exists("%s/%s" %(self.instroot, args[0])):
147             raise errors.KsError("Can't find %s in chroot" % args[0])
148         subprocess.call(args, preexec_fn = self.chroot)
149
150     def apply(self):
151         pass
152
153 class LanguageConfig(KickstartConfig):
154     """A class to apply a kickstart language configuration to a system."""
155     @apply_wrapper
156     def apply(self, kslang):
157         self._check_sysconfig()
158         if kslang.lang:
159             f = open(self.path("/etc/sysconfig/i18n"), "w+")
160             f.write("LANG=\"" + kslang.lang + "\"\n")
161             f.close()
162
163 class KeyboardConfig(KickstartConfig):
164     """A class to apply a kickstart keyboard configuration to a system."""
165     @apply_wrapper
166     def apply(self, kskeyboard):
167         #
168         # FIXME:
169         #   should this impact the X keyboard config too?
170         #   or do we want to make X be able to do this mapping?
171         #
172         #k = rhpl.keyboard.Keyboard()
173         #if kskeyboard.keyboard:
174         #   k.set(kskeyboard.keyboard)
175         #k.write(self.instroot)
176         pass
177
178 class TimezoneConfig(KickstartConfig):
179     """A class to apply a kickstart timezone configuration to a system."""
180     @apply_wrapper
181     def apply(self, kstimezone):
182         self._check_sysconfig()
183         tz = kstimezone.timezone or "America/New_York"
184         utc = str(kstimezone.isUtc)
185
186         f = open(self.path("/etc/sysconfig/clock"), "w+")
187         f.write("ZONE=\"" + tz + "\"\n")
188         f.write("UTC=" + utc + "\n")
189         f.close()
190         tz_source = "/usr/share/zoneinfo/%s" % (tz)
191         tz_dest = "/etc/localtime"
192         try:
193             cpcmd = fs.find_binary_inchroot('cp', self.instroot)
194             if cpcmd:
195                 self.call([cpcmd, "-f", tz_source, tz_dest])
196             else:
197                 cpcmd = fs.find_binary_path('cp')
198                 subprocess.call([cpcmd, "-f",
199                                  self.path(tz_source),
200                                  self.path(tz_dest)])
201         except (IOError, OSError), (errno, msg):
202             raise errors.KsError("Timezone setting error: %s" % msg)
203
204 class AuthConfig(KickstartConfig):
205     """A class to apply a kickstart authconfig configuration to a system."""
206     @apply_wrapper
207     def apply(self, ksauthconfig):
208         auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
209         args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
210         self.call(args + auth.split())
211
212 class FirewallConfig(KickstartConfig):
213     """A class to apply a kickstart firewall configuration to a system."""
214     @apply_wrapper
215     def apply(self, ksfirewall):
216         #
217         # FIXME: should handle the rest of the options
218         #
219         if not os.path.exists(self.path("/usr/sbin/lokkit")):
220             return
221         if ksfirewall.enabled:
222             status = "--enabled"
223         else:
224             status = "--disabled"
225
226         self.call(["/usr/sbin/lokkit",
227                    "-f", "--quiet", "--nostart", status])
228
229 class RootPasswordConfig(KickstartConfig):
230     """A class to apply a kickstart root password configuration to a system."""
231     def unset(self):
232         self.call(["/usr/bin/passwd", "-d", "root"])
233
234     def set_encrypted(self, password):
235         self.call(["/usr/sbin/usermod", "-p", password, "root"])
236
237     def set_unencrypted(self, password):
238         for p in ("/bin/echo", "/usr/sbin/chpasswd"):
239             if not os.path.exists("%s/%s" %(self.instroot, p)):
240                 raise errors.KsError("Unable to set unencrypted password due "
241                                      "to lack of %s" % p)
242
243         p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
244                               stdout = subprocess.PIPE,
245                               preexec_fn = self.chroot)
246         p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
247                               stdin = p1.stdout,
248                               stdout = subprocess.PIPE,
249                               preexec_fn = self.chroot)
250         p2.communicate()
251
252     @apply_wrapper
253     def apply(self, ksrootpw):
254         if ksrootpw.isCrypted:
255             self.set_encrypted(ksrootpw.password)
256         elif ksrootpw.password != "":
257             self.set_unencrypted(ksrootpw.password)
258         else:
259             self.unset()
260
261 class UserConfig(KickstartConfig):
262     def set_empty_passwd(self, user):
263         self.call(["/usr/bin/passwd", "-d", user])
264
265     def set_encrypted_passwd(self, user, password):
266         self.call(["/usr/sbin/usermod", "-p", "%s" % password, user])
267
268     def set_unencrypted_passwd(self, user, password):
269         for p in ("/bin/echo", "/usr/sbin/chpasswd"):
270             if not os.path.exists("%s/%s" %(self.instroot, p)):
271                 raise errors.KsError("Unable to set unencrypted password due "
272                                      "to lack of %s" % p)
273
274         p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
275                               stdout = subprocess.PIPE,
276                               preexec_fn = self.chroot)
277         p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
278                               stdin = p1.stdout,
279                               stdout = subprocess.PIPE,
280                               preexec_fn = self.chroot)
281         p2.communicate()
282
283     def addUser(self, userconfig):
284         args = [ "/usr/sbin/useradd" ]
285         if userconfig.groups:
286             args += [ "--groups", string.join(userconfig.groups, ",") ]
287         if userconfig.name:
288             args += [ "-m"]
289             args += [ "-d", "/home/%s" % userconfig.name  ]
290             args.append(userconfig.name)
291             try:
292                 dev_null = os.open("/dev/null", os.O_WRONLY)
293                 msger.debug('adding user with %s' % args)
294                 subprocess.call(args,
295                                  stdout = dev_null,
296                                  stderr = dev_null,
297                                  preexec_fn = self.chroot)
298                 os.close(dev_null)
299             except:
300                 msger.warning('Cannot add user using "useradd"')
301
302             if userconfig.password not in (None, ""):
303                 if userconfig.isCrypted:
304                     self.set_encrypted_passwd(userconfig.name,
305                                               userconfig.password)
306                 else:
307                     self.set_unencrypted_passwd(userconfig.name,
308                                                 userconfig.password)
309             else:
310                 self.set_empty_passwd(userconfig.name)
311         else:
312             raise errors.KsError("Invalid kickstart command: %s" \
313                                  % userconfig.__str__())
314
315     @apply_wrapper
316     def apply(self, user):
317         for userconfig in user.userList:
318             try:
319                 self.addUser(userconfig)
320             except:
321                 raise
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
643 def get_image_size(ks, default = None):
644     __size = 0
645     for p in ks.handler.partition.partitions:
646         if p.mountpoint == "/" and p.size:
647             __size = p.size
648     if __size > 0:
649         return int(__size) * 1024L * 1024L
650     else:
651         return default
652
653 def get_image_fstype(ks, default = None):
654     for p in ks.handler.partition.partitions:
655         if p.mountpoint == "/" and p.fstype:
656             return p.fstype
657     return default
658
659 def get_image_fsopts(ks, default = None):
660     for p in ks.handler.partition.partitions:
661         if p.mountpoint == "/" and p.fsopts:
662             return p.fsopts
663     return default
664
665 def get_modules(ks):
666     devices = []
667     if isinstance(ks.handler.device, kscommands.device.FC3_Device):
668         devices.append(ks.handler.device)
669     else:
670         devices.extend(ks.handler.device.deviceList)
671
672     modules = []
673     for device in devices:
674         if not device.moduleName:
675             continue
676         modules.extend(device.moduleName.split(":"))
677
678     return modules
679
680 def get_timeout(ks, default = None):
681     if not hasattr(ks.handler.bootloader, "timeout"):
682         return default
683     if ks.handler.bootloader.timeout is None:
684         return default
685     return int(ks.handler.bootloader.timeout)
686
687 def get_kernel_args(ks, default = "ro rd.live.image"):
688     if not hasattr(ks.handler.bootloader, "appendLine"):
689         return default
690     if ks.handler.bootloader.appendLine is None:
691         return default
692     return "%s %s" %(default, ks.handler.bootloader.appendLine)
693
694 def get_menu_args(ks, default = ""):
695     if not hasattr(ks.handler.bootloader, "menus"):
696         return default
697     if ks.handler.bootloader.menus in (None, ""):
698         return default
699     return "%s" % ks.handler.bootloader.menus
700
701 def get_default_kernel(ks, default = None):
702     if not hasattr(ks.handler.bootloader, "default"):
703         return default
704     if not ks.handler.bootloader.default:
705         return default
706     return ks.handler.bootloader.default
707
708 def get_repos(ks, repo_urls=None):
709     repos = {}
710     for repo in ks.handler.repo.repoList:
711         inc = []
712         if hasattr(repo, "includepkgs"):
713             inc.extend(repo.includepkgs)
714
715         exc = []
716         if hasattr(repo, "excludepkgs"):
717             exc.extend(repo.excludepkgs)
718
719         baseurl = repo.baseurl
720         mirrorlist = repo.mirrorlist
721
722         if repo_urls and repo.name in repo_urls:
723             baseurl = repo_urls[repo.name]
724             mirrorlist = None
725
726         if repos.has_key(repo.name):
727             msger.warning("Overriding already specified repo %s" %(repo.name,))
728
729         proxy = None
730         if hasattr(repo, "proxy"):
731             proxy = repo.proxy
732         proxy_username = None
733         if hasattr(repo, "proxy_username"):
734             proxy_username = repo.proxy_username
735         proxy_password = None
736         if hasattr(repo, "proxy_password"):
737             proxy_password = repo.proxy_password
738         if hasattr(repo, "debuginfo"):
739             debuginfo = repo.debuginfo
740         if hasattr(repo, "source"):
741             source = repo.source
742         if hasattr(repo, "gpgkey"):
743             gpgkey = repo.gpgkey
744         if hasattr(repo, "disable"):
745             disable = repo.disable
746         ssl_verify = True
747         if hasattr(repo, "ssl_verify"):
748             ssl_verify = repo.ssl_verify == "yes"
749         nocache = False
750         if hasattr(repo, "nocache"):
751             nocache = repo.nocache
752         cost = None
753         if hasattr(repo, "cost"):
754             cost = repo.cost
755         priority = None
756         if hasattr(repo, "priority"):
757             priority = repo.priority
758
759         repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc,
760                             proxy, proxy_username, proxy_password, debuginfo,
761                             source, gpgkey, disable, ssl_verify, nocache,
762                             cost, priority)
763
764     return repos.values()
765
766 def convert_method_to_repo(ks):
767     try:
768         ks.handler.repo.methodToRepo()
769     except (AttributeError, kserrors.KickstartError):
770         pass
771
772 def get_attachment(ks, required=()):
773     return ks.handler.attachment.packageList + list(required)
774
775 def get_pre_packages(ks, required=()):
776     return ks.handler.prepackages.packageList + list(required)
777
778 def get_packages(ks, required=()):
779     return ks.handler.packages.packageList + list(required)
780
781 def get_groups(ks, required=()):
782     return ks.handler.packages.groupList + list(required)
783
784 def get_excluded(ks, required=()):
785     return ks.handler.packages.excludedList + list(required)
786
787 def get_partitions(ks):
788     return ks.handler.partition.partitions
789
790 def ignore_missing(ks):
791     return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
792
793 def exclude_docs(ks):
794     return ks.handler.packages.excludeDocs
795
796 def inst_langs(ks):
797     if hasattr(ks.handler.packages, "instLange"):
798         return ks.handler.packages.instLange
799     elif hasattr(ks.handler.packages, "instLangs"):
800         return ks.handler.packages.instLangs
801     return ""
802
803 def get_post_scripts(ks):
804     scripts = []
805     for s in ks.handler.scripts:
806         if s.type != ksparser.KS_SCRIPT_POST:
807             continue
808         scripts.append(s)
809     return scripts
810
811 def add_repo(ks, repostr):
812     args = repostr.split()
813     repoobj = ks.handler.repo.parse(args[1:])
814     if repoobj and repoobj not in ks.handler.repo.repoList:
815         ks.handler.repo.repoList.append(repoobj)
816
817 def remove_all_repos(ks):
818     while len(ks.handler.repo.repoList) != 0:
819         del ks.handler.repo.repoList[0]
820
821 def remove_duplicate_repos(ks):
822     i = 0
823     j = i + 1
824     while True:
825         if len(ks.handler.repo.repoList) < 2:
826             break
827         if i >= len(ks.handler.repo.repoList) - 1:
828             break
829         name = ks.handler.repo.repoList[i].name
830         baseurl = ks.handler.repo.repoList[i].baseurl
831         if j < len(ks.handler.repo.repoList):
832             if (ks.handler.repo.repoList[j].name == name or \
833                 ks.handler.repo.repoList[j].baseurl == baseurl):
834                 del ks.handler.repo.repoList[j]
835             else:
836                 j += 1
837             if j >= len(ks.handler.repo.repoList):
838                 i += 1
839                 j = i + 1
840         else:
841             i += 1
842             j = i + 1
843
844 def resolve_groups(creatoropts, repometadata):
845     iszypp = False
846     if 'zypp' == creatoropts['pkgmgr']:
847         iszypp = True
848     ks = creatoropts['ks']
849
850     for repo in repometadata:
851         """ Mustn't replace group with package list if repo is ready for the
852             corresponding package manager.
853         """
854
855         if iszypp and repo["patterns"]:
856             continue
857         if not iszypp and repo["comps"]:
858             continue
859
860         # But we also must handle such cases, use zypp but repo only has comps,
861         # use yum but repo only has patterns, use zypp but use_comps is true,
862         # use yum but use_comps is false.
863         groupfile = None
864         if iszypp and repo["comps"]:
865             groupfile = repo["comps"]
866             get_pkglist_handler = misc.get_pkglist_in_comps
867         if not iszypp and repo["patterns"]:
868             groupfile = repo["patterns"]
869             get_pkglist_handler = misc.get_pkglist_in_patterns
870
871         if groupfile:
872             i = 0
873             while True:
874                 if i >= len(ks.handler.packages.groupList):
875                     break
876                 pkglist = get_pkglist_handler(
877                                         ks.handler.packages.groupList[i].name,
878                                         groupfile)
879                 if pkglist:
880                     del ks.handler.packages.groupList[i]
881                     for pkg in pkglist:
882                         if pkg not in ks.handler.packages.packageList:
883                             ks.handler.packages.packageList.append(pkg)
884                 else:
885                     i = i + 1