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