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