change the path of pykickstart module
[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 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Library General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 import os, sys
20 import shutil
21 import subprocess
22 import time
23 import logging
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         if not os.path.exists(self.path(repodir)):
405             fs.makedirs(self.path(repodir))
406         f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
407         self.__create_repo_section(repo, "base", f)
408         if repo.debuginfo:
409             self.__create_repo_section(repo, "debuginfo", f)
410         if repo.source:
411             self.__create_repo_section(repo, "source", f)
412         f.close()
413
414     def apply(self, ksrepo, repodata):
415         for repo in ksrepo.repoList:
416             if repo.save:
417                 #self.__create_repo_file(repo, "/etc/yum.repos.d")
418                 self.__create_repo_file(repo, "/etc/zypp/repos.d")
419         """ Import repo gpg keys """
420         if repodata:
421             dev_null = os.open("/dev/null", os.O_WRONLY)
422             for repo in repodata:
423                 if repo['repokey']:
424                     subprocess.call([fs.find_binary_path("rpm"), "--root=%s" % self.instroot, "--import", repo['repokey']],
425                                     stdout = dev_null, stderr = dev_null)
426             os.close(dev_null)
427
428 class RPMMacroConfig(KickstartConfig):
429     """A class to apply the specified rpm macros to the filesystem"""
430     def apply(self, ks):
431         if not ks:
432             return
433         if not os.path.exists(self.path("/etc/rpm")):
434             os.mkdir(self.path("/etc/rpm"))
435         f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
436         if exclude_docs(ks):
437             f.write("%_excludedocs 1\n")
438         f.write("%__file_context_path %{nil}\n")
439         if inst_langs(ks) != None:
440             f.write("%_install_langs ")
441             f.write(inst_langs(ks))
442             f.write("\n")
443         f.close()
444
445 class NetworkConfig(KickstartConfig):
446     """A class to apply a kickstart network configuration to a system."""
447     def write_ifcfg(self, network):
448         p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
449
450         f = file(p, "w+")
451         os.chmod(p, 0644)
452
453         f.write("DEVICE=%s\n" % network.device)
454         f.write("BOOTPROTO=%s\n" % network.bootProto)
455
456         if network.bootProto.lower() == "static":
457             if network.ip:
458                 f.write("IPADDR=%s\n" % network.ip)
459             if network.netmask:
460                 f.write("NETMASK=%s\n" % network.netmask)
461
462         if network.onboot:
463             f.write("ONBOOT=on\n")
464         else:
465             f.write("ONBOOT=off\n")
466
467         if network.essid:
468             f.write("ESSID=%s\n" % network.essid)
469
470         if network.ethtool:
471             if network.ethtool.find("autoneg") == -1:
472                 network.ethtool = "autoneg off " + network.ethtool
473             f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
474
475         if network.bootProto.lower() == "dhcp":
476             if network.hostname:
477                 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
478             if network.dhcpclass:
479                 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
480
481         if network.mtu:
482             f.write("MTU=%s\n" % network.mtu)
483
484         f.close()
485
486     def write_wepkey(self, network):
487         if not network.wepkey:
488             return
489
490         p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
491         f = file(p, "w+")
492         os.chmod(p, 0600)
493         f.write("KEY=%s\n" % network.wepkey)
494         f.close()
495
496     def write_sysconfig(self, useipv6, hostname, gateway):
497         path = self.path("/etc/sysconfig/network")
498         f = file(path, "w+")
499         os.chmod(path, 0644)
500
501         f.write("NETWORKING=yes\n")
502
503         if useipv6:
504             f.write("NETWORKING_IPV6=yes\n")
505         else:
506             f.write("NETWORKING_IPV6=no\n")
507
508         if hostname:
509             f.write("HOSTNAME=%s\n" % hostname)
510         else:
511             f.write("HOSTNAME=localhost.localdomain\n")
512
513         if gateway:
514             f.write("GATEWAY=%s\n" % gateway)
515
516         f.close()
517
518     def write_hosts(self, hostname):
519         localline = ""
520         if hostname and hostname != "localhost.localdomain":
521             localline += hostname + " "
522             l = hostname.split(".")
523             if len(l) > 1:
524                 localline += l[0] + " "
525         localline += "localhost.localdomain localhost"
526
527         path = self.path("/etc/hosts")
528         f = file(path, "w+")
529         os.chmod(path, 0644)
530         f.write("127.0.0.1\t\t%s\n" % localline)
531         f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
532         f.close()
533
534     def write_resolv(self, nodns, nameservers):
535         if nodns or not nameservers:
536             return
537
538         path = self.path("/etc/resolv.conf")
539         f = file(path, "w+")
540         os.chmod(path, 0644)
541
542         for ns in (nameservers):
543             if ns:
544                 f.write("nameserver %s\n" % ns)
545
546         f.close()
547
548     def apply(self, ksnet):
549         fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
550
551         useipv6 = False
552         nodns = False
553         hostname = None
554         gateway = None
555         nameservers = None
556
557         for network in ksnet.network:
558             if not network.device:
559                 raise errors.KsError("No --device specified with "
560                                             "network kickstart command")
561
562             if (network.onboot and network.bootProto.lower() != "dhcp" and
563                 not (network.ip and network.netmask)):
564                 raise errors.KsError("No IP address and/or netmask "
565                                             "specified with static "
566                                             "configuration for '%s'" %
567                                             network.device)
568
569             self.write_ifcfg(network)
570             self.write_wepkey(network)
571
572             if network.ipv6:
573                 useipv6 = True
574             if network.nodns:
575                 nodns = True
576
577             if network.hostname:
578                 hostname = network.hostname
579             if network.gateway:
580                 gateway = network.gateway
581
582             if network.nameserver:
583                 nameservers = network.nameserver.split(",")
584
585         self.write_sysconfig(useipv6, hostname, gateway)
586         self.write_hosts(hostname)
587         self.write_resolv(nodns, nameservers)
588
589
590 def get_image_size(ks, default = None):
591     __size = 0
592     for p in ks.handler.partition.partitions:
593         if p.mountpoint == "/" and p.size:
594             __size = p.size
595     if __size > 0:
596         return int(__size) * 1024L * 1024L
597     else:
598         return default
599
600 def get_image_fstype(ks, default = None):
601     for p in ks.handler.partition.partitions:
602         if p.mountpoint == "/" and p.fstype:
603             return p.fstype
604     return default
605
606 def get_image_fsopts(ks, default = None):
607     for p in ks.handler.partition.partitions:
608         if p.mountpoint == "/" and p.fsopts:
609             return p.fstype
610     return default
611
612 def get_modules(ks):
613     devices = []
614     if isinstance(ks.handler.device, kscommands.device.FC3_Device):
615         devices.append(ks.handler.device)
616     else:
617         devices.extend(ks.handler.device.deviceList)
618
619     modules = []
620     for device in devices:
621         if not device.moduleName:
622             continue
623         modules.extend(device.moduleName.split(":"))
624
625     return modules
626
627 def get_timeout(ks, default = None):
628     if not hasattr(ks.handler.bootloader, "timeout"):
629         return default
630     if ks.handler.bootloader.timeout is None:
631         return default
632     return int(ks.handler.bootloader.timeout)
633
634 def get_kernel_args(ks, default = "ro liveimg"):
635     if not hasattr(ks.handler.bootloader, "appendLine"):
636         return default
637     if ks.handler.bootloader.appendLine is None:
638         return default
639     return "%s %s" %(default, ks.handler.bootloader.appendLine)
640
641 def get_menu_args(ks, default = "liveinst"):
642     if not hasattr(ks.handler.bootloader, "menus"):
643         return default
644     if ks.handler.bootloader.menus in (None, ""):
645         return default
646     return "%s" % ks.handler.bootloader.menus
647
648 def get_default_kernel(ks, default = None):
649     if not hasattr(ks.handler.bootloader, "default"):
650         return default
651     if not ks.handler.bootloader.default:
652         return default
653     return ks.handler.bootloader.default
654
655 def get_repos(ks, repo_urls = {}):
656     repos = {}
657     for repo in ks.handler.repo.repoList:
658         inc = []
659         if hasattr(repo, "includepkgs"):
660             inc.extend(repo.includepkgs)
661
662         exc = []
663         if hasattr(repo, "excludepkgs"):
664             exc.extend(repo.excludepkgs)
665
666         baseurl = repo.baseurl
667         mirrorlist = repo.mirrorlist
668
669         if repo.name in repo_urls:
670             baseurl = repo_urls[repo.name]
671             mirrorlist = None
672
673         if repos.has_key(repo.name):
674             logging.warn("Overriding already specified repo %s" %(repo.name,))
675
676         proxy = None
677         if hasattr(repo, "proxy"):
678             proxy = repo.proxy
679         proxy_username = None
680         if hasattr(repo, "proxy_username"):
681             proxy_username = repo.proxy_username
682         proxy_password = None
683         if hasattr(repo, "proxy_password"):
684             proxy_password = repo.proxy_password
685         if hasattr(repo, "debuginfo"):
686             debuginfo = repo.debuginfo
687         if hasattr(repo, "source"):
688             source = repo.source
689         if hasattr(repo, "gpgkey"):
690             gpgkey = repo.gpgkey
691         if hasattr(repo, "disable"):
692             disable = repo.disable
693
694         repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc, proxy, proxy_username, proxy_password, debuginfo, source, gpgkey, disable)
695
696     return repos.values()
697
698 def convert_method_to_repo(ks):
699     try:
700         ks.handler.repo.methodToRepo()
701     except (AttributeError, kserrors.KickstartError):
702         pass
703
704 def get_packages(ks, required = []):
705     return ks.handler.packages.packageList + required
706
707 def get_groups(ks, required = []):
708     return ks.handler.packages.groupList + required
709
710 def get_excluded(ks, required = []):
711     return ks.handler.packages.excludedList + required
712
713 def get_partitions(ks, required = []):
714     return ks.handler.partition.partitions
715
716 def ignore_missing(ks):
717     return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
718
719 def exclude_docs(ks):
720     return ks.handler.packages.excludeDocs
721
722 def inst_langs(ks):
723     if hasattr(ks.handler.packages, "instLange"):
724         return ks.handler.packages.instLange
725     elif hasattr(ks.handler.packages, "instLangs"):
726         return ks.handler.packages.instLangs
727     return ""
728
729 def get_post_scripts(ks):
730     scripts = []
731     for s in ks.handler.scripts:
732         if s.type != ksparser.KS_SCRIPT_POST:
733             continue
734         scripts.append(s)
735     return scripts
736
737 def add_repo(ks, repostr):
738     args = repostr.split()
739     repoobj = ks.handler.repo.parse(args[1:])
740     if repoobj and repoobj not in ks.handler.repo.repoList:
741         ks.handler.repo.repoList.append(repoobj)
742
743 def remove_all_repos(ks):
744     while len(ks.handler.repo.repoList) != 0:
745         del ks.handler.repo.repoList[0]
746
747 def remove_duplicate_repos(ks):
748     i = 0
749     j = i + 1
750     while True:
751         if len(ks.handler.repo.repoList) < 2:
752             break
753         if i >= len(ks.handler.repo.repoList) - 1:
754             break
755         name = ks.handler.repo.repoList[i].name
756         baseurl = ks.handler.repo.repoList[i].baseurl
757         if j < len(ks.handler.repo.repoList):
758             if (ks.handler.repo.repoList[j].name == name or \
759                 ks.handler.repo.repoList[j].baseurl == baseurl):
760                 del ks.handler.repo.repoList[j]
761             else:
762                 j += 1
763             if j >= len(ks.handler.repo.repoList):
764                 i += 1
765                 j = i + 1
766         else:
767             i += 1
768             j = i + 1
769
770 def resolve_groups(creator, repometadata, use_comps = False):
771     pkgmgr = creator.pkgmgr.get_default_pkg_manager
772     iszypp = False
773     if creator.pkgmgr.managers.has_key("zypp") and creator.pkgmgr.managers['zypp'] == pkgmgr:
774         iszypp = True
775     ks = creator.ks
776
777     for repo in repometadata:
778         """ Mustn't replace group with package list if repo is ready for the corresponding package manager """
779         if iszypp and repo["patterns"] and not use_comps:
780             continue
781         if not iszypp and repo["comps"] and use_comps:
782             continue
783
784         """
785             But we also must handle such cases, use zypp but repo only has comps,
786             use yum but repo only has patterns, use zypp but use_comps is true,
787             use yum but use_comps is false.
788         """
789         groupfile = None
790         if iszypp:
791             if (use_comps and repo["comps"]) or (not repo["patterns"] and repo["comps"]):
792                 groupfile = repo["comps"]
793                 get_pkglist_handler = misc.get_pkglist_in_comps
794         if not iszypp:
795             if (not use_comps and repo["patterns"]) or (not repo["comps"] and repo["patterns"]):
796                 groupfile = repo["patterns"]
797                 get_pkglist_handler = misc.get_pkglist_in_patterns
798
799         if groupfile:
800             i = 0
801             while True:
802                 if i >= len(ks.handler.packages.groupList):
803                     break
804                 pkglist = get_pkglist_handler(ks.handler.packages.groupList[i].name, groupfile)
805                 if pkglist:
806                     del ks.handler.packages.groupList[i]
807                     for pkg in pkglist:
808                         if pkg not in ks.handler.packages.packageList:
809                             ks.handler.packages.packageList.append(pkg)
810                 else:
811                     i = i + 1