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