KickstartConfig: add more directory checking
[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 # Copyright 2009, 2010, 2011  Intel, Inc.
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; version 2 of the License.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU Library General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 import os, sys
21 import shutil
22 import subprocess
23 import time
24 import string
25
26 from mic import msger
27 from mic.utils import errors, misc, runner, fs_related as fs
28
29 import pykickstart.commands as kscommands
30 import pykickstart.constants as ksconstants
31 import pykickstart.errors as kserrors
32 import pykickstart.parser as ksparser
33 import pykickstart.version as ksversion
34 from pykickstart.handlers.control import commandMap
35 from pykickstart.handlers.control import dataMap
36
37 import custom_commands.desktop as desktop
38 import custom_commands.moblinrepo as moblinrepo
39 import custom_commands.micboot as micboot
40
41 def read_kickstart(path):
42     """Parse a kickstart file and return a KickstartParser instance.
43
44     This is a simple utility function which takes a path to a kickstart file,
45     parses it and returns a pykickstart KickstartParser instance which can
46     be then passed to an ImageCreator constructor.
47
48     If an error occurs, a CreatorError exception is thrown.
49
50     """
51     #version = ksversion.makeVersion()
52     #ks = ksparser.KickstartParser(version)
53
54     using_version = ksversion.DEVEL
55     commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
56     commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
57     commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
58     dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
59     superclass = ksversion.returnClassForVersion(version=using_version)
60
61     class KSHandlers(superclass):
62         def __init__(self, mapping={}):
63             superclass.__init__(self, mapping=commandMap[using_version])
64
65     ks = ksparser.KickstartParser(KSHandlers())
66
67     try:
68         ks.readKickstart(path)
69     except kserrors.KickstartError, e:
70         raise errors.KsError("'%s': %s" % (path, str(e)))
71     except kserrors.KickstartParseError, e:
72         raise errors.KsError("'%s': %s" % (path, str(e)))
73     return ks
74
75 def build_name(kscfg, prefix = None, suffix = None, maxlen = None):
76     """Construct and return an image name string.
77
78     This is a utility function to help create sensible name and fslabel
79     strings. The name is constructed using the sans-prefix-and-extension
80     kickstart filename and the supplied prefix and suffix.
81
82     If the name exceeds the maxlen length supplied, the prefix is first dropped
83     and then the kickstart filename portion is reduced until it fits. In other
84     words, the suffix takes precedence over the kickstart portion and the
85     kickstart portion takes precedence over the prefix.
86
87     kscfg -- a path to a kickstart file
88     prefix -- a prefix to prepend to the name; defaults to None, which causes
89               no prefix to be used
90     suffix -- a suffix to append to the name; defaults to None, which causes
91               a YYYYMMDDHHMM suffix to be used
92     maxlen -- the maximum length for the returned string; defaults to None,
93               which means there is no restriction on the name length
94
95     Note, if maxlen is less then the len(suffix), you get to keep both pieces.
96
97     """
98     name = os.path.basename(kscfg)
99     idx = name.rfind('.')
100     if idx >= 0:
101         name = name[:idx]
102
103     if prefix is None:
104         prefix = ""
105     if suffix is None:
106         suffix = time.strftime("%Y%m%d%H%M")
107
108     if name.startswith(prefix):
109         name = name[len(prefix):]
110
111     ret = prefix + name + "-" + suffix
112     if not maxlen is None and len(ret) > maxlen:
113         ret = name[:maxlen - len(suffix) - 1] + "-" + suffix
114
115     return ret
116
117 class KickstartConfig(object):
118     """A base class for applying kickstart configurations to a system."""
119     def __init__(self, instroot):
120         self.instroot = instroot
121
122     def path(self, subpath):
123         return self.instroot + subpath
124
125     def chroot(self):
126         os.chroot(self.instroot)
127         os.chdir("/")
128
129     def call(self, args):
130         if not os.path.exists("%s/%s" %(self.instroot, args[0])):
131             msger.warning("%s/%s" %(self.instroot, args[0]))
132             raise errors.KsError("Unable to run %s!" %(args))
133         subprocess.call(args, preexec_fn = self.chroot)
134
135     def apply(self):
136         pass
137
138 class LanguageConfig(KickstartConfig):
139     """A class to apply a kickstart language configuration to a system."""
140     def apply(self, kslang):
141         if not os.path.exists(self.path("/etc/sysconfig")):
142             os.mkdir(self.path("/etc/sysconfig"))
143         if kslang.lang:
144             f = open(self.path("/etc/sysconfig/i18n"), "w+")
145             f.write("LANG=\"" + kslang.lang + "\"\n")
146             f.close()
147
148 class KeyboardConfig(KickstartConfig):
149     """A class to apply a kickstart keyboard configuration to a system."""
150     def apply(self, kskeyboard):
151         #
152         # FIXME:
153         #   should this impact the X keyboard config too?
154         #   or do we want to make X be able to do this mapping?
155         #
156         #k = rhpl.keyboard.Keyboard()
157         #if kskeyboard.keyboard:
158         #   k.set(kskeyboard.keyboard)
159         #k.write(self.instroot)
160         pass
161
162 class TimezoneConfig(KickstartConfig):
163     """A class to apply a kickstart timezone configuration to a system."""
164     def apply(self, kstimezone):
165         if not os.path.exists(self.path("/etc/sysconfig")):
166             os.mkdir(self.path("/etc/sysconfig"))
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         fs.makedirs(self.path(repodir))
404         f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
405         self.__create_repo_section(repo, "base", f)
406         if repo.debuginfo:
407             self.__create_repo_section(repo, "debuginfo", f)
408         if repo.source:
409             self.__create_repo_section(repo, "source", f)
410         f.close()
411
412     def apply(self, ksrepo, repodata):
413         for repo in ksrepo.repoList:
414             if repo.save:
415                 #self.__create_repo_file(repo, "/etc/yum.repos.d")
416                 self.__create_repo_file(repo, "/etc/zypp/repos.d")
417         """ Import repo gpg keys """
418         if repodata:
419             for repo in repodata:
420                 if repo['repokey']:
421                     runner.quiet(['rpm', "--root=%s" % self.instroot, "--import", repo['repokey']])
422
423 class RPMMacroConfig(KickstartConfig):
424     """A class to apply the specified rpm macros to the filesystem"""
425     def apply(self, ks):
426         if not ks:
427             return
428         if not os.path.exists(self.path("/etc/rpm")):
429             os.mkdir(self.path("/etc/rpm"))
430         f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
431         if exclude_docs(ks):
432             f.write("%_excludedocs 1\n")
433         f.write("%__file_context_path %{nil}\n")
434         if inst_langs(ks) != None:
435             f.write("%_install_langs ")
436             f.write(inst_langs(ks))
437             f.write("\n")
438         f.close()
439
440 class NetworkConfig(KickstartConfig):
441     """A class to apply a kickstart network configuration to a system."""
442     def write_ifcfg(self, network):
443         p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
444
445         f = file(p, "w+")
446         os.chmod(p, 0644)
447
448         f.write("DEVICE=%s\n" % network.device)
449         f.write("BOOTPROTO=%s\n" % network.bootProto)
450
451         if network.bootProto.lower() == "static":
452             if network.ip:
453                 f.write("IPADDR=%s\n" % network.ip)
454             if network.netmask:
455                 f.write("NETMASK=%s\n" % network.netmask)
456
457         if network.onboot:
458             f.write("ONBOOT=on\n")
459         else:
460             f.write("ONBOOT=off\n")
461
462         if network.essid:
463             f.write("ESSID=%s\n" % network.essid)
464
465         if network.ethtool:
466             if network.ethtool.find("autoneg") == -1:
467                 network.ethtool = "autoneg off " + network.ethtool
468             f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
469
470         if network.bootProto.lower() == "dhcp":
471             if network.hostname:
472                 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
473             if network.dhcpclass:
474                 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
475
476         if network.mtu:
477             f.write("MTU=%s\n" % network.mtu)
478
479         f.close()
480
481     def write_wepkey(self, network):
482         if not network.wepkey:
483             return
484
485         p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
486         f = file(p, "w+")
487         os.chmod(p, 0600)
488         f.write("KEY=%s\n" % network.wepkey)
489         f.close()
490
491     def write_sysconfig(self, useipv6, hostname, gateway):
492         path = self.path("/etc/sysconfig/network")
493         f = file(path, "w+")
494         os.chmod(path, 0644)
495
496         f.write("NETWORKING=yes\n")
497
498         if useipv6:
499             f.write("NETWORKING_IPV6=yes\n")
500         else:
501             f.write("NETWORKING_IPV6=no\n")
502
503         if hostname:
504             f.write("HOSTNAME=%s\n" % hostname)
505         else:
506             f.write("HOSTNAME=localhost.localdomain\n")
507
508         if gateway:
509             f.write("GATEWAY=%s\n" % gateway)
510
511         f.close()
512
513     def write_hosts(self, hostname):
514         localline = ""
515         if hostname and hostname != "localhost.localdomain":
516             localline += hostname + " "
517             l = hostname.split(".")
518             if len(l) > 1:
519                 localline += l[0] + " "
520         localline += "localhost.localdomain localhost"
521
522         path = self.path("/etc/hosts")
523         f = file(path, "w+")
524         os.chmod(path, 0644)
525         f.write("127.0.0.1\t\t%s\n" % localline)
526         f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
527         f.close()
528
529     def write_resolv(self, nodns, nameservers):
530         if nodns or not nameservers:
531             return
532
533         path = self.path("/etc/resolv.conf")
534         f = file(path, "w+")
535         os.chmod(path, 0644)
536
537         for ns in (nameservers):
538             if ns:
539                 f.write("nameserver %s\n" % ns)
540
541         f.close()
542
543     def apply(self, ksnet):
544         fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
545
546         useipv6 = False
547         nodns = False
548         hostname = None
549         gateway = None
550         nameservers = None
551
552         for network in ksnet.network:
553             if not network.device:
554                 raise errors.KsError("No --device specified with "
555                                             "network kickstart command")
556
557             if (network.onboot and network.bootProto.lower() != "dhcp" and
558                 not (network.ip and network.netmask)):
559                 raise errors.KsError("No IP address and/or netmask "
560                                             "specified with static "
561                                             "configuration for '%s'" %
562                                             network.device)
563
564             self.write_ifcfg(network)
565             self.write_wepkey(network)
566
567             if network.ipv6:
568                 useipv6 = True
569             if network.nodns:
570                 nodns = True
571
572             if network.hostname:
573                 hostname = network.hostname
574             if network.gateway:
575                 gateway = network.gateway
576
577             if network.nameserver:
578                 nameservers = network.nameserver.split(",")
579
580         self.write_sysconfig(useipv6, hostname, gateway)
581         self.write_hosts(hostname)
582         self.write_resolv(nodns, nameservers)
583
584
585 def get_image_size(ks, default = None):
586     __size = 0
587     for p in ks.handler.partition.partitions:
588         if p.mountpoint == "/" and p.size:
589             __size = p.size
590     if __size > 0:
591         return int(__size) * 1024L * 1024L
592     else:
593         return default
594
595 def get_image_fstype(ks, default = None):
596     for p in ks.handler.partition.partitions:
597         if p.mountpoint == "/" and p.fstype:
598             return p.fstype
599     return default
600
601 def get_image_fsopts(ks, default = None):
602     for p in ks.handler.partition.partitions:
603         if p.mountpoint == "/" and p.fsopts:
604             return p.fstype
605     return default
606
607 def get_modules(ks):
608     devices = []
609     if isinstance(ks.handler.device, kscommands.device.FC3_Device):
610         devices.append(ks.handler.device)
611     else:
612         devices.extend(ks.handler.device.deviceList)
613
614     modules = []
615     for device in devices:
616         if not device.moduleName:
617             continue
618         modules.extend(device.moduleName.split(":"))
619
620     return modules
621
622 def get_timeout(ks, default = None):
623     if not hasattr(ks.handler.bootloader, "timeout"):
624         return default
625     if ks.handler.bootloader.timeout is None:
626         return default
627     return int(ks.handler.bootloader.timeout)
628
629 def get_kernel_args(ks, default = "ro liveimg"):
630     if not hasattr(ks.handler.bootloader, "appendLine"):
631         return default
632     if ks.handler.bootloader.appendLine is None:
633         return default
634     return "%s %s" %(default, ks.handler.bootloader.appendLine)
635
636 def get_menu_args(ks, default = "liveinst"):
637     if not hasattr(ks.handler.bootloader, "menus"):
638         return default
639     if ks.handler.bootloader.menus in (None, ""):
640         return default
641     return "%s" % ks.handler.bootloader.menus
642
643 def get_default_kernel(ks, default = None):
644     if not hasattr(ks.handler.bootloader, "default"):
645         return default
646     if not ks.handler.bootloader.default:
647         return default
648     return ks.handler.bootloader.default
649
650 def get_repos(ks, repo_urls = {}):
651     repos = {}
652     for repo in ks.handler.repo.repoList:
653         inc = []
654         if hasattr(repo, "includepkgs"):
655             inc.extend(repo.includepkgs)
656
657         exc = []
658         if hasattr(repo, "excludepkgs"):
659             exc.extend(repo.excludepkgs)
660
661         baseurl = repo.baseurl
662         mirrorlist = repo.mirrorlist
663
664         if repo.name in repo_urls:
665             baseurl = repo_urls[repo.name]
666             mirrorlist = None
667
668         if repos.has_key(repo.name):
669             msger.warning("Overriding already specified repo %s" %(repo.name,))
670
671         proxy = None
672         if hasattr(repo, "proxy"):
673             proxy = repo.proxy
674         proxy_username = None
675         if hasattr(repo, "proxy_username"):
676             proxy_username = repo.proxy_username
677         proxy_password = None
678         if hasattr(repo, "proxy_password"):
679             proxy_password = repo.proxy_password
680         if hasattr(repo, "debuginfo"):
681             debuginfo = repo.debuginfo
682         if hasattr(repo, "source"):
683             source = repo.source
684         if hasattr(repo, "gpgkey"):
685             gpgkey = repo.gpgkey
686         if hasattr(repo, "disable"):
687             disable = repo.disable
688
689         repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc, proxy, proxy_username, proxy_password, debuginfo, source, gpgkey, disable)
690
691     return repos.values()
692
693 def convert_method_to_repo(ks):
694     try:
695         ks.handler.repo.methodToRepo()
696     except (AttributeError, kserrors.KickstartError):
697         pass
698
699 def get_packages(ks, required = []):
700     return ks.handler.packages.packageList + required
701
702 def get_groups(ks, required = []):
703     return ks.handler.packages.groupList + required
704
705 def get_excluded(ks, required = []):
706     return ks.handler.packages.excludedList + required
707
708 def get_partitions(ks, required = []):
709     return ks.handler.partition.partitions
710
711 def ignore_missing(ks):
712     return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
713
714 def exclude_docs(ks):
715     return ks.handler.packages.excludeDocs
716
717 def inst_langs(ks):
718     if hasattr(ks.handler.packages, "instLange"):
719         return ks.handler.packages.instLange
720     elif hasattr(ks.handler.packages, "instLangs"):
721         return ks.handler.packages.instLangs
722     return ""
723
724 def get_post_scripts(ks):
725     scripts = []
726     for s in ks.handler.scripts:
727         if s.type != ksparser.KS_SCRIPT_POST:
728             continue
729         scripts.append(s)
730     return scripts
731
732 def add_repo(ks, repostr):
733     args = repostr.split()
734     repoobj = ks.handler.repo.parse(args[1:])
735     if repoobj and repoobj not in ks.handler.repo.repoList:
736         ks.handler.repo.repoList.append(repoobj)
737
738 def remove_all_repos(ks):
739     while len(ks.handler.repo.repoList) != 0:
740         del ks.handler.repo.repoList[0]
741
742 def remove_duplicate_repos(ks):
743     i = 0
744     j = i + 1
745     while True:
746         if len(ks.handler.repo.repoList) < 2:
747             break
748         if i >= len(ks.handler.repo.repoList) - 1:
749             break
750         name = ks.handler.repo.repoList[i].name
751         baseurl = ks.handler.repo.repoList[i].baseurl
752         if j < len(ks.handler.repo.repoList):
753             if (ks.handler.repo.repoList[j].name == name or \
754                 ks.handler.repo.repoList[j].baseurl == baseurl):
755                 del ks.handler.repo.repoList[j]
756             else:
757                 j += 1
758             if j >= len(ks.handler.repo.repoList):
759                 i += 1
760                 j = i + 1
761         else:
762             i += 1
763             j = i + 1
764
765 def resolve_groups(creatoropts, repometadata):
766     iszypp = False
767     if 'zypp' == creatoropts['pkgmgr']:
768         iszypp = True
769     ks = creatoropts['ks']
770
771     for repo in repometadata:
772         """ Mustn't replace group with package list if repo is ready for the corresponding package manager """
773         if iszypp and repo["patterns"]:
774             continue
775         if not iszypp and repo["comps"]:
776             continue
777
778         """
779             But we also must handle such cases, use zypp but repo only has comps,
780             use yum but repo only has patterns, use zypp but use_comps is true,
781             use yum but use_comps is false.
782         """
783         groupfile = None
784         if iszypp and repo["comps"]:
785             groupfile = repo["comps"]
786             get_pkglist_handler = misc.get_pkglist_in_comps
787         if not iszypp and repo["patterns"]:
788             groupfile = repo["patterns"]
789             get_pkglist_handler = misc.get_pkglist_in_patterns
790
791         if groupfile:
792             i = 0
793             while True:
794                 if i >= len(ks.handler.packages.groupList):
795                     break
796                 pkglist = get_pkglist_handler(ks.handler.packages.groupList[i].name, groupfile)
797                 if pkglist:
798                     del ks.handler.packages.groupList[i]
799                     for pkg in pkglist:
800                         if pkg not in ks.handler.packages.packageList:
801                             ks.handler.packages.packageList.append(pkg)
802                 else:
803                     i = i + 1