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