source: yum/trunk/cli.py@ 1569

Last change on this file since 1569 was 516, checked in by Yuri Dario, 11 years ago

yum: update trunk to 3.4.3.

  • Property svn:eol-style set to native
File size: 72.0 KB
Line 
1#!/usr/bin/python -t
2# This program is free software; you can redistribute it and/or modify
3# it under the terms of the GNU General Public License as published by
4# the Free Software Foundation; either version 2 of the License, or
5# (at your option) any later version.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU Library General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License
13# along with this program; if not, write to the Free Software
14# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15# Copyright 2005 Duke University
16# Written by Seth Vidal
17
18"""
19Command line interface yum class and related.
20"""
21
22import os
23import re
24import sys
25import time
26import random
27import logging
28from optparse import OptionParser,OptionGroup
29import rpm
30
31from weakref import proxy as weakref
32
33import output
34import shell
35import yum
36import yum.Errors
37import yum.logginglevels
38import yum.misc
39import yum.plugins
40from rpmUtils.arch import isMultiLibArch
41from yum import _, P_
42from yum.rpmtrans import RPMTransaction
43import signal
44import yumcommands
45
46from yum.i18n import to_unicode, to_utf8
47
48# This is for yum-utils/yumdownloader in RHEL-5, where it isn't importing this
49# directly but did do "from cli import *", and we did have this in 3.2.22. I
50# just _love_ how python re-exports these by default.
51from yum.packages import parsePackages
52
53def sigquit(signum, frame):
54 """ SIGQUIT handler for the yum cli. """
55 print >> sys.stderr, "Quit signal sent - exiting immediately"
56 sys.exit(1)
57
58class CliError(yum.Errors.YumBaseError):
59
60 """
61 Command line interface related Exception.
62 """
63
64 def __init__(self, args=''):
65 yum.Errors.YumBaseError.__init__(self)
66 self.args = args
67
68class YumBaseCli(yum.YumBase, output.YumOutput):
69 """This is the base class for yum cli.
70 Inherits from yum.YumBase and output.YumOutput """
71
72 def __init__(self):
73 # handle sigquit early on
74 signal.signal(signal.SIGQUIT, sigquit)
75 yum.YumBase.__init__(self)
76 output.YumOutput.__init__(self)
77 logging.basicConfig()
78 self.logger = logging.getLogger("yum.cli")
79 self.verbose_logger = logging.getLogger("yum.verbose.cli")
80 self.yum_cli_commands = {}
81 self.use_txmbr_in_callback = True
82 self.registerCommand(yumcommands.InstallCommand())
83 self.registerCommand(yumcommands.UpdateCommand())
84 self.registerCommand(yumcommands.InfoCommand())
85 self.registerCommand(yumcommands.ListCommand())
86 self.registerCommand(yumcommands.EraseCommand())
87 self.registerCommand(yumcommands.GroupsCommand())
88 self.registerCommand(yumcommands.MakeCacheCommand())
89 self.registerCommand(yumcommands.CleanCommand())
90 self.registerCommand(yumcommands.ProvidesCommand())
91 self.registerCommand(yumcommands.CheckUpdateCommand())
92 self.registerCommand(yumcommands.SearchCommand())
93 self.registerCommand(yumcommands.UpgradeCommand())
94 self.registerCommand(yumcommands.LocalInstallCommand())
95 self.registerCommand(yumcommands.ResolveDepCommand())
96 self.registerCommand(yumcommands.ShellCommand())
97 self.registerCommand(yumcommands.DepListCommand())
98 self.registerCommand(yumcommands.RepoListCommand())
99 self.registerCommand(yumcommands.HelpCommand())
100 self.registerCommand(yumcommands.ReInstallCommand())
101 self.registerCommand(yumcommands.DowngradeCommand())
102 self.registerCommand(yumcommands.VersionCommand())
103 self.registerCommand(yumcommands.HistoryCommand())
104 self.registerCommand(yumcommands.CheckRpmdbCommand())
105 self.registerCommand(yumcommands.DistroSyncCommand())
106 self.registerCommand(yumcommands.LoadTransactionCommand())
107
108 def registerCommand(self, command):
109 for name in command.getNames():
110 if name in self.yum_cli_commands:
111 raise yum.Errors.ConfigError(_('Command "%s" already defined') % name)
112 self.yum_cli_commands[name] = command
113
114 def doRepoSetup(self, thisrepo=None, dosack=1):
115 """grabs the repomd.xml for each enabled repository
116 and sets up the basics of the repository"""
117
118 if self._repos and thisrepo is None:
119 return self._repos
120
121 if not thisrepo:
122 self.verbose_logger.log(yum.logginglevels.INFO_2,
123 _('Setting up repositories'))
124
125 # Call parent class to do the bulk of work
126 # (this also ensures that reposetup plugin hook is called)
127 if thisrepo:
128 yum.YumBase._getRepos(self, thisrepo=thisrepo, doSetup=True)
129 else:
130 yum.YumBase._getRepos(self, thisrepo=thisrepo)
131
132 if dosack: # so we can make the dirs and grab the repomd.xml but not import the md
133 self.verbose_logger.log(yum.logginglevels.INFO_2,
134 _('Reading repository metadata in from local files'))
135 self._getSacks(thisrepo=thisrepo)
136
137 return self._repos
138
139 def _makeUsage(self):
140 """
141 Format an attractive usage string for yum, listing subcommand
142 names and summary usages.
143 """
144 usage = 'yum [options] COMMAND\n\nList of Commands:\n\n'
145 commands = yum.misc.unique([x for x in self.yum_cli_commands.values()
146 if not (hasattr(x, 'hidden') and x.hidden)])
147 commands.sort(key=lambda x: x.getNames()[0])
148 for command in commands:
149 # XXX Remove this when getSummary is common in plugins
150 try:
151 summary = command.getSummary()
152 usage += "%-14s %s\n" % (command.getNames()[0], summary)
153 except (AttributeError, NotImplementedError):
154 usage += "%s\n" % command.getNames()[0]
155
156 return usage
157
158 def _parseSetOpts(self, setopts):
159 """parse the setopts list handed to us and saves the results as
160 repo_setopts and main_setopts in the yumbase object"""
161
162 repoopts = {}
163 mainopts = yum.misc.GenericHolder()
164 mainopts.items = []
165
166 for item in setopts:
167 k,v = item.split('=')
168 period = k.find('.')
169 if period != -1:
170 repo = k[:period]
171 k = k[period+1:]
172 if repo not in repoopts:
173 repoopts[repo] = yum.misc.GenericHolder()
174 repoopts[repo].items = []
175 setattr(repoopts[repo], k, v)
176 repoopts[repo].items.append(k)
177 else:
178 setattr(mainopts, k, v)
179 mainopts.items.append(k)
180
181 self.main_setopts = mainopts
182 self.repo_setopts = repoopts
183
184
185 def getOptionsConfig(self, args):
186 """parses command line arguments, takes cli args:
187 sets up self.conf and self.cmds as well as logger objects
188 in base instance"""
189
190 self.optparser = YumOptionParser(base=self, usage=self._makeUsage())
191
192 # Parse only command line options that affect basic yum setup
193 opts = self.optparser.firstParse(args)
194
195 # Just print out the version if that's what the user wanted
196 if opts.version:
197 print yum.__version__
198 opts.quiet = True
199 opts.verbose = False
200
201 # go through all the setopts and set the global ones
202 self._parseSetOpts(opts.setopts)
203
204 if self.main_setopts:
205 for opt in self.main_setopts.items:
206 setattr(opts, opt, getattr(self.main_setopts, opt))
207
208 # get the install root to use
209 root = self.optparser.getRoot(opts)
210
211 if opts.quiet:
212 opts.debuglevel = 0
213 if opts.verbose:
214 opts.debuglevel = opts.errorlevel = 6
215
216 # Read up configuration options and initialise plugins
217 try:
218 pc = self.preconf
219 pc.fn = opts.conffile
220 pc.root = root
221 pc.init_plugins = not opts.noplugins
222 pc.plugin_types = (yum.plugins.TYPE_CORE,
223 yum.plugins.TYPE_INTERACTIVE)
224 pc.optparser = self.optparser
225 pc.debuglevel = opts.debuglevel
226 pc.errorlevel = opts.errorlevel
227 pc.disabled_plugins = self.optparser._splitArg(opts.disableplugins)
228 pc.enabled_plugins = self.optparser._splitArg(opts.enableplugins)
229 pc.releasever = opts.releasever
230 self.conf
231
232 # now set all the non-first-start opts from main from our setopts
233 if self.main_setopts:
234 for opt in self.main_setopts.items:
235 if not hasattr(self.conf, opt):
236 msg ="Main config did not have a %s attr. before setopt"
237 self.logger.warning(msg % opt)
238 setattr(self.conf, opt, getattr(self.main_setopts, opt))
239
240 except yum.Errors.ConfigError, e:
241 self.logger.critical(_('Config Error: %s'), e)
242 sys.exit(1)
243 except ValueError, e:
244 self.logger.critical(_('Options Error: %s'), e)
245 sys.exit(1)
246
247 # update usage in case plugins have added commands
248 self.optparser.set_usage(self._makeUsage())
249
250 self.plugins.run('args', args=args)
251 # Now parse the command line for real and
252 # apply some of the options to self.conf
253 (opts, self.cmds) = self.optparser.setupYumConfig(args=args)
254
255 if opts.version:
256 opts.quiet = True
257 opts.verbose = False
258
259 # Check that firstParse didn't miss anything, and warn the user if it
260 # did ... because this is really magic, and unexpected.
261 if opts.quiet:
262 opts.debuglevel = 0
263 if opts.verbose:
264 opts.debuglevel = opts.errorlevel = 6
265 if opts.debuglevel != pc.debuglevel or opts.errorlevel != pc.errorlevel:
266 self.logger.warning("Ignored option -q, -v, -d or -e (probably due to merging: -yq != -y -q)")
267 # getRoot() changes it, but then setupYumConfig() changes it back. So
268 # don't test for this, if we are using --installroot.
269 if root == '/' and opts.conffile != pc.fn:
270 self.logger.warning("Ignored option -c (probably due to merging -yc != -y -c)")
271
272 if opts.version:
273 self.conf.cache = 1
274 yum_progs = self.run_with_package_names
275 done = False
276 def sm_ui_time(x):
277 return time.strftime("%Y-%m-%d %H:%M", time.gmtime(x))
278 def sm_ui_date(x): # For changelogs, there is no time
279 return time.strftime("%Y-%m-%d", time.gmtime(x))
280 for pkg in sorted(self.rpmdb.returnPackages(patterns=yum_progs)):
281 # We should only have 1 version of each...
282 if done: print ""
283 done = True
284 if pkg.epoch == '0':
285 ver = '%s-%s.%s' % (pkg.version, pkg.release, pkg.arch)
286 else:
287 ver = '%s:%s-%s.%s' % (pkg.epoch,
288 pkg.version, pkg.release, pkg.arch)
289 name = "%s%s%s" % (self.term.MODE['bold'], pkg.name,
290 self.term.MODE['normal'])
291 print _(" Installed: %s-%s at %s") %(name, ver,
292 sm_ui_time(pkg.installtime))
293 print _(" Built : %s at %s") % (pkg.packager,
294 sm_ui_time(pkg.buildtime))
295 print _(" Committed: %s at %s") % (pkg.committer,
296 sm_ui_date(pkg.committime))
297 sys.exit(0)
298
299 if opts.sleeptime is not None:
300 sleeptime = random.randrange(opts.sleeptime*60)
301 else:
302 sleeptime = 0
303
304 # save our original args out
305 self.args = args
306 # save out as a nice command string
307 self.cmdstring = 'yum '
308 for arg in self.args:
309 self.cmdstring += '%s ' % arg
310
311 try:
312 self.parseCommands() # before we return check over the base command + args
313 # make sure they match/make sense
314 except CliError:
315 sys.exit(1)
316
317 # run the sleep - if it's unchanged then it won't matter
318 time.sleep(sleeptime)
319
320 def parseCommands(self):
321 """reads self.cmds and parses them out to make sure that the requested
322 base command + argument makes any sense at all"""
323
324 self.verbose_logger.debug('Yum Version: %s', yum.__version__)
325 self.verbose_logger.log(yum.logginglevels.DEBUG_4,
326 'COMMAND: %s', self.cmdstring)
327 self.verbose_logger.log(yum.logginglevels.DEBUG_4,
328 'Installroot: %s', self.conf.installroot)
329 if len(self.conf.commands) == 0 and len(self.cmds) < 1:
330 self.cmds = self.conf.commands
331 else:
332 self.conf.commands = self.cmds
333 if len(self.cmds) < 1:
334 self.logger.critical(_('You need to give some command'))
335 self.usage()
336 raise CliError
337
338 self.basecmd = self.cmds[0] # our base command
339 self.extcmds = self.cmds[1:] # out extended arguments/commands
340
341 if len(self.extcmds) > 0:
342 self.verbose_logger.log(yum.logginglevels.DEBUG_4,
343 'Ext Commands:\n')
344 for arg in self.extcmds:
345 self.verbose_logger.log(yum.logginglevels.DEBUG_4, ' %s', arg)
346
347 if self.basecmd not in self.yum_cli_commands:
348 self.logger.critical(_('No such command: %s. Please use %s --help'),
349 self.basecmd, sys.argv[0])
350 raise CliError
351
352 self.yum_cli_commands[self.basecmd].doCheck(self, self.basecmd, self.extcmds)
353
354 def _shell_history_write(self):
355 if not hasattr(self, '_shell_history_cmds'):
356 return
357 if not self._shell_history_cmds:
358 return
359
360 data = self._shell_history_cmds
361 # Turn: [["a", "b"], ["c", "d"]] => "a b\nc d\n"
362 data = [" ".join(cmds) for cmds in data]
363 data.append('')
364 data = "\n".join(data)
365 self.history.write_addon_data('shell-cmds', data)
366
367 def doShell(self):
368 """do a shell-like interface for yum commands"""
369
370 yumshell = shell.YumShell(base=self)
371
372 # We share this array...
373 self._shell_history_cmds = yumshell._shell_history_cmds
374
375 if len(self.extcmds) == 0:
376 yumshell.cmdloop()
377 else:
378 yumshell.script()
379
380 del self._shell_history_cmds
381
382 return yumshell.result, yumshell.resultmsgs
383
384 def errorSummary(self, errstring):
385 """ parse the error string for 'interesting' errors which can
386 be grouped, such as disk space issues """
387 summary = ''
388 # do disk space report first
389 p = re.compile('needs (\d+)MB on the (\S+) filesystem')
390 disk = {}
391 for m in p.finditer(errstring):
392 if m.group(2) not in disk:
393 disk[m.group(2)] = int(m.group(1))
394 if disk[m.group(2)] < int(m.group(1)):
395 disk[m.group(2)] = int(m.group(1))
396
397 if disk:
398 summary += _('Disk Requirements:\n')
399 for k in disk:
400 summary += P_(' At least %dMB more space needed on the %s filesystem.\n', ' At least %dMB more space needed on the %s filesystem.\n', disk[k]) % (disk[k], k)
401
402 # TODO: simplify the dependency errors?
403
404 # Fixup the summary
405 summary = _('Error Summary\n-------------\n') + summary
406
407 return summary
408
409
410 def doCommands(self):
411 """
412 Calls the base command passes the extended commands/args out to be
413 parsed (most notably package globs).
414
415 Returns a numeric result code and an optional string
416 - 0 = we're done, exit
417 - 1 = we've errored, exit with error string
418 - 2 = we've got work yet to do, onto the next stage
419 """
420
421 # at this point we know the args are valid - we don't know their meaning
422 # but we know we're not being sent garbage
423
424 # setup our transaction set if the command we're using needs it
425 # compat with odd modules not subclassing YumCommand
426 needTs = True
427 needTsRemove = False
428 cmd = self.yum_cli_commands[self.basecmd]
429 if hasattr(cmd, 'needTs'):
430 needTs = cmd.needTs(self, self.basecmd, self.extcmds)
431 if not needTs and hasattr(cmd, 'needTsRemove'):
432 needTsRemove = cmd.needTsRemove(self, self.basecmd, self.extcmds)
433
434 if needTs or needTsRemove:
435 try:
436 self._getTs(needTsRemove)
437 except yum.Errors.YumBaseError, e:
438 return 1, [str(e)]
439
440 return self.yum_cli_commands[self.basecmd].doCommand(self, self.basecmd, self.extcmds)
441
442 def doTransaction(self):
443 """takes care of package downloading, checking, user confirmation and actually
444 RUNNING the transaction"""
445
446 # just make sure there's not, well, nothing to do
447 if len(self.tsInfo) == 0:
448 self.verbose_logger.info(_('Trying to run the transaction but nothing to do. Exiting.'))
449 return -1
450
451 # NOTE: In theory we can skip this in -q -y mode, for a slight perf.
452 # gain. But it's probably doom to have a different code path.
453 lsts = self.listTransaction()
454 if self.verbose_logger.isEnabledFor(yum.logginglevels.INFO_1):
455 self.verbose_logger.log(yum.logginglevels.INFO_1, lsts)
456 elif not self.conf.assumeyes:
457 # If we are in quiet, and assumeyes isn't on we want to output
458 # at least the transaction list anyway.
459 self.logger.warn(lsts)
460
461 # Check which packages have to be downloaded
462 downloadpkgs = []
463 rmpkgs = []
464 stuff_to_download = False
465 install_only = True
466 remove_only = True
467 for txmbr in self.tsInfo.getMembers():
468 if txmbr.ts_state not in ('i', 'u'):
469 install_only = False
470 po = txmbr.po
471 if po:
472 rmpkgs.append(po)
473 else:
474 remove_only = False
475 stuff_to_download = True
476 po = txmbr.po
477 if po:
478 downloadpkgs.append(po)
479
480 # Close the connection to the rpmdb so that rpm doesn't hold the SIGINT
481 # handler during the downloads. self.ts is reinitialised later in this
482 # function anyway (initActionTs).
483 self.ts.close()
484
485 # Report the total download size to the user, so he/she can base
486 # the answer on this info
487 if not stuff_to_download:
488 self.reportRemoveSize(rmpkgs)
489 else:
490 self.reportDownloadSize(downloadpkgs, install_only)
491
492 # confirm with user
493 if self._promptWanted():
494 if not self.userconfirm():
495 self.verbose_logger.info(_('Exiting on user Command'))
496 return -1
497
498 self.verbose_logger.log(yum.logginglevels.INFO_2,
499 _('Downloading Packages:'))
500 problems = self.downloadPkgs(downloadpkgs, callback_total=self.download_callback_total_cb)
501
502 if len(problems) > 0:
503 errstring = ''
504 errstring += _('Error Downloading Packages:\n')
505 for key in problems:
506 errors = yum.misc.unique(problems[key])
507 for error in errors:
508 errstring += ' %s: %s\n' % (key, error)
509 raise yum.Errors.YumBaseError, errstring
510
511 # Check GPG signatures
512 if self.gpgsigcheck(downloadpkgs) != 0:
513 return -1
514
515 self.initActionTs()
516 # save our dsCallback out
517 dscb = self.dsCallback
518 self.dsCallback = None # dumb, dumb dumb dumb!
519 self.populateTs(keepold=0) # sigh
520
521 rcd_st = time.time()
522 self.verbose_logger.log(yum.logginglevels.INFO_2,
523 _('Running Transaction Check'))
524 msgs = self._run_rpm_check()
525 if msgs:
526 rpmlib_only = True
527 for msg in msgs:
528 if msg.startswith('rpmlib('):
529 continue
530 rpmlib_only = False
531 if rpmlib_only:
532 print _("ERROR You need to update rpm to handle:")
533 else:
534 print _('ERROR with transaction check vs depsolve:')
535
536 for msg in msgs:
537 print to_utf8(msg)
538
539 if rpmlib_only:
540 return 1, [_('RPM needs to be updated')]
541 return 1, [_('Please report this error in %s') % self.conf.bugtracker_url]
542
543 self.verbose_logger.debug('Transaction Check time: %0.3f' % (time.time() - rcd_st))
544
545 tt_st = time.time()
546 self.verbose_logger.log(yum.logginglevels.INFO_2,
547 _('Running Transaction Test'))
548 if not self.conf.diskspacecheck:
549 self.tsInfo.probFilterFlags.append(rpm.RPMPROB_FILTER_DISKSPACE)
550
551 self.ts.order() # order the transaction
552 self.ts.clean() # release memory not needed beyond this point
553
554 testcb = RPMTransaction(self, test=True)
555 tserrors = self.ts.test(testcb)
556 del testcb
557
558 if len(tserrors) > 0:
559 errstring = _('Transaction Check Error:\n')
560 for descr in tserrors:
561 errstring += ' %s\n' % to_unicode(descr)
562
563 raise yum.Errors.YumBaseError, errstring + '\n' + \
564 self.errorSummary(errstring)
565 self.verbose_logger.log(yum.logginglevels.INFO_2,
566 _('Transaction Test Succeeded'))
567
568 self.verbose_logger.debug('Transaction Test time: %0.3f' % (time.time() - tt_st))
569
570 # unset the sigquit handler
571 signal.signal(signal.SIGQUIT, signal.SIG_DFL)
572
573 ts_st = time.time()
574
575 # Reinstalls broke in: 7115478c527415cb3c8317456cdf50024de89a94 ...
576 # I assume there's a "better" fix, but this fixes reinstalls and lets
577 # other options continue as is (and they seem to work).
578 have_reinstalls = False
579 for txmbr in self.tsInfo.getMembers():
580 if txmbr.reinstall:
581 have_reinstalls = True
582 break
583 if have_reinstalls:
584 self.initActionTs() # make a new, blank ts to populate
585 self.populateTs(keepold=0) # populate the ts
586 self.ts.check() #required for ordering
587 self.ts.order() # order
588 self.ts.clean() # release memory not needed beyond this point
589
590 # put back our depcheck callback
591 self.dsCallback = dscb
592 # setup our rpm ts callback
593 cb = RPMTransaction(self,
594 display=output.YumCliRPMCallBack(weakref(self)))
595 if self.conf.debuglevel < 2:
596 cb.display.output = False
597
598 self.verbose_logger.log(yum.logginglevels.INFO_2, _('Running Transaction'))
599 resultobject = self.runTransaction(cb=cb)
600
601 self.verbose_logger.debug('Transaction time: %0.3f' % (time.time() - ts_st))
602 # close things
603 self.verbose_logger.log(yum.logginglevels.INFO_1,
604 self.postTransactionOutput())
605
606 # put back the sigquit handler
607 signal.signal(signal.SIGQUIT, sigquit)
608
609 return resultobject.return_code
610
611 def gpgsigcheck(self, pkgs):
612 '''Perform GPG signature verification on the given packages, installing
613 keys if possible
614
615 Returns non-zero if execution should stop (user abort).
616 Will raise YumBaseError if there's a problem
617 '''
618 for po in pkgs:
619 result, errmsg = self.sigCheckPkg(po)
620
621 if result == 0:
622 # Verified ok, or verify not req'd
623 continue
624
625 elif result == 1:
626 if not sys.stdin.isatty() and not self.conf.assumeyes:
627 raise yum.Errors.YumBaseError, \
628 _('Refusing to automatically import keys when running ' \
629 'unattended.\nUse "-y" to override.')
630
631 # the callback here expects to be able to take options which
632 # userconfirm really doesn't... so fake it
633 self.getKeyForPackage(po, lambda x, y, z: self.userconfirm())
634
635 else:
636 # Fatal error
637 raise yum.Errors.YumBaseError, errmsg
638
639 return 0
640
641 def _maybeYouMeant(self, arg):
642 """ If install argument doesn't match with case, tell the user. """
643 matches = self.doPackageLists(patterns=[arg], ignore_case=True)
644 matches = matches.installed + matches.available
645 matches = set(map(lambda x: x.name, matches))
646 if matches:
647 msg = self.fmtKeyValFill(_(' * Maybe you meant: '),
648 ", ".join(matches))
649 self.verbose_logger.log(yum.logginglevels.INFO_2, to_unicode(msg))
650
651 def _checkMaybeYouMeant(self, arg, always_output=True, rpmdb_only=False):
652 """ If the update/remove argument doesn't match with case, or due
653 to not being installed, tell the user. """
654 # always_output is a wart due to update/remove not producing the
655 # same output.
656 # if it is a grouppattern then none of this is going to make any sense
657 # skip it.
658 if not arg or arg[0] == '@':
659 return
660
661 pkgnarrow='all'
662 if rpmdb_only:
663 pkgnarrow='installed'
664
665 matches = self.doPackageLists(pkgnarrow=pkgnarrow, patterns=[arg], ignore_case=False)
666 if (matches.installed or (not matches.available and
667 self.returnInstalledPackagesByDep(arg))):
668 return # Found a match so ignore
669 hibeg = self.term.MODE['bold']
670 hiend = self.term.MODE['normal']
671 if matches.available:
672 self.verbose_logger.log(yum.logginglevels.INFO_2,
673 _('Package(s) %s%s%s available, but not installed.'),
674 hibeg, arg, hiend)
675 return
676
677 # No package name, so do the maybeYouMeant thing here too
678 matches = self.doPackageLists(pkgnarrow=pkgnarrow, patterns=[arg], ignore_case=True)
679 if not matches.installed and matches.available:
680 self.verbose_logger.log(yum.logginglevels.INFO_2,
681 _('Package(s) %s%s%s available, but not installed.'),
682 hibeg, arg, hiend)
683 return
684 matches = set(map(lambda x: x.name, matches.installed))
685 if always_output or matches:
686 self.verbose_logger.log(yum.logginglevels.INFO_2,
687 _('No package %s%s%s available.'),
688 hibeg, arg, hiend)
689 if matches:
690 msg = self.fmtKeyValFill(_(' * Maybe you meant: '),
691 ", ".join(matches))
692 self.verbose_logger.log(yum.logginglevels.INFO_2, msg)
693
694 def installPkgs(self, userlist):
695 """Attempts to take the user specified list of packages/wildcards
696 and install them, or if they are installed, update them to a newer
697 version. If a complete version number if specified, attempt to
698 upgrade (or downgrade if they have been removed) them to the
699 specified version"""
700 # get the list of available packages
701 # iterate over the user's list
702 # add packages to Transaction holding class if they match.
703 # if we've added any packages to the transaction then return 2 and a string
704 # if we've hit a snag, return 1 and the failure explanation
705 # if we've got nothing to do, return 0 and a 'nothing available to install' string
706
707 oldcount = len(self.tsInfo)
708
709 done = False
710 for arg in userlist:
711 if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
712 os.path.exists(arg))):
713 self.localInstall(filelist=[arg])
714 continue # it was something on disk and it ended in rpm
715 # no matter what we don't go looking at repos
716 try:
717 self.install(pattern=arg)
718 except yum.Errors.InstallError:
719 self.verbose_logger.log(yum.logginglevels.INFO_2,
720 _('No package %s%s%s available.'),
721 self.term.MODE['bold'], arg,
722 self.term.MODE['normal'])
723 self._maybeYouMeant(arg)
724 else:
725 done = True
726 if len(self.tsInfo) > oldcount:
727 change = len(self.tsInfo) - oldcount
728 return 2, [P_('%d package to install', '%d packages to install', change) % change]
729
730 if not done:
731 return 1, [_('Nothing to do')]
732 return 0, [_('Nothing to do')]
733
734 def updatePkgs(self, userlist, quiet=0, update_to=False):
735 """take user commands and populate transaction wrapper with
736 packages to be updated"""
737
738 # if there is no userlist, then do global update below
739 # this is probably 90% of the calls
740 # if there is a userlist then it's for updating pkgs, not obsoleting
741
742 oldcount = len(self.tsInfo)
743 if len(userlist) == 0: # simple case - do them all
744 self.update()
745
746 else:
747 # go through the userlist - look for items that are local rpms. If we find them
748 # pass them off to localInstall() and then move on
749 localupdates = []
750 for item in userlist:
751 if (item.endswith('.rpm') and (yum.misc.re_remote_url(item) or
752 os.path.exists(item))):
753 localupdates.append(item)
754
755 if len(localupdates) > 0:
756 self.localInstall(filelist=localupdates, updateonly=1)
757 for item in localupdates:
758 userlist.remove(item)
759
760 for arg in userlist:
761 if not self.update(pattern=arg, update_to=update_to):
762 self._checkMaybeYouMeant(arg)
763
764 if len(self.tsInfo) > oldcount:
765 change = len(self.tsInfo) - oldcount
766 return 2, [P_('%d package marked for Update', '%d packages marked for Update', change) % change]
767 else:
768 return 0, [_('No Packages marked for Update')]
769
770 # Note that we aren't in __init__ yet for a couple of reasons, but we
771 # probably will get there for 3.2.28.
772 def distroSyncPkgs(self, userlist):
773 """ This does either upgrade/downgrade, depending on if the latest
774 installed version is older or newer. We allow "selection" but not
775 local packages (use tmprepo, or something). """
776
777 level = 'diff'
778 if userlist and userlist[0] in ('full', 'diff', 'different'):
779 level = userlist[0]
780 userlist = userlist[1:]
781 if level == 'different':
782 level = 'diff'
783
784 dupdates = []
785 ipkgs = {}
786 for pkg in sorted(self.rpmdb.returnPackages(patterns=userlist)):
787 ipkgs[pkg.name] = pkg
788
789 obsoletes = []
790 if self.conf.obsoletes:
791 obsoletes = self.up.getObsoletesTuples(newest=1)
792
793 for (obsoleting, installed) in obsoletes:
794 if installed[0] not in ipkgs:
795 continue
796 dupdates.extend(self.update(pkgtup=installed))
797 for (obsoleting, installed) in obsoletes:
798 if installed[0] not in ipkgs:
799 continue
800 del ipkgs[installed[0]]
801
802 apkgs = {}
803 pkgs = []
804 if ipkgs:
805 try:
806 pkgs = self.pkgSack.returnNewestByName(patterns=ipkgs.keys())
807 except yum.Errors.PackageSackError:
808 pkgs = []
809
810 for pkg in pkgs:
811 if pkg.name not in ipkgs:
812 continue
813 apkgs[pkg.name] = pkg
814
815 for ipkgname in ipkgs:
816 if ipkgname not in apkgs:
817 continue
818
819 ipkg = ipkgs[ipkgname]
820 apkg = apkgs[ipkgname]
821 if ipkg.verEQ(apkg): # Latest installed == Latest avail.
822 if level == 'diff':
823 continue
824
825 # level == full: do reinstalls if checksum doesn't match.
826 # do removals, if older installed versions.
827 for napkg in self.rpmdb.searchNames([ipkgname]):
828 if (not self.allowedMultipleInstalls(apkg) and
829 not napkg.verEQ(ipkg)):
830 dupdates.extend(self.remove(po=napkg))
831 continue
832
833 nayi = napkg.yumdb_info
834 for apkg in self.pkgSack.searchPkgTuple(napkg.pkgtup):
835 if ('checksum_type' in nayi and
836 'checksum_data' in nayi and
837 nayi.checksum_type == apkg.checksum_type and
838 nayi.checksum_data == apkg.pkgId):
839 found = True
840 break
841 if found:
842 continue
843 dupdates.extend(self.reinstall(pkgtup=napkg.pkgtup))
844 continue
845
846 if self.allowedMultipleInstalls(apkg):
847 found = False
848 for napkg in self.rpmdb.searchNames([apkg.name]):
849 if napkg.verEQ(apkg):
850 found = True
851 elif napkg.verGT(apkg):
852 dupdates.extend(self.remove(po=napkg))
853 if found:
854 continue
855 dupdates.extend(self.install(pattern=apkg.name))
856 elif ipkg.verLT(apkg):
857 n,a,e,v,r = apkg.pkgtup
858 dupdates.extend(self.update(name=n, epoch=e, ver=v, rel=r))
859 else:
860 n,a,e,v,r = apkg.pkgtup
861 dupdates.extend(self.downgrade(name=n, epoch=e, ver=v, rel=r))
862
863 if dupdates:
864 return 2, [P_('%d package marked for Distribution Synchronization', '%d packages marked for Distribution Synchronization', len(dupdates)) % len(dupdates)]
865 else:
866 return 0, [_('No Packages marked for Distribution Synchronization')]
867
868 def erasePkgs(self, userlist):
869 """take user commands and populate a transaction wrapper with packages
870 to be erased/removed"""
871
872 oldcount = len(self.tsInfo)
873
874 all_rms = []
875 for arg in userlist:
876 rms = self.remove(pattern=arg)
877 if not rms:
878 self._checkMaybeYouMeant(arg, always_output=False, rpmdb_only=True)
879 all_rms.extend(rms)
880
881 if all_rms:
882 return 2, [P_('%d package marked for removal', '%d packages marked for removal', len(all_rms)) % len(all_rms)]
883 else:
884 return 0, [_('No Packages marked for removal')]
885
886 def downgradePkgs(self, userlist):
887 """Attempts to take the user specified list of packages/wildcards
888 and downgrade them. If a complete version number if specified,
889 attempt to downgrade them to the specified version"""
890
891 oldcount = len(self.tsInfo)
892
893 for arg in userlist:
894 if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
895 os.path.exists(arg))):
896 self.downgradeLocal(arg)
897 continue # it was something on disk and it ended in rpm
898 # no matter what we don't go looking at repos
899
900 try:
901 self.downgrade(pattern=arg)
902 except yum.Errors.DowngradeError:
903 self.verbose_logger.log(yum.logginglevels.INFO_2,
904 _('No package %s%s%s available.'),
905 self.term.MODE['bold'], arg,
906 self.term.MODE['normal'])
907 self._maybeYouMeant(arg)
908 if len(self.tsInfo) > oldcount:
909 change = len(self.tsInfo) - oldcount
910 return 2, [P_('%d package to downgrade', '%d packages to downgrade', change) % change]
911 return 0, [_('Nothing to do')]
912
913 def reinstallPkgs(self, userlist):
914 """Attempts to take the user specified list of packages/wildcards
915 and reinstall them. """
916
917 oldcount = len(self.tsInfo)
918
919 for arg in userlist:
920 if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
921 os.path.exists(arg))):
922 self.reinstallLocal(arg)
923 continue # it was something on disk and it ended in rpm
924 # no matter what we don't go looking at repos
925
926 try:
927 self.reinstall(pattern=arg)
928 except yum.Errors.ReinstallRemoveError:
929 self._checkMaybeYouMeant(arg, always_output=False)
930 except yum.Errors.ReinstallInstallError, e:
931 for ipkg in e.failed_pkgs:
932 xmsg = ''
933 if 'from_repo' in ipkg.yumdb_info:
934 xmsg = ipkg.yumdb_info.from_repo
935 xmsg = _(' (from %s)') % xmsg
936 msg = _('Installed package %s%s%s%s not available.')
937 self.verbose_logger.log(yum.logginglevels.INFO_2, msg,
938 self.term.MODE['bold'], ipkg,
939 self.term.MODE['normal'], xmsg)
940 except yum.Errors.ReinstallError, e:
941 assert False, "Shouldn't happen, but just in case"
942 self.verbose_logger.log(yum.logginglevels.INFO_2, e)
943 if len(self.tsInfo) > oldcount:
944 change = len(self.tsInfo) - oldcount
945 return 2, [P_('%d package to reinstall', '%d packages to reinstall', change) % change]
946 return 0, [_('Nothing to do')]
947
948 def localInstall(self, filelist, updateonly=0):
949 """handles installs/updates of rpms provided on the filesystem in a
950 local dir (ie: not from a repo)"""
951
952 # read in each package into a YumLocalPackage Object
953 # append it to self.localPackages
954 # check if it can be installed or updated based on nevra versus rpmdb
955 # don't import the repos until we absolutely need them for depsolving
956
957 if len(filelist) == 0:
958 return 0, [_('No Packages Provided')]
959
960 installing = False
961 for pkg in filelist:
962 if not pkg.endswith('.rpm'):
963 self.verbose_logger.log(yum.logginglevels.INFO_2,
964 "Skipping: %s, filename does not end in .rpm.", pkg)
965 continue
966 txmbrs = self.installLocal(pkg, updateonly=updateonly)
967 if txmbrs:
968 installing = True
969
970 if installing:
971 return 2, [_('Package(s) to install')]
972 return 0, [_('Nothing to do')]
973
974 def returnPkgLists(self, extcmds, installed_available=False):
975 """Returns packages lists based on arguments on the cli.returns a
976 GenericHolder instance with the following lists defined:
977 available = list of packageObjects
978 installed = list of packageObjects
979 updates = tuples of packageObjects (updating, installed)
980 extras = list of packageObjects
981 obsoletes = tuples of packageObjects (obsoleting, installed)
982 recent = list of packageObjects
983
984 installed_available = that the available package list is present
985 as .hidden_available when doing any of:
986 all/available/installed
987 """
988
989 special = ['available', 'installed', 'all', 'extras', 'updates', 'recent',
990 'obsoletes']
991
992 pkgnarrow = 'all'
993 done_hidden_available = False
994 done_hidden_installed = False
995 if len(extcmds) > 0:
996 if installed_available and extcmds[0] == 'installed':
997 done_hidden_available = True
998 extcmds.pop(0)
999 elif installed_available and extcmds[0] == 'available':
1000 done_hidden_installed = True
1001 extcmds.pop(0)
1002 elif extcmds[0] in special:
1003 pkgnarrow = extcmds.pop(0)
1004
1005 ypl = self.doPackageLists(pkgnarrow=pkgnarrow, patterns=extcmds,
1006 ignore_case=True)
1007 if self.conf.showdupesfromrepos:
1008 ypl.available += ypl.reinstall_available
1009
1010 if installed_available:
1011 ypl.hidden_available = ypl.available
1012 ypl.hidden_installed = ypl.installed
1013 if done_hidden_available:
1014 ypl.available = []
1015 if done_hidden_installed:
1016 ypl.installed = []
1017 return ypl
1018
1019 def search(self, args):
1020 """cli wrapper method for module search function, searches simple
1021 text tags in a package object"""
1022
1023 # call the yum module search function with lists of tags to search
1024 # and what to search for
1025 # display the list of matches
1026
1027 searchlist = ['name', 'summary', 'description', 'url']
1028 dups = self.conf.showdupesfromrepos
1029 args = map(to_unicode, args)
1030
1031 okeys = set()
1032 akeys = set() # All keys, used to see if nothing matched
1033 mkeys = set() # "Main" set of keys for N/S search (biggest term. hit).
1034 pos = set()
1035
1036 def _print_match_section(text):
1037 # Print them in the order they were passed
1038 used_keys = [arg for arg in args if arg in keys]
1039 print self.fmtSection(text % ", ".join(used_keys))
1040
1041 # First try just the name/summary fields, and if we get any hits
1042 # don't do the other stuff. Unless the user overrides via. "all".
1043 if len(args) > 1 and args[0] == 'all':
1044 args.pop(0)
1045 else:
1046 matching = self.searchGenerator(['name', 'summary'], args,
1047 showdups=dups, keys=True)
1048 for (po, keys, matched_value) in matching:
1049 if keys != okeys:
1050 if akeys:
1051 if len(mkeys) == len(args):
1052 break
1053 print ""
1054 else:
1055 mkeys = set(keys)
1056 _print_match_section(_('N/S Matched: %s'))
1057 okeys = keys
1058 pos.add(po)
1059 akeys.update(keys)
1060 self.matchcallback(po, matched_value, args)
1061
1062 matching = self.searchGenerator(searchlist, args,
1063 showdups=dups, keys=True)
1064
1065 okeys = set()
1066
1067 # If we got a hit with just name/summary then we only care about hits
1068 # with _more_ search terms. Thus. if we hit all our search terms. do
1069 # nothing.
1070 if len(mkeys) == len(args):
1071 print ""
1072 if len(args) == 1:
1073 msg = _(' Name and summary matches %sonly%s, use "search all" for everything.')
1074 else:
1075 msg = _(' Full name and summary matches %sonly%s, use "search all" for everything.')
1076 print msg % (self.term.MODE['bold'], self.term.MODE['normal'])
1077 matching = []
1078
1079 for (po, keys, matched_value) in matching:
1080 # Don't print matches for "a", "b", "c" on N+S+D when we already
1081 # matched that on just N+S.
1082 if len(keys) <= len(mkeys):
1083 continue
1084 # Just print the highest level of full matches, when we did
1085 # minimal matches. Ie. "A", "B" match N+S, just print the
1086 # "A", "B", "C", "D" full match, and not the "B", "C", "D" matches.
1087 if mkeys and len(keys) < len(okeys):
1088 continue
1089
1090 if keys != okeys:
1091 if akeys:
1092 print ""
1093 _print_match_section(_('Matched: %s'))
1094 okeys = keys
1095 akeys.update(keys)
1096 self.matchcallback(po, matched_value, args)
1097
1098 if mkeys and len(mkeys) != len(args):
1099 print ""
1100 print _(' Name and summary matches %smostly%s, use "search all" for everything.') % (self.term.MODE['bold'], self.term.MODE['normal'])
1101
1102 for arg in args:
1103 if arg not in akeys:
1104 self.logger.warning(_('Warning: No matches found for: %s'), arg)
1105
1106 if not akeys:
1107 return 0, [_('No Matches found')]
1108 return 0, matching
1109
1110 def deplist(self, args):
1111 """cli wrapper method for findDeps method takes a list of packages and
1112 returns a formatted deplist for that package"""
1113
1114 pkgs = []
1115 for arg in args:
1116 if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
1117 os.path.exists(arg))):
1118 thispkg = yum.packages.YumUrlPackage(self, self.ts, arg)
1119 pkgs.append(thispkg)
1120 elif self.conf.showdupesfromrepos:
1121 pkgs.extend(self.pkgSack.returnPackages(patterns=[arg]))
1122 else:
1123 try:
1124 pkgs.extend(self.pkgSack.returnNewestByName(patterns=[arg]))
1125 except yum.Errors.PackageSackError:
1126 pass
1127
1128 results = self.findDeps(pkgs)
1129 self.depListOutput(results)
1130
1131 return 0, []
1132
1133 def provides(self, args):
1134 """use the provides methods in the rpmdb and pkgsack to produce a list
1135 of items matching the provides strings. This is a cli wrapper to the
1136 module"""
1137
1138 old_sdup = self.conf.showdupesfromrepos
1139 # For output, as searchPackageProvides() is always in showdups mode
1140 self.conf.showdupesfromrepos = True
1141 cb = self.matchcallback_verbose
1142 matching = self.searchPackageProvides(args, callback=cb,
1143 callback_has_matchfor=True)
1144 if len(matching) == 0:
1145 # Try to be a bit clever, for commands, and python modules.
1146 # Maybe want something so we can do perl/etc. too?
1147 paths = set(sys.path + os.environ['PATH'].split(':'))
1148 nargs = []
1149 for arg in args:
1150 if yum.misc.re_filename(arg) or yum.misc.re_glob(arg):
1151 continue
1152 for path in paths:
1153 if not path:
1154 continue
1155 nargs.append("%s/%s" % (path, arg))
1156 matching = self.searchPackageProvides(nargs, callback=cb,
1157 callback_has_matchfor=True)
1158 self.conf.showdupesfromrepos = old_sdup
1159
1160 if len(matching) == 0:
1161 return 0, ['No Matches found']
1162
1163 return 0, []
1164
1165 def resolveDepCli(self, args):
1166 """returns a package (one per user arg) that provide the supplied arg"""
1167
1168 for arg in args:
1169 try:
1170 pkg = self.returnPackageByDep(arg)
1171 except yum.Errors.YumBaseError:
1172 self.logger.critical(_('No Package Found for %s'), arg)
1173 else:
1174 msg = '%s:%s-%s-%s.%s' % (pkg.epoch, pkg.name, pkg.version, pkg.release, pkg.arch)
1175 self.verbose_logger.info(msg)
1176
1177 return 0, []
1178
1179 def cleanCli(self, userlist):
1180 hdrcode = pkgcode = xmlcode = dbcode = expccode = 0
1181 pkgresults = hdrresults = xmlresults = dbresults = expcresults = []
1182 msg = self.fmtKeyValFill(_('Cleaning repos: '),
1183 ' '.join([ x.id for x in self.repos.listEnabled()]))
1184 self.verbose_logger.log(yum.logginglevels.INFO_2, msg)
1185 if 'all' in userlist:
1186 self.verbose_logger.log(yum.logginglevels.INFO_2,
1187 _('Cleaning up Everything'))
1188 pkgcode, pkgresults = self.cleanPackages()
1189 hdrcode, hdrresults = self.cleanHeaders()
1190 xmlcode, xmlresults = self.cleanMetadata()
1191 dbcode, dbresults = self.cleanSqlite()
1192 rpmcode, rpmresults = self.cleanRpmDB()
1193 self.plugins.run('clean')
1194
1195 code = hdrcode + pkgcode + xmlcode + dbcode + rpmcode
1196 results = (hdrresults + pkgresults + xmlresults + dbresults +
1197 rpmresults)
1198 for msg in results:
1199 self.logger.debug(msg)
1200 return code, []
1201
1202 if 'headers' in userlist:
1203 self.logger.debug(_('Cleaning up Headers'))
1204 hdrcode, hdrresults = self.cleanHeaders()
1205 if 'packages' in userlist:
1206 self.logger.debug(_('Cleaning up Packages'))
1207 pkgcode, pkgresults = self.cleanPackages()
1208 if 'metadata' in userlist:
1209 self.logger.debug(_('Cleaning up xml metadata'))
1210 xmlcode, xmlresults = self.cleanMetadata()
1211 if 'dbcache' in userlist or 'metadata' in userlist:
1212 self.logger.debug(_('Cleaning up database cache'))
1213 dbcode, dbresults = self.cleanSqlite()
1214 if 'expire-cache' in userlist or 'metadata' in userlist:
1215 self.logger.debug(_('Cleaning up expire-cache metadata'))
1216 expccode, expcresults = self.cleanExpireCache()
1217 if 'rpmdb' in userlist:
1218 self.logger.debug(_('Cleaning up cached rpmdb data'))
1219 expccode, expcresults = self.cleanRpmDB()
1220 if 'plugins' in userlist:
1221 self.logger.debug(_('Cleaning up plugins'))
1222 self.plugins.run('clean')
1223
1224 code = hdrcode + pkgcode + xmlcode + dbcode + expccode
1225 results = hdrresults + pkgresults + xmlresults + dbresults + expcresults
1226 for msg in results:
1227 self.verbose_logger.log(yum.logginglevels.INFO_2, msg)
1228 return code, []
1229
1230 def returnGroupLists(self, userlist):
1231
1232 uservisible=1
1233
1234 if len(userlist) > 0:
1235 if userlist[0] == 'hidden':
1236 uservisible=0
1237 userlist.pop(0)
1238 if not userlist:
1239 userlist = None # Match everything...
1240
1241 installed, available = self.doGroupLists(uservisible=uservisible,
1242 patterns=userlist)
1243
1244 if not installed and not available:
1245 self.logger.error(_('Warning: No groups match: %s'),
1246 ", ".join(userlist))
1247 return 0, []
1248
1249 def _out_grp(sect, group):
1250 if not done:
1251 self.verbose_logger.log(yum.logginglevels.INFO_2, sect)
1252 msg = ' %s' % group.ui_name
1253 if self.verbose_logger.isEnabledFor(yum.logginglevels.DEBUG_3):
1254 msg += ' (%s)' % group.groupid
1255 if group.langonly:
1256 msg += ' [%s]' % group.langonly
1257 self.verbose_logger.log(yum.logginglevels.INFO_2, '%s', msg)
1258
1259 done = False
1260 for group in installed:
1261 if group.langonly: continue
1262 _out_grp(_('Installed Groups:'), group)
1263 done = True
1264
1265 done = False
1266 for group in installed:
1267 if not group.langonly: continue
1268 _out_grp(_('Installed Language Groups:'), group)
1269 done = True
1270
1271 done = False
1272 for group in available:
1273 if group.langonly: continue
1274 _out_grp(_('Available Groups:'), group)
1275 done = True
1276
1277 done = False
1278 for group in available:
1279 if not group.langonly: continue
1280 _out_grp(_('Available Language Groups:'), group)
1281 done = True
1282
1283 return 0, [_('Done')]
1284
1285 def returnGroupSummary(self, userlist):
1286
1287 uservisible=1
1288
1289 if len(userlist) > 0:
1290 if userlist[0] == 'hidden':
1291 uservisible=0
1292 userlist.pop(0)
1293 if not userlist:
1294 userlist = None # Match everything...
1295
1296 installed, available = self.doGroupLists(uservisible=uservisible,
1297 patterns=userlist)
1298
1299 def _out_grp(sect, num):
1300 if not num:
1301 return
1302 self.verbose_logger.log(yum.logginglevels.INFO_2, '%s %u', sect,num)
1303 done = 0
1304 for group in installed:
1305 if group.langonly: continue
1306 done += 1
1307 _out_grp(_('Installed Groups:'), done)
1308
1309 done = 0
1310 for group in installed:
1311 if not group.langonly: continue
1312 done += 1
1313 _out_grp(_('Installed Language Groups:'), done)
1314
1315 done = False
1316 for group in available:
1317 if group.langonly: continue
1318 done += 1
1319 _out_grp(_('Available Groups:'), done)
1320
1321 done = False
1322 for group in available:
1323 if not group.langonly: continue
1324 done += 1
1325 _out_grp(_('Available Language Groups:'), done)
1326
1327 return 0, [_('Done')]
1328
1329 def returnGroupInfo(self, userlist):
1330 """returns complete information on a list of groups"""
1331 for strng in userlist:
1332 group_matched = False
1333 for group in self.comps.return_groups(strng):
1334 self.displayPkgsInGroups(group)
1335 group_matched = True
1336
1337 if not group_matched:
1338 self.logger.error(_('Warning: Group %s does not exist.'), strng)
1339
1340 return 0, []
1341
1342 def installGroups(self, grouplist):
1343 """for each group requested do 'selectGroup' on them."""
1344
1345 pkgs_used = []
1346
1347 for group_string in grouplist:
1348 group_matched = False
1349 for group in self.comps.return_groups(group_string):
1350 group_matched = True
1351
1352
1353 try:
1354 txmbrs = self.selectGroup(group.groupid)
1355 except yum.Errors.GroupsError:
1356 self.logger.critical(_('Warning: Group %s does not exist.'), group_string)
1357 continue
1358 else:
1359 pkgs_used.extend(txmbrs)
1360
1361 if not group_matched:
1362 self.logger.error(_('Warning: Group %s does not exist.'), group_string)
1363 continue
1364
1365 if not pkgs_used:
1366 return 0, [_('No packages in any requested group available to install or update')]
1367 else:
1368 return 2, [P_('%d package to Install', '%d packages to Install', len(pkgs_used)) % len(pkgs_used)]
1369
1370 def removeGroups(self, grouplist):
1371 """Remove only packages of the named group(s). Do not recurse."""
1372
1373 pkgs_used = []
1374 for group_string in grouplist:
1375 try:
1376 txmbrs = self.groupRemove(group_string)
1377 except yum.Errors.GroupsError:
1378 self.logger.critical(_('No group named %s exists'), group_string)
1379 continue
1380 else:
1381 pkgs_used.extend(txmbrs)
1382
1383 if not pkgs_used:
1384 return 0, [_('No packages to remove from groups')]
1385 else:
1386 return 2, [P_('%d package to remove', '%d packages to remove', len(pkgs_used)) % len(pkgs_used)]
1387
1388
1389
1390 def _promptWanted(self):
1391 # shortcut for the always-off/always-on options
1392 if self.conf.assumeyes:
1393 return False
1394 if self.conf.alwaysprompt:
1395 return True
1396
1397 # prompt if:
1398 # package was added to fill a dependency
1399 # package is being removed
1400 # package wasn't explictly given on the command line
1401 for txmbr in self.tsInfo.getMembers():
1402 if txmbr.isDep or \
1403 txmbr.ts_state == 'e' or \
1404 txmbr.name not in self.extcmds:
1405 return True
1406
1407 # otherwise, don't prompt
1408 return False
1409
1410 def usage(self):
1411 ''' Print out command line usage '''
1412 sys.stdout.write(self.optparser.format_help())
1413
1414 def shellUsage(self):
1415 ''' Print out the shell usage '''
1416 sys.stdout.write(self.optparser.get_usage())
1417
1418 def _installable(self, pkg, ematch=False):
1419
1420 """check if the package is reasonably installable, true/false"""
1421
1422 exactarchlist = self.conf.exactarchlist
1423 # we look through each returned possibility and rule out the
1424 # ones that we obviously can't use
1425
1426 if self.rpmdb.contains(po=pkg):
1427 self.verbose_logger.log(yum.logginglevels.DEBUG_3,
1428 _('Package %s is already installed, skipping'), pkg)
1429 return False
1430
1431 # everything installed that matches the name
1432 installedByKey = self.rpmdb.searchNevra(name=pkg.name)
1433 comparable = []
1434 for instpo in installedByKey:
1435 if isMultiLibArch(instpo.arch) == isMultiLibArch(pkg.arch):
1436 comparable.append(instpo)
1437 else:
1438 self.verbose_logger.log(yum.logginglevels.DEBUG_3,
1439 _('Discarding non-comparable pkg %s.%s'), instpo.name, instpo.arch)
1440 continue
1441
1442 # go through each package
1443 if len(comparable) > 0:
1444 for instpo in comparable:
1445 if pkg.verGT(instpo): # we're newer - this is an update, pass to them
1446 if instpo.name in exactarchlist:
1447 if pkg.arch == instpo.arch:
1448 return True
1449 else:
1450 return True
1451
1452 elif pkg.verEQ(instpo): # same, ignore
1453 return False
1454
1455 elif pkg.verLT(instpo): # lesser, check if the pkgtup is an exactmatch
1456 # if so then add it to be installed
1457 # if it can be multiply installed
1458 # this is where we could handle setting
1459 # it to be an 'oldpackage' revert.
1460
1461 if ematch and self.allowedMultipleInstalls(pkg):
1462 return True
1463
1464 else: # we've not got any installed that match n or n+a
1465 self.verbose_logger.log(yum.logginglevels.DEBUG_1, _('No other %s installed, adding to list for potential install'), pkg.name)
1466 return True
1467
1468 return False
1469
1470class YumOptionParser(OptionParser):
1471 '''Subclass that makes some minor tweaks to make OptionParser do things the
1472 "yum way".
1473 '''
1474
1475 def __init__(self,base, **kwargs):
1476 # check if this is called with a utils=True/False parameter
1477 if 'utils' in kwargs:
1478 self._utils = kwargs['utils']
1479 del kwargs['utils']
1480 else:
1481 self._utils = False
1482 OptionParser.__init__(self, **kwargs)
1483 self.logger = logging.getLogger("yum.cli")
1484 self.base = base
1485 self.plugin_option_group = OptionGroup(self, _("Plugin Options"))
1486 self.add_option_group(self.plugin_option_group)
1487
1488 self._addYumBasicOptions()
1489
1490 def error(self, msg):
1491 '''This method is overridden so that error output goes to logger. '''
1492 self.print_usage()
1493 self.logger.critical(_("Command line error: %s"), msg)
1494 sys.exit(1)
1495
1496 def firstParse(self,args):
1497 # Parse only command line options that affect basic yum setup
1498 try:
1499 args = _filtercmdline(
1500 ('--noplugins','--version','-q', '-v', "--quiet", "--verbose"),
1501 ('-c', '--config', '-d', '--debuglevel',
1502 '-e', '--errorlevel',
1503 '--installroot',
1504 '--disableplugin', '--enableplugin', '--releasever',
1505 '--setopt'),
1506 args)
1507 except ValueError, arg:
1508 self.base.usage()
1509 print >> sys.stderr, (_("\n\n%s: %s option requires an argument") %
1510 ('Command line error', arg))
1511 sys.exit(1)
1512 return self.parse_args(args=args)[0]
1513
1514 @staticmethod
1515 def _splitArg(seq):
1516 """ Split all strings in seq, at "," and whitespace.
1517 Returns a new list. """
1518 ret = []
1519 for arg in seq:
1520 ret.extend(arg.replace(",", " ").split())
1521 return ret
1522
1523 def setupYumConfig(self, args=None):
1524 # Now parse the command line for real
1525 if not args:
1526 (opts, cmds) = self.parse_args()
1527 else:
1528 (opts, cmds) = self.parse_args(args=args)
1529
1530 # Let the plugins know what happened on the command line
1531 self.base.plugins.setCmdLine(opts, cmds)
1532
1533 try:
1534 # config file is parsed and moving us forward
1535 # set some things in it.
1536
1537 # Handle remaining options
1538 if opts.assumeyes:
1539 self.base.conf.assumeyes =1
1540
1541 # Instead of going cache-only for a non-root user, try to use a
1542 # user writable cachedir. If that fails fall back to cache-only.
1543 if opts.cacheonly:
1544 self.base.conf.cache = 1
1545 elif not self.base.setCacheDir():
1546 self.base.conf.cache = 1
1547
1548 if opts.obsoletes:
1549 self.base.conf.obsoletes = 1
1550
1551 if opts.installroot:
1552 self._checkAbsInstallRoot(opts)
1553 self.base.conf.installroot = opts.installroot
1554
1555 if opts.skipbroken:
1556 self.base.conf.skip_broken = True
1557
1558 if opts.showdupesfromrepos:
1559 self.base.conf.showdupesfromrepos = True
1560
1561 if opts.color not in (None, 'auto', 'always', 'never',
1562 'tty', 'if-tty', 'yes', 'no', 'on', 'off'):
1563 raise ValueError, _("--color takes one of: auto, always, never")
1564 elif opts.color is None:
1565 if self.base.conf.color != 'auto':
1566 self.base.term.reinit(color=self.base.conf.color)
1567 else:
1568 _remap = {'tty' : 'auto', 'if-tty' : 'auto',
1569 '1' : 'always', 'true' : 'always',
1570 'yes' : 'always', 'on' : 'always',
1571 '0' : 'always', 'false' : 'always',
1572 'no' : 'never', 'off' : 'never'}
1573 opts.color = _remap.get(opts.color, opts.color)
1574 if opts.color != 'auto':
1575 self.base.term.reinit(color=opts.color)
1576
1577 if opts.disableexcludes:
1578 disable_excludes = self._splitArg(opts.disableexcludes)
1579 else:
1580 disable_excludes = []
1581 self.base.conf.disable_excludes = disable_excludes
1582
1583 for exclude in self._splitArg(opts.exclude):
1584 try:
1585 excludelist = self.base.conf.exclude
1586 excludelist.append(exclude)
1587 self.base.conf.exclude = excludelist
1588 except yum.Errors.ConfigError, e:
1589 self.logger.critical(e)
1590 self.base.usage()
1591 sys.exit(1)
1592
1593 if opts.rpmverbosity is not None:
1594 self.base.conf.rpmverbosity = opts.rpmverbosity
1595
1596 # setup the progress bars/callbacks
1597 self.base.setupProgressCallbacks()
1598 # setup the callbacks to import gpg pubkeys and confirm them
1599 self.base.setupKeyImportCallbacks()
1600
1601 # Process repo enables and disables in order
1602 for opt, repoexp in opts.repos:
1603 try:
1604 if opt == '--enablerepo':
1605 self.base.repos.enableRepo(repoexp)
1606 elif opt == '--disablerepo':
1607 self.base.repos.disableRepo(repoexp)
1608 except yum.Errors.ConfigError, e:
1609 self.logger.critical(e)
1610 self.base.usage()
1611 sys.exit(1)
1612
1613 # make sure the added repos are setup.
1614 if len(opts.repos) > 0:
1615 self.base._getRepos(doSetup=True)
1616
1617 # Disable all gpg key checking, if requested.
1618 if opts.nogpgcheck:
1619 # Altering the normal configs. doesn't work too well, esp. with
1620 # regard to dynamically enabled repos.
1621 self.base._override_sigchecks = True
1622 for repo in self.base.repos.listEnabled():
1623 repo._override_sigchecks = True
1624
1625 except ValueError, e:
1626 self.logger.critical(_('Options Error: %s'), e)
1627 self.base.usage()
1628 sys.exit(1)
1629
1630 return opts, cmds
1631
1632 def _checkAbsInstallRoot(self, opts):
1633 if not opts.installroot:
1634 return
1635 if opts.installroot[0] == '/':
1636 return
1637 # We have a relative installroot ... haha
1638 self.logger.critical(_('--installroot must be an absolute path: %s'),
1639 opts.installroot)
1640 sys.exit(1)
1641
1642 def getRoot(self,opts):
1643 self._checkAbsInstallRoot(opts)
1644 # If the conf file is inside the installroot - use that.
1645 # otherwise look for it in the normal root
1646 if opts.installroot:
1647 if os.access(opts.installroot+'/'+opts.conffile, os.R_OK):
1648 opts.conffile = opts.installroot+'/'+opts.conffile
1649 elif opts.conffile == '/etc/yum/yum.conf':
1650 # check if /installroot/etc/yum.conf exists.
1651 if os.access(opts.installroot+'/etc/yum.conf', os.R_OK):
1652 opts.conffile = opts.installroot+'/etc/yum.conf'
1653 root=opts.installroot
1654 else:
1655 root = '/@unixroot'
1656 return root
1657
1658 def _wrapOptParseUsage(self, opt, value, parser, *args, **kwargs):
1659 self.base.usage()
1660 self.exit()
1661
1662 def _addYumBasicOptions(self):
1663 def repo_optcb(optobj, opt, value, parser):
1664 '''Callback for the enablerepo and disablerepo option.
1665
1666 Combines the values given for these options while preserving order
1667 from command line.
1668 '''
1669 dest = eval('parser.values.%s' % optobj.dest)
1670 dest.append((opt, value))
1671
1672 if self._utils:
1673 group = OptionGroup(self, "Yum Base Options")
1674 self.add_option_group(group)
1675 else:
1676 group = self
1677
1678 # Note that we can't use the default action="help" because of the
1679 # fact that print_help() unconditionally does .encode() ... which is
1680 # bad on unicode input.
1681 group.conflict_handler = "resolve"
1682 group.add_option("-h", "--help", action="callback",
1683 callback=self._wrapOptParseUsage,
1684 help=_("show this help message and exit"))
1685 group.conflict_handler = "error"
1686
1687 group.add_option("-t", "--tolerant", action="store_true",
1688 help=_("be tolerant of errors"))
1689 group.add_option("-C", "--cacheonly", dest="cacheonly",
1690 action="store_true",
1691 help=_("run entirely from system cache, don't update cache"))
1692 group.add_option("-c", "--config", dest="conffile",
1693 default='/@unixroot/etc/yum/yum.conf',
1694 help=_("config file location"), metavar='[config file]')
1695 group.add_option("-R", "--randomwait", dest="sleeptime", type='int',
1696 default=None,
1697 help=_("maximum command wait time"), metavar='[minutes]')
1698 group.add_option("-d", "--debuglevel", dest="debuglevel", default=None,
1699 help=_("debugging output level"), type='int',
1700 metavar='[debug level]')
1701 group.add_option("--showduplicates", dest="showdupesfromrepos",
1702 action="store_true",
1703 help=_("show duplicates, in repos, in list/search commands"))
1704 group.add_option("-e", "--errorlevel", dest="errorlevel", default=None,
1705 help=_("error output level"), type='int',
1706 metavar='[error level]')
1707 group.add_option("", "--rpmverbosity", default=None,
1708 help=_("debugging output level for rpm"),
1709 metavar='[debug level name]')
1710 group.add_option("-q", "--quiet", dest="quiet", action="store_true",
1711 help=_("quiet operation"))
1712 group.add_option("-v", "--verbose", dest="verbose", action="store_true",
1713 help=_("verbose operation"))
1714 group.add_option("-y", "--assumeyes", dest="assumeyes",
1715 action="store_true", help=_("answer yes for all questions"))
1716 group.add_option("--version", action="store_true",
1717 help=_("show Yum version and exit"))
1718 group.add_option("--installroot", help=_("set install root"),
1719 metavar='[path]')
1720 group.add_option("--enablerepo", action='callback',
1721 type='string', callback=repo_optcb, dest='repos', default=[],
1722 help=_("enable one or more repositories (wildcards allowed)"),
1723 metavar='[repo]')
1724 group.add_option("--disablerepo", action='callback',
1725 type='string', callback=repo_optcb, dest='repos', default=[],
1726 help=_("disable one or more repositories (wildcards allowed)"),
1727 metavar='[repo]')
1728 group.add_option("-x", "--exclude", default=[], action="append",
1729 help=_("exclude package(s) by name or glob"), metavar='[package]')
1730 group.add_option("", "--disableexcludes", default=[], action="append",
1731 help=_("disable exclude from main, for a repo or for everything"),
1732 metavar='[repo]')
1733 group.add_option("--obsoletes", action="store_true",
1734 help=_("enable obsoletes processing during updates"))
1735 group.add_option("--noplugins", action="store_true",
1736 help=_("disable Yum plugins"))
1737 group.add_option("--nogpgcheck", action="store_true",
1738 help=_("disable gpg signature checking"))
1739 group.add_option("", "--disableplugin", dest="disableplugins", default=[],
1740 action="append", help=_("disable plugins by name"),
1741 metavar='[plugin]')
1742 group.add_option("", "--enableplugin", dest="enableplugins", default=[],
1743 action="append", help=_("enable plugins by name"),
1744 metavar='[plugin]')
1745 group.add_option("--skip-broken", action="store_true", dest="skipbroken",
1746 help=_("skip packages with depsolving problems"))
1747 group.add_option("", "--color", dest="color", default=None,
1748 help=_("control whether color is used"))
1749 group.add_option("", "--releasever", dest="releasever", default=None,
1750 help=_("set value of $releasever in yum config and repo files"))
1751 group.add_option("", "--setopt", dest="setopts", default=[],
1752 action="append", help=_("set arbitrary config and repo options"))
1753
1754
1755def _filtercmdline(novalopts, valopts, args):
1756 '''Keep only specific options from the command line argument list
1757
1758 This function allows us to peek at specific command line options when using
1759 the optparse module. This is useful when some options affect what other
1760 options should be available.
1761
1762 @param novalopts: A sequence of options to keep that don't take an argument.
1763 @param valopts: A sequence of options to keep that take a single argument.
1764 @param args: The command line arguments to parse (as per sys.argv[:1]
1765 @return: A list of strings containing the filtered version of args.
1766
1767 Will raise ValueError if there was a problem parsing the command line.
1768 '''
1769 # ' xemacs syntax hack
1770 out = []
1771 args = list(args) # Make a copy because this func is destructive
1772
1773 while len(args) > 0:
1774 a = args.pop(0)
1775 if '=' in a:
1776 opt, _ = a.split('=', 1)
1777 if opt in valopts:
1778 out.append(a)
1779
1780 elif a == '--':
1781 out.append(a)
1782
1783 elif a in novalopts:
1784 out.append(a)
1785
1786 elif a in valopts:
1787 if len(args) < 1:
1788 raise ValueError, a
1789 next = args.pop(0)
1790 if next[0] == '-':
1791 raise ValueError, a
1792
1793 out.extend([a, next])
1794
1795 else:
1796 # Check for single letter options that take a value, where the
1797 # value is right up against the option
1798 for opt in valopts:
1799 if len(opt) == 2 and a.startswith(opt):
1800 out.append(a)
1801
1802 return out
1803
Note: See TracBrowser for help on using the repository browser.