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