tolerate some OS errors in the image confirations stage
[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             msger.warning("Error copying timezone info from "
210                                         "'%s' to '%s': %s" \
211                                         % (tz_source, tz_dest, msg))
212
213 class AuthConfig(KickstartConfig):
214     """A class to apply a kickstart authconfig configuration to a system."""
215     def apply(self, ksauthconfig):
216         auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
217         args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
218         self.call(args + auth.split())
219
220 class FirewallConfig(KickstartConfig):
221     """A class to apply a kickstart firewall configuration to a system."""
222     def apply(self, ksfirewall):
223         #
224         # FIXME: should handle the rest of the options
225         #
226         if not os.path.exists(self.path("/usr/sbin/lokkit")):
227             return
228         if ksfirewall.enabled:
229             status = "--enabled"
230         else:
231             status = "--disabled"
232
233         self.call(["/usr/sbin/lokkit",
234                    "-f", "--quiet", "--nostart", status])
235
236 class RootPasswordConfig(KickstartConfig):
237     """A class to apply a kickstart root password configuration to a system."""
238     def unset(self):
239         self.call(["/usr/bin/passwd", "-d", "root"])
240
241     def set_encrypted(self, password):
242         self.call(["/usr/sbin/usermod", "-p", password, "root"])
243
244     def set_unencrypted(self, password):
245         for p in ("/bin/echo", "/usr/sbin/chpasswd"):
246             if not os.path.exists("%s/%s" %(self.instroot, p)):
247                 raise errors.KsError("Unable to set unencrypted password due "
248                                      "to lack of %s" % p)
249
250         p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
251                               stdout = subprocess.PIPE,
252                               preexec_fn = self.chroot)
253         p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
254                               stdin = p1.stdout,
255                               stdout = subprocess.PIPE,
256                               preexec_fn = self.chroot)
257         p2.communicate()
258
259     def apply(self, ksrootpw):
260         if ksrootpw.isCrypted:
261             self.set_encrypted(ksrootpw.password)
262         elif ksrootpw.password != "":
263             self.set_unencrypted(ksrootpw.password)
264         else:
265             self.unset()
266
267 class UserConfig(KickstartConfig):
268     def set_empty_passwd(self, user):
269         self.call(["/usr/bin/passwd", "-d", user])
270
271     def set_encrypted_passwd(self, user, password):
272         self.call(["/usr/sbin/usermod", "-p", "%s" % password, user])
273
274     def set_unencrypted_passwd(self, user, password):
275         for p in ("/bin/echo", "/usr/sbin/chpasswd"):
276             if not os.path.exists("%s/%s" %(self.instroot, p)):
277                 raise errors.KsError("Unable to set unencrypted password due "
278                                      "to lack of %s" % p)
279
280         p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
281                               stdout = subprocess.PIPE,
282                               preexec_fn = self.chroot)
283         p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
284                               stdin = p1.stdout,
285                               stdout = subprocess.PIPE,
286                               preexec_fn = self.chroot)
287         p2.communicate()
288
289     def addUser(self, userconfig):
290         args = [ "/usr/sbin/useradd" ]
291         if userconfig.groups:
292             args += [ "--groups", string.join(userconfig.groups, ",") ]
293         if userconfig.name:
294             args.append(userconfig.name)
295             try:
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             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     def apply(self, user):
319         for userconfig in user.userList:
320             try:
321                 self.addUser(userconfig)
322             except:
323                 raise
324
325 class ServicesConfig(KickstartConfig):
326     """A class to apply a kickstart services configuration to a system."""
327     def apply(self, ksservices):
328         if not os.path.exists(self.path("/sbin/chkconfig")):
329             return
330         for s in ksservices.enabled:
331             self.call(["/sbin/chkconfig", s, "on"])
332         for s in ksservices.disabled:
333             self.call(["/sbin/chkconfig", s, "off"])
334
335 class XConfig(KickstartConfig):
336     """A class to apply a kickstart X configuration to a system."""
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     def apply(self, ksdesktop):
354         if ksdesktop.defaultdesktop:
355             self._check_sysconfig()
356             f = open(self.path("/etc/sysconfig/desktop"), "w")
357             f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n")
358             f.close()
359             if os.path.exists(self.path("/etc/gdm/custom.conf")):
360                 f = open(self.path("/etc/skel/.dmrc"), "w")
361                 f.write("[Desktop]\n")
362                 f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n")
363                 f.close()
364         if ksdesktop.session:
365             if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
366                 f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
367                 f.write("session="+ksdesktop.session.lower()+"\n")
368                 f.close()
369         if ksdesktop.autologinuser:
370             self._check_sysconfig()
371             f = open(self.path("/etc/sysconfig/desktop"), "a+")
372             f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n")
373             f.close()
374             if os.path.exists(self.path("/etc/gdm/custom.conf")):
375                 f = open(self.path("/etc/gdm/custom.conf"), "w")
376                 f.write("[daemon]\n")
377                 f.write("AutomaticLoginEnable=true\n")
378                 f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n")
379                 f.close()
380
381 class MoblinRepoConfig(KickstartConfig):
382     """A class to apply a kickstart desktop configuration to a system."""
383     def __create_repo_section(self, repo, type, fd):
384         baseurl = None
385         mirrorlist = None
386         reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"}
387         reponame = repo.name + reposuffix[type]
388         if type == "base":
389             if repo.baseurl:
390                 baseurl = repo.baseurl
391             if repo.mirrorlist:
392                 mirrorlist = repo.mirrorlist
393
394         elif type == "debuginfo":
395             if repo.baseurl:
396                 if repo.baseurl.endswith("/"):
397                     baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
398                 else:
399                     baseurl = os.path.dirname(repo.baseurl)
400                 baseurl += "/debug"
401
402             if repo.mirrorlist:
403                 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
404                 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
405                 mirrorlist += "debug" + "-" + variant
406
407         elif type == "source":
408             if repo.baseurl:
409                 if repo.baseurl.endswith("/"):
410                     baseurl = os.path.dirname(
411                                  os.path.dirname(
412                                     os.path.dirname(repo.baseurl)))
413                 else:
414                     baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
415                 baseurl += "/source"
416
417             if repo.mirrorlist:
418                 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
419                 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
420                 mirrorlist += "source" + "-" + variant
421
422         fd.write("[" + reponame + "]\n")
423         fd.write("name=" + reponame + "\n")
424         fd.write("failovermethod=priority\n")
425         if baseurl:
426             fd.write("baseurl=" + baseurl + "\n")
427         if mirrorlist:
428             fd.write("mirrorlist=" + mirrorlist + "\n")
429         """ Skip saving proxy settings """
430         #if repo.proxy:
431         #    fd.write("proxy=" + repo.proxy + "\n")
432         #if repo.proxy_username:
433         #    fd.write("proxy_username=" + repo.proxy_username + "\n")
434         #if repo.proxy_password:
435         #    fd.write("proxy_password=" + repo.proxy_password + "\n")
436         if repo.gpgkey:
437             fd.write("gpgkey=" + repo.gpgkey + "\n")
438             fd.write("gpgcheck=1\n")
439         else:
440             fd.write("gpgcheck=0\n")
441         if type == "source" or type == "debuginfo" or repo.disable:
442             fd.write("enabled=0\n")
443         else:
444             fd.write("enabled=1\n")
445         fd.write("\n")
446
447     def __create_repo_file(self, repo, repodir):
448         fs.makedirs(self.path(repodir))
449         f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
450         self.__create_repo_section(repo, "base", f)
451         if repo.debuginfo:
452             self.__create_repo_section(repo, "debuginfo", f)
453         if repo.source:
454             self.__create_repo_section(repo, "source", f)
455         f.close()
456
457     def apply(self, ksrepo, repodata):
458         for repo in ksrepo.repoList:
459             if repo.save:
460                 #self.__create_repo_file(repo, "/etc/yum.repos.d")
461                 self.__create_repo_file(repo, "/etc/zypp/repos.d")
462         """ Import repo gpg keys """
463         if repodata:
464             for repo in repodata:
465                 if repo['repokey']:
466                     runner.quiet(['rpm',
467                                   "--root=%s" % self.instroot,
468                                   "--import",
469                                   repo['repokey']])
470
471 class RPMMacroConfig(KickstartConfig):
472     """A class to apply the specified rpm macros to the filesystem"""
473     def apply(self, ks):
474         if not ks:
475             return
476         if not os.path.exists(self.path("/etc/rpm")):
477             os.mkdir(self.path("/etc/rpm"))
478         f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
479         if exclude_docs(ks):
480             f.write("%_excludedocs 1\n")
481         f.write("%__file_context_path %{nil}\n")
482         if inst_langs(ks) != None:
483             f.write("%_install_langs ")
484             f.write(inst_langs(ks))
485             f.write("\n")
486         f.close()
487
488 class NetworkConfig(KickstartConfig):
489     """A class to apply a kickstart network configuration to a system."""
490     def write_ifcfg(self, network):
491         p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
492
493         f = file(p, "w+")
494         os.chmod(p, 0644)
495
496         f.write("DEVICE=%s\n" % network.device)
497         f.write("BOOTPROTO=%s\n" % network.bootProto)
498
499         if network.bootProto.lower() == "static":
500             if network.ip:
501                 f.write("IPADDR=%s\n" % network.ip)
502             if network.netmask:
503                 f.write("NETMASK=%s\n" % network.netmask)
504
505         if network.onboot:
506             f.write("ONBOOT=on\n")
507         else:
508             f.write("ONBOOT=off\n")
509
510         if network.essid:
511             f.write("ESSID=%s\n" % network.essid)
512
513         if network.ethtool:
514             if network.ethtool.find("autoneg") == -1:
515                 network.ethtool = "autoneg off " + network.ethtool
516             f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
517
518         if network.bootProto.lower() == "dhcp":
519             if network.hostname:
520                 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
521             if network.dhcpclass:
522                 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
523
524         if network.mtu:
525             f.write("MTU=%s\n" % network.mtu)
526
527         f.close()
528
529     def write_wepkey(self, network):
530         if not network.wepkey:
531             return
532
533         p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
534         f = file(p, "w+")
535         os.chmod(p, 0600)
536         f.write("KEY=%s\n" % network.wepkey)
537         f.close()
538
539     def write_sysconfig(self, useipv6, hostname, gateway):
540         path = self.path("/etc/sysconfig/network")
541         f = file(path, "w+")
542         os.chmod(path, 0644)
543
544         f.write("NETWORKING=yes\n")
545
546         if useipv6:
547             f.write("NETWORKING_IPV6=yes\n")
548         else:
549             f.write("NETWORKING_IPV6=no\n")
550
551         if hostname:
552             f.write("HOSTNAME=%s\n" % hostname)
553         else:
554             f.write("HOSTNAME=localhost.localdomain\n")
555
556         if gateway:
557             f.write("GATEWAY=%s\n" % gateway)
558
559         f.close()
560
561     def write_hosts(self, hostname):
562         localline = ""
563         if hostname and hostname != "localhost.localdomain":
564             localline += hostname + " "
565             l = hostname.split(".")
566             if len(l) > 1:
567                 localline += l[0] + " "
568         localline += "localhost.localdomain localhost"
569
570         path = self.path("/etc/hosts")
571         f = file(path, "w+")
572         os.chmod(path, 0644)
573         f.write("127.0.0.1\t\t%s\n" % localline)
574         f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
575         f.close()
576
577     def write_resolv(self, nodns, nameservers):
578         if nodns or not nameservers:
579             return
580
581         path = self.path("/etc/resolv.conf")
582         f = file(path, "w+")
583         os.chmod(path, 0644)
584
585         for ns in (nameservers):
586             if ns:
587                 f.write("nameserver %s\n" % ns)
588
589         f.close()
590
591     def apply(self, ksnet):
592         fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
593
594         useipv6 = False
595         nodns = False
596         hostname = None
597         gateway = None
598         nameservers = None
599
600         for network in ksnet.network:
601             if not network.device:
602                 raise errors.KsError("No --device specified with "
603                                             "network kickstart command")
604
605             if (network.onboot and network.bootProto.lower() != "dhcp" and
606                 not (network.ip and network.netmask)):
607                 raise errors.KsError("No IP address and/or netmask "
608                                             "specified with static "
609                                             "configuration for '%s'" %
610                                             network.device)
611
612             self.write_ifcfg(network)
613             self.write_wepkey(network)
614
615             if network.ipv6:
616                 useipv6 = True
617             if network.nodns:
618                 nodns = True
619
620             if network.hostname:
621                 hostname = network.hostname
622             if network.gateway:
623                 gateway = network.gateway
624
625             if network.nameserver:
626                 nameservers = network.nameserver.split(",")
627
628         self.write_sysconfig(useipv6, hostname, gateway)
629         self.write_hosts(hostname)
630         self.write_resolv(nodns, nameservers)
631
632
633 def get_image_size(ks, default = None):
634     __size = 0
635     for p in ks.handler.partition.partitions:
636         if p.mountpoint == "/" and p.size:
637             __size = p.size
638     if __size > 0:
639         return int(__size) * 1024L * 1024L
640     else:
641         return default
642
643 def get_image_fstype(ks, default = None):
644     for p in ks.handler.partition.partitions:
645         if p.mountpoint == "/" and p.fstype:
646             return p.fstype
647     return default
648
649 def get_image_fsopts(ks, default = None):
650     for p in ks.handler.partition.partitions:
651         if p.mountpoint == "/" and p.fsopts:
652             return p.fstype
653     return default
654
655 def get_modules(ks):
656     devices = []
657     if isinstance(ks.handler.device, kscommands.device.FC3_Device):
658         devices.append(ks.handler.device)
659     else:
660         devices.extend(ks.handler.device.deviceList)
661
662     modules = []
663     for device in devices:
664         if not device.moduleName:
665             continue
666         modules.extend(device.moduleName.split(":"))
667
668     return modules
669
670 def get_timeout(ks, default = None):
671     if not hasattr(ks.handler.bootloader, "timeout"):
672         return default
673     if ks.handler.bootloader.timeout is None:
674         return default
675     return int(ks.handler.bootloader.timeout)
676
677 def get_kernel_args(ks, default = "ro liveimg"):
678     if not hasattr(ks.handler.bootloader, "appendLine"):
679         return default
680     if ks.handler.bootloader.appendLine is None:
681         return default
682     return "%s %s" %(default, ks.handler.bootloader.appendLine)
683
684 def get_menu_args(ks, default = ""):
685     if not hasattr(ks.handler.bootloader, "menus"):
686         return default
687     if ks.handler.bootloader.menus in (None, ""):
688         return default
689     return "%s" % ks.handler.bootloader.menus
690
691 def get_default_kernel(ks, default = None):
692     if not hasattr(ks.handler.bootloader, "default"):
693         return default
694     if not ks.handler.bootloader.default:
695         return default
696     return ks.handler.bootloader.default
697
698 def get_repos(ks, repo_urls = {}):
699     repos = {}
700     for repo in ks.handler.repo.repoList:
701         inc = []
702         if hasattr(repo, "includepkgs"):
703             inc.extend(repo.includepkgs)
704
705         exc = []
706         if hasattr(repo, "excludepkgs"):
707             exc.extend(repo.excludepkgs)
708
709         baseurl = repo.baseurl
710         mirrorlist = repo.mirrorlist
711
712         if repo.name in repo_urls:
713             baseurl = repo_urls[repo.name]
714             mirrorlist = None
715
716         if repos.has_key(repo.name):
717             msger.warning("Overriding already specified repo %s" %(repo.name,))
718
719         proxy = None
720         if hasattr(repo, "proxy"):
721             proxy = repo.proxy
722         proxy_username = None
723         if hasattr(repo, "proxy_username"):
724             proxy_username = repo.proxy_username
725         proxy_password = None
726         if hasattr(repo, "proxy_password"):
727             proxy_password = repo.proxy_password
728         if hasattr(repo, "debuginfo"):
729             debuginfo = repo.debuginfo
730         if hasattr(repo, "source"):
731             source = repo.source
732         if hasattr(repo, "gpgkey"):
733             gpgkey = repo.gpgkey
734         if hasattr(repo, "disable"):
735             disable = repo.disable
736         ssl_verify = True
737         if hasattr(repo, "ssl_verify"):
738             ssl_verify = repo.ssl_verify == "yes"
739         cost = None
740         if hasattr(repo, "cost"):
741             cost = repo.cost
742         priority = None
743         if hasattr(repo, "priority"):
744             priority = repo.priority
745
746         repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc,
747                             proxy, proxy_username, proxy_password, debuginfo,
748                             source, gpgkey, disable, ssl_verify, cost, priority)
749
750     return repos.values()
751
752 def convert_method_to_repo(ks):
753     try:
754         ks.handler.repo.methodToRepo()
755     except (AttributeError, kserrors.KickstartError):
756         pass
757
758 def get_pre_packages(ks, required = []):
759     return ks.handler.prepackages.packageList + required
760
761 def get_packages(ks, required = []):
762     return ks.handler.packages.packageList + required
763
764 def get_groups(ks, required = []):
765     return ks.handler.packages.groupList + required
766
767 def get_excluded(ks, required = []):
768     return ks.handler.packages.excludedList + required
769
770 def get_partitions(ks, required = []):
771     return ks.handler.partition.partitions
772
773 def ignore_missing(ks):
774     return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
775
776 def exclude_docs(ks):
777     return ks.handler.packages.excludeDocs
778
779 def inst_langs(ks):
780     if hasattr(ks.handler.packages, "instLange"):
781         return ks.handler.packages.instLange
782     elif hasattr(ks.handler.packages, "instLangs"):
783         return ks.handler.packages.instLangs
784     return ""
785
786 def get_post_scripts(ks):
787     scripts = []
788     for s in ks.handler.scripts:
789         if s.type != ksparser.KS_SCRIPT_POST:
790             continue
791         scripts.append(s)
792     return scripts
793
794 def add_repo(ks, repostr):
795     args = repostr.split()
796     repoobj = ks.handler.repo.parse(args[1:])
797     if repoobj and repoobj not in ks.handler.repo.repoList:
798         ks.handler.repo.repoList.append(repoobj)
799
800 def remove_all_repos(ks):
801     while len(ks.handler.repo.repoList) != 0:
802         del ks.handler.repo.repoList[0]
803
804 def remove_duplicate_repos(ks):
805     i = 0
806     j = i + 1
807     while True:
808         if len(ks.handler.repo.repoList) < 2:
809             break
810         if i >= len(ks.handler.repo.repoList) - 1:
811             break
812         name = ks.handler.repo.repoList[i].name
813         baseurl = ks.handler.repo.repoList[i].baseurl
814         if j < len(ks.handler.repo.repoList):
815             if (ks.handler.repo.repoList[j].name == name or \
816                 ks.handler.repo.repoList[j].baseurl == baseurl):
817                 del ks.handler.repo.repoList[j]
818             else:
819                 j += 1
820             if j >= len(ks.handler.repo.repoList):
821                 i += 1
822                 j = i + 1
823         else:
824             i += 1
825             j = i + 1
826
827 def resolve_groups(creatoropts, repometadata):
828     iszypp = False
829     if 'zypp' == creatoropts['pkgmgr']:
830         iszypp = True
831     ks = creatoropts['ks']
832
833     for repo in repometadata:
834         """ Mustn't replace group with package list if repo is ready for the
835             corresponding package manager.
836         """
837
838         if iszypp and repo["patterns"]:
839             continue
840         if not iszypp and repo["comps"]:
841             continue
842
843         # But we also must handle such cases, use zypp but repo only has comps,
844         # use yum but repo only has patterns, use zypp but use_comps is true,
845         # use yum but use_comps is false.
846         groupfile = None
847         if iszypp and repo["comps"]:
848             groupfile = repo["comps"]
849             get_pkglist_handler = misc.get_pkglist_in_comps
850         if not iszypp and repo["patterns"]:
851             groupfile = repo["patterns"]
852             get_pkglist_handler = misc.get_pkglist_in_patterns
853
854         if groupfile:
855             i = 0
856             while True:
857                 if i >= len(ks.handler.packages.groupList):
858                     break
859                 pkglist = get_pkglist_handler(
860                                         ks.handler.packages.groupList[i].name,
861                                         groupfile)
862                 if pkglist:
863                     del ks.handler.packages.groupList[i]
864                     for pkg in pkglist:
865                         if pkg not in ks.handler.packages.packageList:
866                             ks.handler.packages.packageList.append(pkg)
867                 else:
868                     i = i + 1