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