do not use default value for "lang" in kickstart
[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.utils import errors
27 from mic.utils import misc
28 from mic.utils import fs_related as fs
29 from mic import msger
30
31 import pykickstart.commands as kscommands
32 import pykickstart.constants as ksconstants
33 import pykickstart.errors as kserrors
34 import pykickstart.parser as ksparser
35 import pykickstart.version as ksversion
36 from pykickstart.handlers.control import commandMap
37 from pykickstart.handlers.control import dataMap
38
39 import custom_commands.desktop as desktop
40 import custom_commands.moblinrepo as moblinrepo
41 import custom_commands.micboot as micboot
42
43 def read_kickstart(path):
44     """Parse a kickstart file and return a KickstartParser instance.
45
46     This is a simple utility function which takes a path to a kickstart file,
47     parses it and returns a pykickstart KickstartParser instance which can
48     be then passed to an ImageCreator constructor.
49
50     If an error occurs, a CreatorError exception is thrown.
51
52     """
53     #version = ksversion.makeVersion()
54     #ks = ksparser.KickstartParser(version)
55
56     using_version = ksversion.DEVEL
57     commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
58     commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
59     commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
60     dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
61     superclass = ksversion.returnClassForVersion(version=using_version)
62
63     class KSHandlers(superclass):
64         def __init__(self, mapping={}):
65             superclass.__init__(self, mapping=commandMap[using_version])
66
67     ks = ksparser.KickstartParser(KSHandlers())
68
69     try:
70         ks.readKickstart(path)
71     except IOError, (err, msg):
72         raise errors.KsError("Failed to read kickstart file "
73                              "'%s' : %s" % (path, msg))
74     except kserrors.KickstartParseError, e:
75         raise errors.KsError("Failed to parse kickstart file "
76                              "'%s' : %s" % (path, e))
77     return ks
78
79 def build_name(kscfg, prefix = None, suffix = None, maxlen = None):
80     """Construct and return an image name string.
81
82     This is a utility function to help create sensible name and fslabel
83     strings. The name is constructed using the sans-prefix-and-extension
84     kickstart filename and the supplied prefix and suffix.
85
86     If the name exceeds the maxlen length supplied, the prefix is first dropped
87     and then the kickstart filename portion is reduced until it fits. In other
88     words, the suffix takes precedence over the kickstart portion and the
89     kickstart portion takes precedence over the prefix.
90
91     kscfg -- a path to a kickstart file
92     prefix -- a prefix to prepend to the name; defaults to None, which causes
93               no prefix to be used
94     suffix -- a suffix to append to the name; defaults to None, which causes
95               a YYYYMMDDHHMM suffix to be used
96     maxlen -- the maximum length for the returned string; defaults to None,
97               which means there is no restriction on the name length
98
99     Note, if maxlen is less then the len(suffix), you get to keep both pieces.
100
101     """
102     name = os.path.basename(kscfg)
103     idx = name.rfind('.')
104     if idx >= 0:
105         name = name[:idx]
106
107     if prefix is None:
108         prefix = ""
109     if suffix is None:
110         suffix = time.strftime("%Y%m%d%H%M")
111
112     if name.startswith(prefix):
113         name = name[len(prefix):]
114
115     ret = prefix + name + "-" + suffix
116     if not maxlen is None and len(ret) > maxlen:
117         ret = name[:maxlen - len(suffix) - 1] + "-" + suffix
118
119     return ret
120
121 class KickstartConfig(object):
122     """A base class for applying kickstart configurations to a system."""
123     def __init__(self, instroot):
124         self.instroot = instroot
125
126     def path(self, subpath):
127         return self.instroot + subpath
128
129     def chroot(self):
130         os.chroot(self.instroot)
131         os.chdir("/")
132
133     def call(self, args):
134         if not os.path.exists("%s/%s" %(self.instroot, args[0])):
135             msger.warning("%s/%s" %(self.instroot, args[0]))
136             raise errors.KsError("Unable to run %s!" %(args))
137         subprocess.call(args, preexec_fn = self.chroot)
138
139     def apply(self):
140         pass
141
142 class LanguageConfig(KickstartConfig):
143     """A class to apply a kickstart language configuration to a system."""
144     def apply(self, kslang):
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         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                     msger.run(['rpm', "--root=%s" % self.instroot, "--import", repo['repokey']], True)
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(creator, repometadata, use_comps = False):
766     pkgmgr = creator.pkgmgr.get_default_pkg_manager
767     iszypp = False
768     if creator.pkgmgr.managers.has_key("zypp") and creator.pkgmgr.managers['zypp'] == pkgmgr:
769         iszypp = True
770     ks = creator.ks
771
772     for repo in repometadata:
773         """ Mustn't replace group with package list if repo is ready for the corresponding package manager """
774         if iszypp and repo["patterns"] and not use_comps:
775             continue
776         if not iszypp and repo["comps"] and use_comps:
777             continue
778
779         """
780             But we also must handle such cases, use zypp but repo only has comps,
781             use yum but repo only has patterns, use zypp but use_comps is true,
782             use yum but use_comps is false.
783         """
784         groupfile = None
785         if iszypp:
786             if (use_comps and repo["comps"]) or (not repo["patterns"] and repo["comps"]):
787                 groupfile = repo["comps"]
788                 get_pkglist_handler = misc.get_pkglist_in_comps
789         if not iszypp:
790             if (not use_comps and repo["patterns"]) or (not repo["comps"] and repo["patterns"]):
791                 groupfile = repo["patterns"]
792                 get_pkglist_handler = misc.get_pkglist_in_patterns
793
794         if groupfile:
795             i = 0
796             while True:
797                 if i >= len(ks.handler.packages.groupList):
798                     break
799                 pkglist = get_pkglist_handler(ks.handler.packages.groupList[i].name, groupfile)
800                 if pkglist:
801                     del ks.handler.packages.groupList[i]
802                     for pkg in pkglist:
803                         if pkg not in ks.handler.packages.packageList:
804                             ks.handler.packages.packageList.append(pkg)
805                 else:
806                     i = i + 1