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