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