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