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