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