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