3 # Copyright (c) 2007 Red Hat, Inc.
4 # Copyright (c) 2009, 2010, 2011 Intel, Inc.
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
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
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.
26 from mic.utils import errors, misc, runner, fs_related as fs
28 import pykickstart.sections as kssections
29 import pykickstart.commands as kscommands
30 import pykickstart.constants as ksconstants
31 import pykickstart.errors as kserrors
32 import pykickstart.parser as ksparser
33 import pykickstart.version as ksversion
34 from pykickstart.handlers.control import commandMap
35 from pykickstart.handlers.control import dataMap
37 import custom_commands.desktop as desktop
38 import custom_commands.moblinrepo as moblinrepo
39 import custom_commands.micboot as micboot
40 import custom_commands.partition as partition
42 class PrepackageSection(kssections.Section):
43 sectionOpen = "%prepackages"
45 def handleLine(self, line):
49 (h, s, t) = line.partition('#')
52 self.handler.prepackages.add([line])
54 def handleHeader(self, lineno, args):
55 kssections.Section.handleHeader(self, lineno, args)
57 class AttachmentSection(kssections.Section):
58 sectionOpen = "%attachment"
60 def handleLine(self, line):
64 (h, s, t) = line.partition('#')
67 self.handler.attachment.add([line])
69 def handleHeader(self, lineno, args):
70 kssections.Section.handleHeader(self, lineno, args)
72 def read_kickstart(path):
73 """Parse a kickstart file and return a KickstartParser instance.
75 This is a simple utility function which takes a path to a kickstart file,
76 parses it and returns a pykickstart KickstartParser instance which can
77 be then passed to an ImageCreator constructor.
79 If an error occurs, a CreatorError exception is thrown.
82 #version = ksversion.makeVersion()
83 #ks = ksparser.KickstartParser(version)
85 using_version = ksversion.DEVEL
86 commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
87 commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
88 commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
89 commandMap[using_version]["part"] = partition.MeeGo_Partition
90 commandMap[using_version]["partition"] = partition.MeeGo_Partition
91 dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
92 dataMap[using_version]["PartData"] = partition.MeeGo_PartData
93 superclass = ksversion.returnClassForVersion(version=using_version)
95 class KSHandlers(superclass):
96 def __init__(self, mapping={}):
97 superclass.__init__(self, mapping=commandMap[using_version])
98 self.prepackages = ksparser.Packages()
99 self.attachment = ksparser.Packages()
101 ks = ksparser.KickstartParser(KSHandlers())
102 ks.registerSection(PrepackageSection(ks.handler))
103 ks.registerSection(AttachmentSection(ks.handler))
106 ks.readKickstart(path)
107 except kserrors.KickstartParseError, e:
108 msgptn = re.compile("^\D*(\d+).*(Section does not end with.*)$", re.S)
109 m = msgptn.match(str(e))
113 msger.warning("'%s:%s': %s" % (path, lineno, wrnmsg))
115 raise errors.KsError("'%s': %s" % (path, str(e)))
116 except kserrors.KickstartError, e:
117 raise errors.KsError("'%s': %s" % (path, str(e)))
120 def build_name(kscfg, prefix = None, suffix = None, maxlen = None):
121 """Construct and return an image name string.
123 This is a utility function to help create sensible name and fslabel
124 strings. The name is constructed using the sans-prefix-and-extension
125 kickstart filename and the supplied prefix and suffix.
127 If the name exceeds the maxlen length supplied, the prefix is first dropped
128 and then the kickstart filename portion is reduced until it fits. In other
129 words, the suffix takes precedence over the kickstart portion and the
130 kickstart portion takes precedence over the prefix.
132 kscfg -- a path to a kickstart file
133 prefix -- a prefix to prepend to the name; defaults to None, which causes
135 suffix -- a suffix to append to the name; defaults to None, which causes
136 a YYYYMMDDHHMM suffix to be used
137 maxlen -- the maximum length for the returned string; defaults to None,
138 which means there is no restriction on the name length
140 Note, if maxlen is less then the len(suffix), you get to keep both pieces.
143 name = os.path.basename(kscfg)
144 idx = name.rfind('.')
151 suffix = time.strftime("%Y%m%d%H%M")
153 if name.startswith(prefix):
154 name = name[len(prefix):]
156 ret = prefix + name + "-" + suffix
157 if not maxlen is None and len(ret) > maxlen:
158 ret = name[:maxlen - len(suffix) - 1] + "-" + suffix
162 class KickstartConfig(object):
163 """A base class for applying kickstart configurations to a system."""
164 def __init__(self, instroot):
165 self.instroot = instroot
167 def path(self, subpath):
168 return self.instroot + subpath
170 def _check_sysconfig(self):
171 if not os.path.exists(self.path("/etc/sysconfig")):
172 fs.makedirs(self.path("/etc/sysconfig"))
175 os.chroot(self.instroot)
178 def call(self, args):
179 if not os.path.exists("%s/%s" %(self.instroot, args[0])):
180 msger.warning("%s/%s" %(self.instroot, args[0]))
181 raise errors.KsError("Unable to run %s!" %(args))
182 subprocess.call(args, preexec_fn = self.chroot)
187 class LanguageConfig(KickstartConfig):
188 """A class to apply a kickstart language configuration to a system."""
189 def apply(self, kslang):
190 self._check_sysconfig()
192 f = open(self.path("/etc/sysconfig/i18n"), "w+")
193 f.write("LANG=\"" + kslang.lang + "\"\n")
196 class KeyboardConfig(KickstartConfig):
197 """A class to apply a kickstart keyboard configuration to a system."""
198 def apply(self, kskeyboard):
201 # should this impact the X keyboard config too?
202 # or do we want to make X be able to do this mapping?
204 #k = rhpl.keyboard.Keyboard()
205 #if kskeyboard.keyboard:
206 # k.set(kskeyboard.keyboard)
207 #k.write(self.instroot)
210 class TimezoneConfig(KickstartConfig):
211 """A class to apply a kickstart timezone configuration to a system."""
212 def apply(self, kstimezone):
213 self._check_sysconfig()
214 tz = kstimezone.timezone or "America/New_York"
215 utc = str(kstimezone.isUtc)
217 f = open(self.path("/etc/sysconfig/clock"), "w+")
218 f.write("ZONE=\"" + tz + "\"\n")
219 f.write("UTC=" + utc + "\n")
221 tz_source = "/usr/share/zoneinfo/%s" % (tz)
222 tz_dest = "/etc/localtime"
224 self.call(["/bin/cp", "-f", tz_source, tz_dest])
225 except (IOError, OSError), (errno, msg):
226 msger.warning("Failed to copy timezone info from '%s' to '%s': %s" \
227 % (tz_source, tz_dest, msg))
229 class AuthConfig(KickstartConfig):
230 """A class to apply a kickstart authconfig configuration to a system."""
231 def apply(self, ksauthconfig):
232 auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
233 args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
234 self.call(args + auth.split())
236 class FirewallConfig(KickstartConfig):
237 """A class to apply a kickstart firewall configuration to a system."""
238 def apply(self, ksfirewall):
240 # FIXME: should handle the rest of the options
242 if not os.path.exists(self.path("/usr/sbin/lokkit")):
244 if ksfirewall.enabled:
247 status = "--disabled"
249 self.call(["/usr/sbin/lokkit",
250 "-f", "--quiet", "--nostart", status])
252 class RootPasswordConfig(KickstartConfig):
253 """A class to apply a kickstart root password configuration to a system."""
255 self.call(["/usr/bin/passwd", "-d", "root"])
257 def set_encrypted(self, password):
258 self.call(["/usr/sbin/usermod", "-p", password, "root"])
260 def set_unencrypted(self, password):
261 for p in ("/bin/echo", "/usr/sbin/chpasswd"):
262 if not os.path.exists("%s/%s" %(self.instroot, p)):
263 raise errors.KsError("Unable to set unencrypted password due "
266 p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
267 stdout = subprocess.PIPE,
268 preexec_fn = self.chroot)
269 p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
271 stdout = subprocess.PIPE,
272 preexec_fn = self.chroot)
275 def apply(self, ksrootpw):
276 if ksrootpw.isCrypted:
277 self.set_encrypted(ksrootpw.password)
278 elif ksrootpw.password != "":
279 self.set_unencrypted(ksrootpw.password)
283 class UserConfig(KickstartConfig):
284 def set_empty_passwd(self, user):
285 self.call(["/usr/bin/passwd", "-d", user])
287 def set_encrypted_passwd(self, user, password):
288 self.call(["/usr/sbin/usermod", "-p", "%s" % password, user])
290 def set_unencrypted_passwd(self, user, password):
291 for p in ("/bin/echo", "/usr/sbin/chpasswd"):
292 if not os.path.exists("%s/%s" %(self.instroot, p)):
293 raise errors.KsError("Unable to set unencrypted password due "
296 p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
297 stdout = subprocess.PIPE,
298 preexec_fn = self.chroot)
299 p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
301 stdout = subprocess.PIPE,
302 preexec_fn = self.chroot)
305 def addUser(self, userconfig):
306 args = [ "/usr/sbin/useradd" ]
307 if userconfig.groups:
308 args += [ "--groups", string.join(userconfig.groups, ",") ]
310 args.append(userconfig.name)
312 dev_null = os.open("/dev/null", os.O_WRONLY)
313 subprocess.call(args,
316 preexec_fn = self.chroot)
319 msger.warning('Cannot add user using "useradd"')
321 if userconfig.password not in (None, ""):
322 if userconfig.isCrypted:
323 self.set_encrypted_passwd(userconfig.name,
326 self.set_unencrypted_passwd(userconfig.name,
329 self.set_empty_passwd(userconfig.name)
331 raise errors.KsError("Invalid kickstart command: %s" \
332 % userconfig.__str__())
334 def apply(self, user):
335 for userconfig in user.userList:
337 self.addUser(userconfig)
341 class ServicesConfig(KickstartConfig):
342 """A class to apply a kickstart services configuration to a system."""
343 def apply(self, ksservices):
344 if not os.path.exists(self.path("/sbin/chkconfig")):
346 for s in ksservices.enabled:
347 self.call(["/sbin/chkconfig", s, "on"])
348 for s in ksservices.disabled:
349 self.call(["/sbin/chkconfig", s, "off"])
351 class XConfig(KickstartConfig):
352 """A class to apply a kickstart X configuration to a system."""
353 def apply(self, ksxconfig):
354 if ksxconfig.startX and os.path.exists(self.path("/etc/inittab")):
355 f = open(self.path("/etc/inittab"), "rw+")
357 buf = buf.replace("id:3:initdefault", "id:5:initdefault")
361 if ksxconfig.defaultdesktop:
362 self._check_sysconfig()
363 f = open(self.path("/etc/sysconfig/desktop"), "w")
364 f.write("DESKTOP="+ksxconfig.defaultdesktop+"\n")
367 class DesktopConfig(KickstartConfig):
368 """A class to apply a kickstart desktop configuration to a system."""
369 def apply(self, ksdesktop):
370 if ksdesktop.defaultdesktop:
371 self._check_sysconfig()
372 f = open(self.path("/etc/sysconfig/desktop"), "w")
373 f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n")
375 if os.path.exists(self.path("/etc/gdm/custom.conf")):
376 f = open(self.path("/etc/skel/.dmrc"), "w")
377 f.write("[Desktop]\n")
378 f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n")
380 if ksdesktop.session:
381 if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
382 f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
383 f.write("session="+ksdesktop.session.lower()+"\n")
385 if ksdesktop.autologinuser:
386 self._check_sysconfig()
387 f = open(self.path("/etc/sysconfig/desktop"), "a+")
388 f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n")
390 if os.path.exists(self.path("/etc/gdm/custom.conf")):
391 f = open(self.path("/etc/gdm/custom.conf"), "w")
392 f.write("[daemon]\n")
393 f.write("AutomaticLoginEnable=true\n")
394 f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n")
397 class MoblinRepoConfig(KickstartConfig):
398 """A class to apply a kickstart desktop configuration to a system."""
399 def __create_repo_section(self, repo, type, fd):
402 reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"}
403 reponame = repo.name + reposuffix[type]
406 baseurl = repo.baseurl
408 mirrorlist = repo.mirrorlist
410 elif type == "debuginfo":
412 if repo.baseurl.endswith("/"):
413 baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
415 baseurl = os.path.dirname(repo.baseurl)
419 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
420 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
421 mirrorlist += "debug" + "-" + variant
423 elif type == "source":
425 if repo.baseurl.endswith("/"):
426 baseurl = os.path.dirname(
428 os.path.dirname(repo.baseurl)))
430 baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
434 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
435 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
436 mirrorlist += "source" + "-" + variant
438 fd.write("[" + reponame + "]\n")
439 fd.write("name=" + reponame + "\n")
440 fd.write("failovermethod=priority\n")
442 fd.write("baseurl=" + baseurl + "\n")
444 fd.write("mirrorlist=" + mirrorlist + "\n")
445 """ Skip saving proxy settings """
447 # fd.write("proxy=" + repo.proxy + "\n")
448 #if repo.proxy_username:
449 # fd.write("proxy_username=" + repo.proxy_username + "\n")
450 #if repo.proxy_password:
451 # fd.write("proxy_password=" + repo.proxy_password + "\n")
453 fd.write("gpgkey=" + repo.gpgkey + "\n")
454 fd.write("gpgcheck=1\n")
456 fd.write("gpgcheck=0\n")
457 if type == "source" or type == "debuginfo" or repo.disable:
458 fd.write("enabled=0\n")
460 fd.write("enabled=1\n")
463 def __create_repo_file(self, repo, repodir):
464 fs.makedirs(self.path(repodir))
465 f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
466 self.__create_repo_section(repo, "base", f)
468 self.__create_repo_section(repo, "debuginfo", f)
470 self.__create_repo_section(repo, "source", f)
473 def apply(self, ksrepo, repodata):
474 for repo in ksrepo.repoList:
476 #self.__create_repo_file(repo, "/etc/yum.repos.d")
477 self.__create_repo_file(repo, "/etc/zypp/repos.d")
478 """ Import repo gpg keys """
480 for repo in repodata:
483 "--root=%s" % self.instroot,
487 class RPMMacroConfig(KickstartConfig):
488 """A class to apply the specified rpm macros to the filesystem"""
492 if not os.path.exists(self.path("/etc/rpm")):
493 os.mkdir(self.path("/etc/rpm"))
494 f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
496 f.write("%_excludedocs 1\n")
497 f.write("%__file_context_path %{nil}\n")
498 if inst_langs(ks) != None:
499 f.write("%_install_langs ")
500 f.write(inst_langs(ks))
504 class NetworkConfig(KickstartConfig):
505 """A class to apply a kickstart network configuration to a system."""
506 def write_ifcfg(self, network):
507 p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
512 f.write("DEVICE=%s\n" % network.device)
513 f.write("BOOTPROTO=%s\n" % network.bootProto)
515 if network.bootProto.lower() == "static":
517 f.write("IPADDR=%s\n" % network.ip)
519 f.write("NETMASK=%s\n" % network.netmask)
522 f.write("ONBOOT=on\n")
524 f.write("ONBOOT=off\n")
527 f.write("ESSID=%s\n" % network.essid)
530 if network.ethtool.find("autoneg") == -1:
531 network.ethtool = "autoneg off " + network.ethtool
532 f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
534 if network.bootProto.lower() == "dhcp":
536 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
537 if network.dhcpclass:
538 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
541 f.write("MTU=%s\n" % network.mtu)
545 def write_wepkey(self, network):
546 if not network.wepkey:
549 p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
552 f.write("KEY=%s\n" % network.wepkey)
555 def write_sysconfig(self, useipv6, hostname, gateway):
556 path = self.path("/etc/sysconfig/network")
560 f.write("NETWORKING=yes\n")
563 f.write("NETWORKING_IPV6=yes\n")
565 f.write("NETWORKING_IPV6=no\n")
568 f.write("HOSTNAME=%s\n" % hostname)
570 f.write("HOSTNAME=localhost.localdomain\n")
573 f.write("GATEWAY=%s\n" % gateway)
577 def write_hosts(self, hostname):
579 if hostname and hostname != "localhost.localdomain":
580 localline += hostname + " "
581 l = hostname.split(".")
583 localline += l[0] + " "
584 localline += "localhost.localdomain localhost"
586 path = self.path("/etc/hosts")
589 f.write("127.0.0.1\t\t%s\n" % localline)
590 f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
593 def write_resolv(self, nodns, nameservers):
594 if nodns or not nameservers:
597 path = self.path("/etc/resolv.conf")
601 for ns in (nameservers):
603 f.write("nameserver %s\n" % ns)
607 def apply(self, ksnet):
608 fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
616 for network in ksnet.network:
617 if not network.device:
618 raise errors.KsError("No --device specified with "
619 "network kickstart command")
621 if (network.onboot and network.bootProto.lower() != "dhcp" and
622 not (network.ip and network.netmask)):
623 raise errors.KsError("No IP address and/or netmask "
624 "specified with static "
625 "configuration for '%s'" %
628 self.write_ifcfg(network)
629 self.write_wepkey(network)
637 hostname = network.hostname
639 gateway = network.gateway
641 if network.nameserver:
642 nameservers = network.nameserver.split(",")
644 self.write_sysconfig(useipv6, hostname, gateway)
645 self.write_hosts(hostname)
646 self.write_resolv(nodns, nameservers)
649 def get_image_size(ks, default = None):
651 for p in ks.handler.partition.partitions:
652 if p.mountpoint == "/" and p.size:
655 return int(__size) * 1024L * 1024L
659 def get_image_fstype(ks, default = None):
660 for p in ks.handler.partition.partitions:
661 if p.mountpoint == "/" and p.fstype:
665 def get_image_fsopts(ks, default = None):
666 for p in ks.handler.partition.partitions:
667 if p.mountpoint == "/" and p.fsopts:
673 if isinstance(ks.handler.device, kscommands.device.FC3_Device):
674 devices.append(ks.handler.device)
676 devices.extend(ks.handler.device.deviceList)
679 for device in devices:
680 if not device.moduleName:
682 modules.extend(device.moduleName.split(":"))
686 def get_timeout(ks, default = None):
687 if not hasattr(ks.handler.bootloader, "timeout"):
689 if ks.handler.bootloader.timeout is None:
691 return int(ks.handler.bootloader.timeout)
693 def get_kernel_args(ks, default = "ro liveimg"):
694 if not hasattr(ks.handler.bootloader, "appendLine"):
696 if ks.handler.bootloader.appendLine is None:
698 return "%s %s" %(default, ks.handler.bootloader.appendLine)
700 def get_menu_args(ks, default = ""):
701 if not hasattr(ks.handler.bootloader, "menus"):
703 if ks.handler.bootloader.menus in (None, ""):
705 return "%s" % ks.handler.bootloader.menus
707 def get_default_kernel(ks, default = None):
708 if not hasattr(ks.handler.bootloader, "default"):
710 if not ks.handler.bootloader.default:
712 return ks.handler.bootloader.default
714 def get_repos(ks, repo_urls = {}):
716 for repo in ks.handler.repo.repoList:
718 if hasattr(repo, "includepkgs"):
719 inc.extend(repo.includepkgs)
722 if hasattr(repo, "excludepkgs"):
723 exc.extend(repo.excludepkgs)
725 baseurl = repo.baseurl
726 mirrorlist = repo.mirrorlist
728 if repo.name in repo_urls:
729 baseurl = repo_urls[repo.name]
732 if repos.has_key(repo.name):
733 msger.warning("Overriding already specified repo %s" %(repo.name,))
736 if hasattr(repo, "proxy"):
738 proxy_username = None
739 if hasattr(repo, "proxy_username"):
740 proxy_username = repo.proxy_username
741 proxy_password = None
742 if hasattr(repo, "proxy_password"):
743 proxy_password = repo.proxy_password
744 if hasattr(repo, "debuginfo"):
745 debuginfo = repo.debuginfo
746 if hasattr(repo, "source"):
748 if hasattr(repo, "gpgkey"):
750 if hasattr(repo, "disable"):
751 disable = repo.disable
753 if hasattr(repo, "ssl_verify"):
754 ssl_verify = repo.ssl_verify == "yes"
756 if hasattr(repo, "cost"):
759 if hasattr(repo, "priority"):
760 priority = repo.priority
762 repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc,
763 proxy, proxy_username, proxy_password, debuginfo,
764 source, gpgkey, disable, ssl_verify, cost, priority)
766 return repos.values()
768 def convert_method_to_repo(ks):
770 ks.handler.repo.methodToRepo()
771 except (AttributeError, kserrors.KickstartError):
774 def get_attachment(ks, required = []):
775 return ks.handler.attachment.packageList + required
777 def get_pre_packages(ks, required = []):
778 return ks.handler.prepackages.packageList + required
780 def get_packages(ks, required = []):
781 return ks.handler.packages.packageList + required
783 def get_groups(ks, required = []):
784 return ks.handler.packages.groupList + required
786 def get_excluded(ks, required = []):
787 return ks.handler.packages.excludedList + required
789 def get_partitions(ks, required = []):
790 return ks.handler.partition.partitions
792 def ignore_missing(ks):
793 return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
795 def exclude_docs(ks):
796 return ks.handler.packages.excludeDocs
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
805 def get_post_scripts(ks):
807 for s in ks.handler.scripts:
808 if s.type != ksparser.KS_SCRIPT_POST:
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)
819 def remove_all_repos(ks):
820 while len(ks.handler.repo.repoList) != 0:
821 del ks.handler.repo.repoList[0]
823 def remove_duplicate_repos(ks):
827 if len(ks.handler.repo.repoList) < 2:
829 if i >= len(ks.handler.repo.repoList) - 1:
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]
839 if j >= len(ks.handler.repo.repoList):
846 def resolve_groups(creatoropts, repometadata):
848 if 'zypp' == creatoropts['pkgmgr']:
850 ks = creatoropts['ks']
852 for repo in repometadata:
853 """ Mustn't replace group with package list if repo is ready for the
854 corresponding package manager.
857 if iszypp and repo["patterns"]:
859 if not iszypp and repo["comps"]:
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.
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
876 if i >= len(ks.handler.packages.groupList):
878 pkglist = get_pkglist_handler(
879 ks.handler.packages.groupList[i].name,
882 del ks.handler.packages.groupList[i]
884 if pkg not in ks.handler.packages.packageList:
885 ks.handler.packages.packageList.append(pkg)