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 def read_kickstart(path):
58 """Parse a kickstart file and return a KickstartParser instance.
60 This is a simple utility function which takes a path to a kickstart file,
61 parses it and returns a pykickstart KickstartParser instance which can
62 be then passed to an ImageCreator constructor.
64 If an error occurs, a CreatorError exception is thrown.
67 #version = ksversion.makeVersion()
68 #ks = ksparser.KickstartParser(version)
70 using_version = ksversion.DEVEL
71 commandMap[using_version]["desktop"] = desktop.Moblin_Desktop
72 commandMap[using_version]["repo"] = moblinrepo.Moblin_Repo
73 commandMap[using_version]["bootloader"] = micboot.Moblin_Bootloader
74 commandMap[using_version]["part"] = partition.MeeGo_Partition
75 commandMap[using_version]["partition"] = partition.MeeGo_Partition
76 dataMap[using_version]["RepoData"] = moblinrepo.Moblin_RepoData
77 dataMap[using_version]["PartData"] = partition.MeeGo_PartData
78 superclass = ksversion.returnClassForVersion(version=using_version)
80 class KSHandlers(superclass):
81 def __init__(self, mapping={}):
82 superclass.__init__(self, mapping=commandMap[using_version])
83 self.prepackages = ksparser.Packages()
85 ks = ksparser.KickstartParser(KSHandlers())
86 ks.registerSection(PrepackageSection(ks.handler))
89 ks.readKickstart(path)
90 except kserrors.KickstartParseError, e:
91 msgptn = re.compile("^\D*(\d+).*(Section does not end with.*)$", re.S)
92 m = msgptn.match(str(e))
96 msger.warning("'%s:%s': %s" % (path, lineno, wrnmsg))
98 raise errors.KsError("'%s': %s" % (path, str(e)))
99 except kserrors.KickstartError, e:
100 raise errors.KsError("'%s': %s" % (path, str(e)))
103 def build_name(kscfg, prefix = None, suffix = None, maxlen = None):
104 """Construct and return an image name string.
106 This is a utility function to help create sensible name and fslabel
107 strings. The name is constructed using the sans-prefix-and-extension
108 kickstart filename and the supplied prefix and suffix.
110 If the name exceeds the maxlen length supplied, the prefix is first dropped
111 and then the kickstart filename portion is reduced until it fits. In other
112 words, the suffix takes precedence over the kickstart portion and the
113 kickstart portion takes precedence over the prefix.
115 kscfg -- a path to a kickstart file
116 prefix -- a prefix to prepend to the name; defaults to None, which causes
118 suffix -- a suffix to append to the name; defaults to None, which causes
119 a YYYYMMDDHHMM suffix to be used
120 maxlen -- the maximum length for the returned string; defaults to None,
121 which means there is no restriction on the name length
123 Note, if maxlen is less then the len(suffix), you get to keep both pieces.
126 name = os.path.basename(kscfg)
127 idx = name.rfind('.')
134 suffix = time.strftime("%Y%m%d%H%M")
136 if name.startswith(prefix):
137 name = name[len(prefix):]
139 ret = prefix + name + "-" + suffix
140 if not maxlen is None and len(ret) > maxlen:
141 ret = name[:maxlen - len(suffix) - 1] + "-" + suffix
145 class KickstartConfig(object):
146 """A base class for applying kickstart configurations to a system."""
147 def __init__(self, instroot):
148 self.instroot = instroot
150 def path(self, subpath):
151 return self.instroot + subpath
153 def _check_sysconfig(self):
154 if not os.path.exists(self.path("/etc/sysconfig")):
155 fs.makedirs(self.path("/etc/sysconfig"))
158 os.chroot(self.instroot)
161 def call(self, args):
162 if not os.path.exists("%s/%s" %(self.instroot, args[0])):
163 msger.warning("%s/%s" %(self.instroot, args[0]))
164 raise errors.KsError("Unable to run %s!" %(args))
165 subprocess.call(args, preexec_fn = self.chroot)
170 class LanguageConfig(KickstartConfig):
171 """A class to apply a kickstart language configuration to a system."""
172 def apply(self, kslang):
173 self._check_sysconfig()
175 f = open(self.path("/etc/sysconfig/i18n"), "w+")
176 f.write("LANG=\"" + kslang.lang + "\"\n")
179 class KeyboardConfig(KickstartConfig):
180 """A class to apply a kickstart keyboard configuration to a system."""
181 def apply(self, kskeyboard):
184 # should this impact the X keyboard config too?
185 # or do we want to make X be able to do this mapping?
187 #k = rhpl.keyboard.Keyboard()
188 #if kskeyboard.keyboard:
189 # k.set(kskeyboard.keyboard)
190 #k.write(self.instroot)
193 class TimezoneConfig(KickstartConfig):
194 """A class to apply a kickstart timezone configuration to a system."""
195 def apply(self, kstimezone):
196 self._check_sysconfig()
197 tz = kstimezone.timezone or "America/New_York"
198 utc = str(kstimezone.isUtc)
200 f = open(self.path("/etc/sysconfig/clock"), "w+")
201 f.write("ZONE=\"" + tz + "\"\n")
202 f.write("UTC=" + utc + "\n")
204 tz_source = self.path("/usr/share/zoneinfo/%s" % (tz))
205 tz_dest = self.path("/etc/localtime")
207 shutil.copyfile(tz_source, tz_dest)
208 except (IOError, OSError), (errno, msg):
209 msger.warning("Error copying timezone info from "
211 % (tz_source, tz_dest, msg))
213 class AuthConfig(KickstartConfig):
214 """A class to apply a kickstart authconfig configuration to a system."""
215 def apply(self, ksauthconfig):
216 auth = ksauthconfig.authconfig or "--useshadow --enablemd5"
217 args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"]
218 self.call(args + auth.split())
220 class FirewallConfig(KickstartConfig):
221 """A class to apply a kickstart firewall configuration to a system."""
222 def apply(self, ksfirewall):
224 # FIXME: should handle the rest of the options
226 if not os.path.exists(self.path("/usr/sbin/lokkit")):
228 if ksfirewall.enabled:
231 status = "--disabled"
233 self.call(["/usr/sbin/lokkit",
234 "-f", "--quiet", "--nostart", status])
236 class RootPasswordConfig(KickstartConfig):
237 """A class to apply a kickstart root password configuration to a system."""
239 self.call(["/usr/bin/passwd", "-d", "root"])
241 def set_encrypted(self, password):
242 self.call(["/usr/sbin/usermod", "-p", password, "root"])
244 def set_unencrypted(self, password):
245 for p in ("/bin/echo", "/usr/sbin/chpasswd"):
246 if not os.path.exists("%s/%s" %(self.instroot, p)):
247 raise errors.KsError("Unable to set unencrypted password due "
250 p1 = subprocess.Popen(["/bin/echo", "root:%s" %password],
251 stdout = subprocess.PIPE,
252 preexec_fn = self.chroot)
253 p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
255 stdout = subprocess.PIPE,
256 preexec_fn = self.chroot)
259 def apply(self, ksrootpw):
260 if ksrootpw.isCrypted:
261 self.set_encrypted(ksrootpw.password)
262 elif ksrootpw.password != "":
263 self.set_unencrypted(ksrootpw.password)
267 class UserConfig(KickstartConfig):
268 def set_empty_passwd(self, user):
269 self.call(["/usr/bin/passwd", "-d", user])
271 def set_encrypted_passwd(self, user, password):
272 self.call(["/usr/sbin/usermod", "-p", "%s" % password, user])
274 def set_unencrypted_passwd(self, user, password):
275 for p in ("/bin/echo", "/usr/sbin/chpasswd"):
276 if not os.path.exists("%s/%s" %(self.instroot, p)):
277 raise errors.KsError("Unable to set unencrypted password due "
280 p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)],
281 stdout = subprocess.PIPE,
282 preexec_fn = self.chroot)
283 p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"],
285 stdout = subprocess.PIPE,
286 preexec_fn = self.chroot)
289 def addUser(self, userconfig):
290 args = [ "/usr/sbin/useradd" ]
291 if userconfig.groups:
292 args += [ "--groups", string.join(userconfig.groups, ",") ]
294 args.append(userconfig.name)
296 dev_null = os.open("/dev/null", os.O_WRONLY)
297 subprocess.call(args,
300 preexec_fn = self.chroot)
303 msger.warning('Cannot add user using "useradd"')
305 if userconfig.password not in (None, ""):
306 if userconfig.isCrypted:
307 self.set_encrypted_passwd(userconfig.name,
310 self.set_unencrypted_passwd(userconfig.name,
313 self.set_empty_passwd(userconfig.name)
315 raise errors.KsError("Invalid kickstart command: %s" \
316 % userconfig.__str__())
318 def apply(self, user):
319 for userconfig in user.userList:
321 self.addUser(userconfig)
325 class ServicesConfig(KickstartConfig):
326 """A class to apply a kickstart services configuration to a system."""
327 def apply(self, ksservices):
328 if not os.path.exists(self.path("/sbin/chkconfig")):
330 for s in ksservices.enabled:
331 self.call(["/sbin/chkconfig", s, "on"])
332 for s in ksservices.disabled:
333 self.call(["/sbin/chkconfig", s, "off"])
335 class XConfig(KickstartConfig):
336 """A class to apply a kickstart X configuration to a system."""
337 def apply(self, ksxconfig):
338 if ksxconfig.startX and os.path.exists(self.path("/etc/inittab")):
339 f = open(self.path("/etc/inittab"), "rw+")
341 buf = buf.replace("id:3:initdefault", "id:5:initdefault")
345 if ksxconfig.defaultdesktop:
346 self._check_sysconfig()
347 f = open(self.path("/etc/sysconfig/desktop"), "w")
348 f.write("DESKTOP="+ksxconfig.defaultdesktop+"\n")
351 class DesktopConfig(KickstartConfig):
352 """A class to apply a kickstart desktop configuration to a system."""
353 def apply(self, ksdesktop):
354 if ksdesktop.defaultdesktop:
355 self._check_sysconfig()
356 f = open(self.path("/etc/sysconfig/desktop"), "w")
357 f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n")
359 if os.path.exists(self.path("/etc/gdm/custom.conf")):
360 f = open(self.path("/etc/skel/.dmrc"), "w")
361 f.write("[Desktop]\n")
362 f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n")
364 if ksdesktop.session:
365 if os.path.exists(self.path("/etc/sysconfig/uxlaunch")):
366 f = open(self.path("/etc/sysconfig/uxlaunch"), "a+")
367 f.write("session="+ksdesktop.session.lower()+"\n")
369 if ksdesktop.autologinuser:
370 self._check_sysconfig()
371 f = open(self.path("/etc/sysconfig/desktop"), "a+")
372 f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n")
374 if os.path.exists(self.path("/etc/gdm/custom.conf")):
375 f = open(self.path("/etc/gdm/custom.conf"), "w")
376 f.write("[daemon]\n")
377 f.write("AutomaticLoginEnable=true\n")
378 f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n")
381 class MoblinRepoConfig(KickstartConfig):
382 """A class to apply a kickstart desktop configuration to a system."""
383 def __create_repo_section(self, repo, type, fd):
386 reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"}
387 reponame = repo.name + reposuffix[type]
390 baseurl = repo.baseurl
392 mirrorlist = repo.mirrorlist
394 elif type == "debuginfo":
396 if repo.baseurl.endswith("/"):
397 baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
399 baseurl = os.path.dirname(repo.baseurl)
403 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
404 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
405 mirrorlist += "debug" + "-" + variant
407 elif type == "source":
409 if repo.baseurl.endswith("/"):
410 baseurl = os.path.dirname(
412 os.path.dirname(repo.baseurl)))
414 baseurl = os.path.dirname(os.path.dirname(repo.baseurl))
418 variant = repo.mirrorlist[repo.mirrorlist.find("$"):]
419 mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")]
420 mirrorlist += "source" + "-" + variant
422 fd.write("[" + reponame + "]\n")
423 fd.write("name=" + reponame + "\n")
424 fd.write("failovermethod=priority\n")
426 fd.write("baseurl=" + baseurl + "\n")
428 fd.write("mirrorlist=" + mirrorlist + "\n")
429 """ Skip saving proxy settings """
431 # fd.write("proxy=" + repo.proxy + "\n")
432 #if repo.proxy_username:
433 # fd.write("proxy_username=" + repo.proxy_username + "\n")
434 #if repo.proxy_password:
435 # fd.write("proxy_password=" + repo.proxy_password + "\n")
437 fd.write("gpgkey=" + repo.gpgkey + "\n")
438 fd.write("gpgcheck=1\n")
440 fd.write("gpgcheck=0\n")
441 if type == "source" or type == "debuginfo" or repo.disable:
442 fd.write("enabled=0\n")
444 fd.write("enabled=1\n")
447 def __create_repo_file(self, repo, repodir):
448 fs.makedirs(self.path(repodir))
449 f = open(self.path(repodir + "/" + repo.name + ".repo"), "w")
450 self.__create_repo_section(repo, "base", f)
452 self.__create_repo_section(repo, "debuginfo", f)
454 self.__create_repo_section(repo, "source", f)
457 def apply(self, ksrepo, repodata):
458 for repo in ksrepo.repoList:
460 #self.__create_repo_file(repo, "/etc/yum.repos.d")
461 self.__create_repo_file(repo, "/etc/zypp/repos.d")
462 """ Import repo gpg keys """
464 for repo in repodata:
467 "--root=%s" % self.instroot,
471 class RPMMacroConfig(KickstartConfig):
472 """A class to apply the specified rpm macros to the filesystem"""
476 if not os.path.exists(self.path("/etc/rpm")):
477 os.mkdir(self.path("/etc/rpm"))
478 f = open(self.path("/etc/rpm/macros.imgcreate"), "w+")
480 f.write("%_excludedocs 1\n")
481 f.write("%__file_context_path %{nil}\n")
482 if inst_langs(ks) != None:
483 f.write("%_install_langs ")
484 f.write(inst_langs(ks))
488 class NetworkConfig(KickstartConfig):
489 """A class to apply a kickstart network configuration to a system."""
490 def write_ifcfg(self, network):
491 p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device)
496 f.write("DEVICE=%s\n" % network.device)
497 f.write("BOOTPROTO=%s\n" % network.bootProto)
499 if network.bootProto.lower() == "static":
501 f.write("IPADDR=%s\n" % network.ip)
503 f.write("NETMASK=%s\n" % network.netmask)
506 f.write("ONBOOT=on\n")
508 f.write("ONBOOT=off\n")
511 f.write("ESSID=%s\n" % network.essid)
514 if network.ethtool.find("autoneg") == -1:
515 network.ethtool = "autoneg off " + network.ethtool
516 f.write("ETHTOOL_OPTS=%s\n" % network.ethtool)
518 if network.bootProto.lower() == "dhcp":
520 f.write("DHCP_HOSTNAME=%s\n" % network.hostname)
521 if network.dhcpclass:
522 f.write("DHCP_CLASSID=%s\n" % network.dhcpclass)
525 f.write("MTU=%s\n" % network.mtu)
529 def write_wepkey(self, network):
530 if not network.wepkey:
533 p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device)
536 f.write("KEY=%s\n" % network.wepkey)
539 def write_sysconfig(self, useipv6, hostname, gateway):
540 path = self.path("/etc/sysconfig/network")
544 f.write("NETWORKING=yes\n")
547 f.write("NETWORKING_IPV6=yes\n")
549 f.write("NETWORKING_IPV6=no\n")
552 f.write("HOSTNAME=%s\n" % hostname)
554 f.write("HOSTNAME=localhost.localdomain\n")
557 f.write("GATEWAY=%s\n" % gateway)
561 def write_hosts(self, hostname):
563 if hostname and hostname != "localhost.localdomain":
564 localline += hostname + " "
565 l = hostname.split(".")
567 localline += l[0] + " "
568 localline += "localhost.localdomain localhost"
570 path = self.path("/etc/hosts")
573 f.write("127.0.0.1\t\t%s\n" % localline)
574 f.write("::1\t\tlocalhost6.localdomain6 localhost6\n")
577 def write_resolv(self, nodns, nameservers):
578 if nodns or not nameservers:
581 path = self.path("/etc/resolv.conf")
585 for ns in (nameservers):
587 f.write("nameserver %s\n" % ns)
591 def apply(self, ksnet):
592 fs.makedirs(self.path("/etc/sysconfig/network-scripts"))
600 for network in ksnet.network:
601 if not network.device:
602 raise errors.KsError("No --device specified with "
603 "network kickstart command")
605 if (network.onboot and network.bootProto.lower() != "dhcp" and
606 not (network.ip and network.netmask)):
607 raise errors.KsError("No IP address and/or netmask "
608 "specified with static "
609 "configuration for '%s'" %
612 self.write_ifcfg(network)
613 self.write_wepkey(network)
621 hostname = network.hostname
623 gateway = network.gateway
625 if network.nameserver:
626 nameservers = network.nameserver.split(",")
628 self.write_sysconfig(useipv6, hostname, gateway)
629 self.write_hosts(hostname)
630 self.write_resolv(nodns, nameservers)
633 def get_image_size(ks, default = None):
635 for p in ks.handler.partition.partitions:
636 if p.mountpoint == "/" and p.size:
639 return int(__size) * 1024L * 1024L
643 def get_image_fstype(ks, default = None):
644 for p in ks.handler.partition.partitions:
645 if p.mountpoint == "/" and p.fstype:
649 def get_image_fsopts(ks, default = None):
650 for p in ks.handler.partition.partitions:
651 if p.mountpoint == "/" and p.fsopts:
657 if isinstance(ks.handler.device, kscommands.device.FC3_Device):
658 devices.append(ks.handler.device)
660 devices.extend(ks.handler.device.deviceList)
663 for device in devices:
664 if not device.moduleName:
666 modules.extend(device.moduleName.split(":"))
670 def get_timeout(ks, default = None):
671 if not hasattr(ks.handler.bootloader, "timeout"):
673 if ks.handler.bootloader.timeout is None:
675 return int(ks.handler.bootloader.timeout)
677 def get_kernel_args(ks, default = "ro liveimg"):
678 if not hasattr(ks.handler.bootloader, "appendLine"):
680 if ks.handler.bootloader.appendLine is None:
682 return "%s %s" %(default, ks.handler.bootloader.appendLine)
684 def get_menu_args(ks, default = ""):
685 if not hasattr(ks.handler.bootloader, "menus"):
687 if ks.handler.bootloader.menus in (None, ""):
689 return "%s" % ks.handler.bootloader.menus
691 def get_default_kernel(ks, default = None):
692 if not hasattr(ks.handler.bootloader, "default"):
694 if not ks.handler.bootloader.default:
696 return ks.handler.bootloader.default
698 def get_repos(ks, repo_urls = {}):
700 for repo in ks.handler.repo.repoList:
702 if hasattr(repo, "includepkgs"):
703 inc.extend(repo.includepkgs)
706 if hasattr(repo, "excludepkgs"):
707 exc.extend(repo.excludepkgs)
709 baseurl = repo.baseurl
710 mirrorlist = repo.mirrorlist
712 if repo.name in repo_urls:
713 baseurl = repo_urls[repo.name]
716 if repos.has_key(repo.name):
717 msger.warning("Overriding already specified repo %s" %(repo.name,))
720 if hasattr(repo, "proxy"):
722 proxy_username = None
723 if hasattr(repo, "proxy_username"):
724 proxy_username = repo.proxy_username
725 proxy_password = None
726 if hasattr(repo, "proxy_password"):
727 proxy_password = repo.proxy_password
728 if hasattr(repo, "debuginfo"):
729 debuginfo = repo.debuginfo
730 if hasattr(repo, "source"):
732 if hasattr(repo, "gpgkey"):
734 if hasattr(repo, "disable"):
735 disable = repo.disable
737 if hasattr(repo, "ssl_verify"):
738 ssl_verify = repo.ssl_verify == "yes"
740 if hasattr(repo, "cost"):
743 if hasattr(repo, "priority"):
744 priority = repo.priority
746 repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc,
747 proxy, proxy_username, proxy_password, debuginfo,
748 source, gpgkey, disable, ssl_verify, cost, priority)
750 return repos.values()
752 def convert_method_to_repo(ks):
754 ks.handler.repo.methodToRepo()
755 except (AttributeError, kserrors.KickstartError):
758 def get_pre_packages(ks, required = []):
759 return ks.handler.prepackages.packageList + required
761 def get_packages(ks, required = []):
762 return ks.handler.packages.packageList + required
764 def get_groups(ks, required = []):
765 return ks.handler.packages.groupList + required
767 def get_excluded(ks, required = []):
768 return ks.handler.packages.excludedList + required
770 def get_partitions(ks, required = []):
771 return ks.handler.partition.partitions
773 def ignore_missing(ks):
774 return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
776 def exclude_docs(ks):
777 return ks.handler.packages.excludeDocs
780 if hasattr(ks.handler.packages, "instLange"):
781 return ks.handler.packages.instLange
782 elif hasattr(ks.handler.packages, "instLangs"):
783 return ks.handler.packages.instLangs
786 def get_post_scripts(ks):
788 for s in ks.handler.scripts:
789 if s.type != ksparser.KS_SCRIPT_POST:
794 def add_repo(ks, repostr):
795 args = repostr.split()
796 repoobj = ks.handler.repo.parse(args[1:])
797 if repoobj and repoobj not in ks.handler.repo.repoList:
798 ks.handler.repo.repoList.append(repoobj)
800 def remove_all_repos(ks):
801 while len(ks.handler.repo.repoList) != 0:
802 del ks.handler.repo.repoList[0]
804 def remove_duplicate_repos(ks):
808 if len(ks.handler.repo.repoList) < 2:
810 if i >= len(ks.handler.repo.repoList) - 1:
812 name = ks.handler.repo.repoList[i].name
813 baseurl = ks.handler.repo.repoList[i].baseurl
814 if j < len(ks.handler.repo.repoList):
815 if (ks.handler.repo.repoList[j].name == name or \
816 ks.handler.repo.repoList[j].baseurl == baseurl):
817 del ks.handler.repo.repoList[j]
820 if j >= len(ks.handler.repo.repoList):
827 def resolve_groups(creatoropts, repometadata):
829 if 'zypp' == creatoropts['pkgmgr']:
831 ks = creatoropts['ks']
833 for repo in repometadata:
834 """ Mustn't replace group with package list if repo is ready for the
835 corresponding package manager.
838 if iszypp and repo["patterns"]:
840 if not iszypp and repo["comps"]:
843 # But we also must handle such cases, use zypp but repo only has comps,
844 # use yum but repo only has patterns, use zypp but use_comps is true,
845 # use yum but use_comps is false.
847 if iszypp and repo["comps"]:
848 groupfile = repo["comps"]
849 get_pkglist_handler = misc.get_pkglist_in_comps
850 if not iszypp and repo["patterns"]:
851 groupfile = repo["patterns"]
852 get_pkglist_handler = misc.get_pkglist_in_patterns
857 if i >= len(ks.handler.packages.groupList):
859 pkglist = get_pkglist_handler(
860 ks.handler.packages.groupList[i].name,
863 del ks.handler.packages.groupList[i]
865 if pkg not in ks.handler.packages.packageList:
866 ks.handler.packages.packageList.append(pkg)