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