wrapper all subprocess call using msger.run
[tools/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         lang = kslang.lang or "en_US.UTF-8"
146
147         f = open(self.path("/etc/sysconfig/i18n"), "w+")
148         f.write("LANG=\"" + lang + "\"\n")
149         f.close()
150
151 class KeyboardConfig(KickstartConfig):
152     """A class to apply a kickstart keyboard configuration to a system."""
153     def apply(self, kskeyboard):
154         #
155         # FIXME:
156         #   should this impact the X keyboard config too?
157         #   or do we want to make X be able to do this mapping?
158         #
159         #k = rhpl.keyboard.Keyboard()
160         #if kskeyboard.keyboard:
161         #   k.set(kskeyboard.keyboard)
162         #k.write(self.instroot)
163         pass
164
165 class TimezoneConfig(KickstartConfig):
166     """A class to apply a kickstart timezone configuration to a system."""
167     def apply(self, kstimezone):
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:
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             f = open(self.path("/etc/sysconfig/desktop"), "w")
307             f.write("DESKTOP="+ksxconfig.defaultdesktop+"\n")
308             f.close()
309
310 class DesktopConfig(KickstartConfig):
311     """A class to apply a kickstart desktop configuration to a system."""
312     def apply(self, ksdesktop):
313         if ksdesktop.defaultdesktop:
314             f = open(self.path("/etc/sysconfig/desktop"), "w")
315             f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n")
316             f.close()
317             if os.path.exists(self.path("/etc/gdm/custom.conf")):
318                 f = open(self.path("/etc/skel/.dmrc"), "w")
319                 f.write("[Desktop]\n")
320                 f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n")
321                 f.close()
322         if ksdesktop.session:
323             if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
324                 f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
325                 f.write("session="+ksdesktop.session.lower()+"\n")
326                 f.close()
327         if ksdesktop.autologinuser:
328             f = open(self.path("/etc/sysconfig/desktop"), "a+")
329             f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n")
330             f.close()
331             if ksdesktop.session:
332                 if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
333                     f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
334                     f.write("user="+ksdesktop.autologinuser+"\n")
335                     f.close()
336             if os.path.exists(self.path("/etc/gdm/custom.conf")):
337                 f = open(self.path("/etc/gdm/custom.conf"), "w")
338                 f.write("[daemon]\n")
339                 f.write("AutomaticLoginEnable=true\n")
340                 f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n")
341                 f.close()
342
343 class MoblinRepoConfig(KickstartConfig):
344     """A class to apply a kickstart desktop configuration to a system."""
345     def __create_repo_section(self, repo, type, fd):
346         baseurl = None
347         mirrorlist = None
348         reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"}
349         reponame = repo.name + reposuffix[type]
350         if type == "base":
351             if repo.baseurl:
352                 baseurl = repo.baseurl
353             if repo.mirrorlist:
354                 mirrorlist = repo.mirrorlist
355         elif type == "debuginfo":
356             if repo.baseurl:
357                 if repo.baseurl.endswith("/"):
358                     baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
359                 else:
360                     baseurl = os.path.dirname(repo.baseurl)
361                 baseurl += "/debug"
362             if repo.mirrorlist:
363                 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
364                 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
365                 mirrorlist += "debug" + "-" + variant
366         elif type == "source":
367             if repo.baseurl:
368                 if repo.baseurl.endswith("/"):
369                     baseurl = os.path.dirname(os.path.dirname(os.path.dirname(repo.baseurl)))
370                 else:
371                     baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
372                 baseurl += "/source"
373             if repo.mirrorlist:
374                 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
375                 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
376                 mirrorlist += "source" + "-" + variant
377
378         fd.write("[" + reponame + "]\n")
379         fd.write("name=" + reponame + "\n")
380         fd.write("failovermethod=priority\n")
381         if baseurl:
382             fd.write("baseurl=" + baseurl + "\n")
383         if mirrorlist:
384             fd.write("mirrorlist=" + mirrorlist + "\n")
385         """ Skip saving proxy settings """
386         #if repo.proxy:
387         #    fd.write("proxy=" + repo.proxy + "\n")
388         #if repo.proxy_username:
389         #    fd.write("proxy_username=" + repo.proxy_username + "\n")
390         #if repo.proxy_password:
391         #    fd.write("proxy_password=" + repo.proxy_password + "\n")
392         if repo.gpgkey:
393             fd.write("gpgkey=" + repo.gpgkey + "\n")
394             fd.write("gpgcheck=1\n")
395         else:
396             fd.write("gpgcheck=0\n")
397         if type == "source" or type == "debuginfo" or repo.disable:
398             fd.write("enabled=0\n")
399         else:
400             fd.write("enabled=1\n")
401         fd.write("\n")
402
403     def __create_repo_file(self, repo, repodir):
404         fs.makedirs(self.path(repodir))
405         f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
406         self.__create_repo_section(repo, "base", f)
407         if repo.debuginfo:
408             self.__create_repo_section(repo, "debuginfo", f)
409         if repo.source:
410             self.__create_repo_section(repo, "source", f)
411         f.close()
412
413     def apply(self, ksrepo, repodata):
414         for repo in ksrepo.repoList:
415             if repo.save:
416                 #self.__create_repo_file(repo, "/etc/yum.repos.d")
417                 self.__create_repo_file(repo, "/etc/zypp/repos.d")
418         """ Import repo gpg keys """
419         if repodata:
420             for repo in repodata:
421                 if repo['repokey']:
422                     msger.run(['rpm', "--root=%s" % self.instroot, "--import", repo['repokey']], True)
423
424 class RPMMacroConfig(KickstartConfig):
425     """A class to apply the specified rpm macros to the filesystem"""
426     def apply(self, ks):
427         if not ks:
428             return
429         if not os.path.exists(self.path("/etc/rpm")):
430             os.mkdir(self.path("/etc/rpm"))
431         f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
432         if exclude_docs(ks):
433             f.write("%_excludedocs 1\n")
434         f.write("%__file_context_path %{nil}\n")
435         if inst_langs(ks) != None:
436             f.write("%_install_langs ")
437             f.write(inst_langs(ks))
438             f.write("\n")
439         f.close()
440
441 class NetworkConfig(KickstartConfig):
442     """A class to apply a kickstart network configuration to a system."""
443     def write_ifcfg(self, network):
444         p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
445
446         f = file(p, "w+")
447         os.chmod(p, 0644)
448
449         f.write("DEVICE=%s\n" % network.device)
450         f.write("BOOTPROTO=%s\n" % network.bootProto)
451
452         if network.bootProto.lower() == "static":
453             if network.ip:
454                 f.write("IPADDR=%s\n" % network.ip)
455             if network.netmask:
456                 f.write("NETMASK=%s\n" % network.netmask)
457
458         if network.onboot:
459             f.write("ONBOOT=on\n")
460         else:
461             f.write("ONBOOT=off\n")
462
463         if network.essid:
464             f.write("ESSID=%s\n" % network.essid)
465
466         if network.ethtool:
467             if network.ethtool.find("autoneg") == -1:
468                 network.ethtool = "autoneg off " + network.ethtool
469             f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
470
471         if network.bootProto.lower() == "dhcp":
472             if network.hostname:
473                 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
474             if network.dhcpclass:
475                 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
476
477         if network.mtu:
478             f.write("MTU=%s\n" % network.mtu)
479
480         f.close()
481
482     def write_wepkey(self, network):
483         if not network.wepkey:
484             return
485
486         p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
487         f = file(p, "w+")
488         os.chmod(p, 0600)
489         f.write("KEY=%s\n" % network.wepkey)
490         f.close()
491
492     def write_sysconfig(self, useipv6, hostname, gateway):
493         path = self.path("/etc/sysconfig/network")
494         f = file(path, "w+")
495         os.chmod(path, 0644)
496
497         f.write("NETWORKING=yes\n")
498
499         if useipv6:
500             f.write("NETWORKING_IPV6=yes\n")
501         else:
502             f.write("NETWORKING_IPV6=no\n")
503
504         if hostname:
505             f.write("HOSTNAME=%s\n" % hostname)
506         else:
507             f.write("HOSTNAME=localhost.localdomain\n")
508
509         if gateway:
510             f.write("GATEWAY=%s\n" % gateway)
511
512         f.close()
513
514     def write_hosts(self, hostname):
515         localline = ""
516         if hostname and hostname != "localhost.localdomain":
517             localline += hostname + " "
518             l = hostname.split(".")
519             if len(l) > 1:
520                 localline += l[0] + " "
521         localline += "localhost.localdomain localhost"
522
523         path = self.path("/etc/hosts")
524         f = file(path, "w+")
525         os.chmod(path, 0644)
526         f.write("127.0.0.1\t\t%s\n" % localline)
527         f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
528         f.close()
529
530     def write_resolv(self, nodns, nameservers):
531         if nodns or not nameservers:
532             return
533
534         path = self.path("/etc/resolv.conf")
535         f = file(path, "w+")
536         os.chmod(path, 0644)
537
538         for ns in (nameservers):
539             if ns:
540                 f.write("nameserver %s\n" % ns)
541
542         f.close()
543
544     def apply(self, ksnet):
545         fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
546
547         useipv6 = False
548         nodns = False
549         hostname = None
550         gateway = None
551         nameservers = None
552
553         for network in ksnet.network:
554             if not network.device:
555                 raise errors.KsError("No --device specified with "
556                                             "network kickstart command")
557
558             if (network.onboot and network.bootProto.lower() != "dhcp" and
559                 not (network.ip and network.netmask)):
560                 raise errors.KsError("No IP address and/or netmask "
561                                             "specified with static "
562                                             "configuration for '%s'" %
563                                             network.device)
564
565             self.write_ifcfg(network)
566             self.write_wepkey(network)
567
568             if network.ipv6:
569                 useipv6 = True
570             if network.nodns:
571                 nodns = True
572
573             if network.hostname:
574                 hostname = network.hostname
575             if network.gateway:
576                 gateway = network.gateway
577
578             if network.nameserver:
579                 nameservers = network.nameserver.split(",")
580
581         self.write_sysconfig(useipv6, hostname, gateway)
582         self.write_hosts(hostname)
583         self.write_resolv(nodns, nameservers)
584
585
586 def get_image_size(ks, default = None):
587     __size = 0
588     for p in ks.handler.partition.partitions:
589         if p.mountpoint == "/" and p.size:
590             __size = p.size
591     if __size > 0:
592         return int(__size) * 1024L * 1024L
593     else:
594         return default
595
596 def get_image_fstype(ks, default = None):
597     for p in ks.handler.partition.partitions:
598         if p.mountpoint == "/" and p.fstype:
599             return p.fstype
600     return default
601
602 def get_image_fsopts(ks, default = None):
603     for p in ks.handler.partition.partitions:
604         if p.mountpoint == "/" and p.fsopts:
605             return p.fstype
606     return default
607
608 def get_modules(ks):
609     devices = []
610     if isinstance(ks.handler.device, kscommands.device.FC3_Device):
611         devices.append(ks.handler.device)
612     else:
613         devices.extend(ks.handler.device.deviceList)
614
615     modules = []
616     for device in devices:
617         if not device.moduleName:
618             continue
619         modules.extend(device.moduleName.split(":"))
620
621     return modules
622
623 def get_timeout(ks, default = None):
624     if not hasattr(ks.handler.bootloader, "timeout"):
625         return default
626     if ks.handler.bootloader.timeout is None:
627         return default
628     return int(ks.handler.bootloader.timeout)
629
630 def get_kernel_args(ks, default = "ro liveimg"):
631     if not hasattr(ks.handler.bootloader, "appendLine"):
632         return default
633     if ks.handler.bootloader.appendLine is None:
634         return default
635     return "%s %s" %(default, ks.handler.bootloader.appendLine)
636
637 def get_menu_args(ks, default = "liveinst"):
638     if not hasattr(ks.handler.bootloader, "menus"):
639         return default
640     if ks.handler.bootloader.menus in (None, ""):
641         return default
642     return "%s" % ks.handler.bootloader.menus
643
644 def get_default_kernel(ks, default = None):
645     if not hasattr(ks.handler.bootloader, "default"):
646         return default
647     if not ks.handler.bootloader.default:
648         return default
649     return ks.handler.bootloader.default
650
651 def get_repos(ks, repo_urls = {}):
652     repos = {}
653     for repo in ks.handler.repo.repoList:
654         inc = []
655         if hasattr(repo, "includepkgs"):
656             inc.extend(repo.includepkgs)
657
658         exc = []
659         if hasattr(repo, "excludepkgs"):
660             exc.extend(repo.excludepkgs)
661
662         baseurl = repo.baseurl
663         mirrorlist = repo.mirrorlist
664
665         if repo.name in repo_urls:
666             baseurl = repo_urls[repo.name]
667             mirrorlist = None
668
669         if repos.has_key(repo.name):
670             msger.warning("Overriding already specified repo %s" %(repo.name,))
671
672         proxy = None
673         if hasattr(repo, "proxy"):
674             proxy = repo.proxy
675         proxy_username = None
676         if hasattr(repo, "proxy_username"):
677             proxy_username = repo.proxy_username
678         proxy_password = None
679         if hasattr(repo, "proxy_password"):
680             proxy_password = repo.proxy_password
681         if hasattr(repo, "debuginfo"):
682             debuginfo = repo.debuginfo
683         if hasattr(repo, "source"):
684             source = repo.source
685         if hasattr(repo, "gpgkey"):
686             gpgkey = repo.gpgkey
687         if hasattr(repo, "disable"):
688             disable = repo.disable
689
690         repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc, proxy, proxy_username, proxy_password, debuginfo, source, gpgkey, disable)
691
692     return repos.values()
693
694 def convert_method_to_repo(ks):
695     try:
696         ks.handler.repo.methodToRepo()
697     except (AttributeError, kserrors.KickstartError):
698         pass
699
700 def get_packages(ks, required = []):
701     return ks.handler.packages.packageList + required
702
703 def get_groups(ks, required = []):
704     return ks.handler.packages.groupList + required
705
706 def get_excluded(ks, required = []):
707     return ks.handler.packages.excludedList + required
708
709 def get_partitions(ks, required = []):
710     return ks.handler.partition.partitions
711
712 def ignore_missing(ks):
713     return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
714
715 def exclude_docs(ks):
716     return ks.handler.packages.excludeDocs
717
718 def inst_langs(ks):
719     if hasattr(ks.handler.packages, "instLange"):
720         return ks.handler.packages.instLange
721     elif hasattr(ks.handler.packages, "instLangs"):
722         return ks.handler.packages.instLangs
723     return ""
724
725 def get_post_scripts(ks):
726     scripts = []
727     for s in ks.handler.scripts:
728         if s.type != ksparser.KS_SCRIPT_POST:
729             continue
730         scripts.append(s)
731     return scripts
732
733 def add_repo(ks, repostr):
734     args = repostr.split()
735     repoobj = ks.handler.repo.parse(args[1:])
736     if repoobj and repoobj not in ks.handler.repo.repoList:
737         ks.handler.repo.repoList.append(repoobj)
738
739 def remove_all_repos(ks):
740     while len(ks.handler.repo.repoList) != 0:
741         del ks.handler.repo.repoList[0]
742
743 def remove_duplicate_repos(ks):
744     i = 0
745     j = i + 1
746     while True:
747         if len(ks.handler.repo.repoList) < 2:
748             break
749         if i >= len(ks.handler.repo.repoList) - 1:
750             break
751         name = ks.handler.repo.repoList[i].name
752         baseurl = ks.handler.repo.repoList[i].baseurl
753         if j < len(ks.handler.repo.repoList):
754             if (ks.handler.repo.repoList[j].name == name or \
755                 ks.handler.repo.repoList[j].baseurl == baseurl):
756                 del ks.handler.repo.repoList[j]
757             else:
758                 j += 1
759             if j >= len(ks.handler.repo.repoList):
760                 i += 1
761                 j = i + 1
762         else:
763             i += 1
764             j = i + 1
765
766 def resolve_groups(creator, repometadata, use_comps = False):
767     pkgmgr = creator.pkgmgr.get_default_pkg_manager
768     iszypp = False
769     if creator.pkgmgr.managers.has_key("zypp") and creator.pkgmgr.managers['zypp'] == pkgmgr:
770         iszypp = True
771     ks = creator.ks
772
773     for repo in repometadata:
774         """ Mustn't replace group with package list if repo is ready for the corresponding package manager """
775         if iszypp and repo["patterns"] and not use_comps:
776             continue
777         if not iszypp and repo["comps"] and use_comps:
778             continue
779
780         """
781             But we also must handle such cases, use zypp but repo only has comps,
782             use yum but repo only has patterns, use zypp but use_comps is true,
783             use yum but use_comps is false.
784         """
785         groupfile = None
786         if iszypp:
787             if (use_comps and repo["comps"]) or (not repo["patterns"] and repo["comps"]):
788                 groupfile = repo["comps"]
789                 get_pkglist_handler = misc.get_pkglist_in_comps
790         if not iszypp:
791             if (not use_comps and repo["patterns"]) or (not repo["comps"] and repo["patterns"]):
792                 groupfile = repo["patterns"]
793                 get_pkglist_handler = misc.get_pkglist_in_patterns
794
795         if groupfile:
796             i = 0
797             while True:
798                 if i >= len(ks.handler.packages.groupList):
799                     break
800                 pkglist = get_pkglist_handler(ks.handler.packages.groupList[i].name, groupfile)
801                 if pkglist:
802                     del ks.handler.packages.groupList[i]
803                     for pkg in pkglist:
804                         if pkg not in ks.handler.packages.packageList:
805                             ks.handler.packages.packageList.append(pkg)
806                 else:
807                     i = i + 1