1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """
19 Configuration parser and default values for yum.
20 """
21 _use_iniparse = True
22
23 import os
24 import sys
25 import warnings
26 import rpm
27 import copy
28 import urlparse
29 import shlex
30 from parser import ConfigPreProcessor, varReplace
31 try:
32 from iniparse import INIConfig
33 from iniparse.compat import NoSectionError, NoOptionError, ParsingError
34 from iniparse.compat import RawConfigParser as ConfigParser
35 except ImportError:
36 _use_iniparse = False
37 if not _use_iniparse:
38 from ConfigParser import NoSectionError, NoOptionError, ParsingError
39 from ConfigParser import ConfigParser
40 import rpmUtils.transaction
41 import Errors
42 import types
43 from misc import get_uuid
44
45
46 __pkgs_gpgcheck_default__ = False
47 __repo_gpgcheck_default__ = False
48
50 '''
51 This class handles a single Yum configuration file option. Create
52 subclasses for each type of supported configuration option.
53
54 Python descriptor foo (__get__ and __set__) is used to make option
55 definition easy and consise.
56 '''
57
62
64 '''Calculate the internal attribute name used to store option state in
65 configuration instances.
66 '''
67 self._attrname = '__opt%d' % id(self)
68
70 '''Called when the option is read (via the descriptor protocol).
71
72 @param obj: The configuration instance to modify.
73 @param objtype: The type of the config instance (not used).
74 @return: The parsed option value or the default value if the value
75 wasn't set in the configuration file.
76 '''
77
78 if obj is None:
79 return self
80
81 return getattr(obj, self._attrname, None)
82
84 '''Called when the option is set (via the descriptor protocol).
85
86 @param obj: The configuration instance to modify.
87 @param value: The value to set the option to.
88 @return: Nothing.
89 '''
90
91 if isinstance(value, basestring):
92 try:
93 value = self.parse(value)
94 except ValueError, e:
95
96 raise ValueError('Error parsing "%s = %r": %s' % (self._optname,
97 value, str(e)))
98 setattr(obj, self._attrname, value)
99
100 - def setup(self, obj, name):
101 '''Initialise the option for a config instance.
102 This must be called before the option can be set or retrieved.
103
104 @param obj: BaseConfig (or subclass) instance.
105 @param name: Name of the option.
106 '''
107 self._optname = name
108 setattr(obj, self._attrname, copy.copy(self.default))
109
111 '''Return a safe copy of this Option instance
112 '''
113 new = copy.copy(self)
114 new._setattrname()
115 return new
116
118 '''Parse the string value to the Option's native value.
119
120 @param s: Raw string value to parse.
121 @return: Validated native value.
122
123 Will raise ValueError if there was a problem parsing the string.
124 Subclasses should override this.
125 '''
126 return s
127
129 '''Convert the Option's native value to a string value.
130
131 @param value: Native option value.
132 @return: String representation of input.
133
134 This does the opposite of the parse() method above.
135 Subclasses should override this.
136 '''
137 return str(value)
138
140 '''Clone an Option instance for the purposes of inheritance. The returned
141 instance has all the same properties as the input Option and shares items
142 such as the default value. Use this to avoid redefinition of reused
143 options.
144
145 @param option_obj: Option instance to inherit.
146 @return: New Option instance inherited from the input.
147 '''
148 new_option = option_obj.clone()
149 new_option.inherit = True
150 return new_option
151
153
154 """
155 An option containing a list of strings.
156 """
157
162
164 """Converts a string from the config file to a workable list
165
166 Commas and spaces are used as separators for the list
167 """
168
169
170 s = s.replace('\n', ' ')
171 s = s.replace(',', ' ')
172 return s.split()
173
175 return '\n '.join(value)
176
178 '''
179 This option handles lists of URLs with validation of the URL scheme.
180 '''
181
182 - def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'),
183 allow_none=False):
187
189 url = url.strip()
190
191
192 if url.lower() == '_none_':
193 if self.allow_none:
194 return None
195 else:
196 raise ValueError('"_none_" is not a valid value')
197
198
199 (s,b,p,q,f,o) = urlparse.urlparse(url)
200 if s not in self.schemes:
201 raise ValueError('URL must be %s not "%s"' % (self._schemelist(), s))
202
203 return url
204
206 '''Return a user friendly list of the allowed schemes
207 '''
208 if len(self.schemes) < 1:
209 return 'empty'
210 elif len(self.schemes) == 1:
211 return self.schemes[0]
212 else:
213 return '%s or %s' % (', '.join(self.schemes[:-1]), self.schemes[-1])
214
216 '''
217 Option for handling lists of URLs with validation of the URL scheme.
218 '''
219
220 - def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https')):
225
227 out = []
228 s = s.replace('\n', ' ')
229 s = s.replace(',', ' ')
230 items = [ item.replace(' ', '%20') for item in shlex.split(s) ]
231 s = ' '.join(items)
232 for url in super(UrlListOption, self).parse(s):
233 out.append(self._urloption.parse(url))
234 return out
235
236
238
239 """
240 An option representing an integer value.
241 """
242
243 - def __init__(self, default=None, range_min=None, range_max=None):
247
249 try:
250 val = int(s)
251 except (ValueError, TypeError), e:
252 raise ValueError('invalid integer value')
253 if self._range_max is not None and val > self._range_max:
254 raise ValueError('out of range integer value')
255 if self._range_min is not None and val < self._range_min:
256 raise ValueError('out of range integer value')
257 return val
258
260
261 """
262 An option representing a positive integer value, where 0 can have a special
263 represention.
264 """
265
266 - def __init__(self, default=None, range_min=0, range_max=None,
267 names_of_0=None):
270
275
277
278 """
279 An option representing an integer value of seconds, or a human readable
280 variation specifying days, hours, minutes or seconds until something
281 happens. Works like BytesOption.
282 Note that due to historical president -1 means "never", so this accepts
283 that and allows the word never too.
284
285 Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never
286 Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y
287
288 Return value will always be an integer
289 """
290 MULTS = {'d': 60 * 60 * 24, 'h' : 60 * 60, 'm' : 60, 's': 1}
291
293 if len(s) < 1:
294 raise ValueError("no value specified")
295
296 if s == "-1" or s == "never":
297 return -1
298 if s[-1].isalpha():
299 n = s[:-1]
300 unit = s[-1].lower()
301 mult = self.MULTS.get(unit, None)
302 if not mult:
303 raise ValueError("unknown unit '%s'" % unit)
304 else:
305 n = s
306 mult = 1
307
308 try:
309 n = float(n)
310 except (ValueError, TypeError), e:
311 raise ValueError('invalid value')
312
313 if n < 0:
314 raise ValueError("seconds value may not be negative")
315
316 return int(n * mult)
317
319
320 """
321 An option representing a boolean value.
322
323 The value can be one of 0, 1, yes, no, true, or false.
324 """
325
327 s = s.lower()
328 if s in ('0', 'no', 'false'):
329 return False
330 elif s in ('1', 'yes', 'true'):
331 return True
332 else:
333 raise ValueError('invalid boolean value')
334
336 if value:
337 return "1"
338 else:
339 return "0"
340
342 """
343 An option representing a numeric float value.
344 """
346 try:
347 return float(s.strip())
348 except (ValueError, TypeError):
349 raise ValueError('invalid float value')
350
352 '''Handles string values where only specific values are allowed
353 '''
354 - def __init__(self, default=None, allowed=(), mapper={}):
358
360 if s in self._mapper:
361 s = self._mapper[s]
362 if s not in self._allowed:
363 raise ValueError('"%s" is not an allowed value' % s)
364 return s
365
367 ''' Mainly for compat. with BoolOption, works like SelectionOption but
368 lowers input case. '''
369
372
374
375 """
376 An option representing a value in bytes.
377
378 The value may be given in bytes, kilobytes, megabytes, or gigabytes.
379 """
380
381 MULTS = {
382 'k': 1024,
383 'm': 1024*1024,
384 'g': 1024*1024*1024,
385 }
386
388 """Parse a friendly bandwidth option to bytes
389
390 The input should be a string containing a (possibly floating point)
391 number followed by an optional single character unit. Valid units are
392 'k', 'M', 'G'. Case is ignored.
393
394 Valid inputs: 100, 123M, 45.6k, 12.4G, 100K, 786.3, 0
395 Invalid inputs: -10, -0.1, 45.6L, 123Mb
396
397 Return value will always be an integer
398
399 1k = 1024 bytes.
400
401 ValueError will be raised if the option couldn't be parsed.
402 """
403 if len(s) < 1:
404 raise ValueError("no value specified")
405
406 if s[-1].isalpha():
407 n = s[:-1]
408 unit = s[-1].lower()
409 mult = self.MULTS.get(unit, None)
410 if not mult:
411 raise ValueError("unknown unit '%s'" % unit)
412 else:
413 n = s
414 mult = 1
415
416 try:
417 n = float(n)
418 except ValueError:
419 raise ValueError("couldn't convert '%s' to number" % n)
420
421 if n < 0:
422 raise ValueError("bytes value may not be negative")
423
424 return int(n * mult)
425
427
428 """
429 An option representing a bandwidth throttle value. See
430 ThrottleOption.parse for acceptable input values.
431 """
432
434 """Get a throttle option.
435
436 Input may either be a percentage or a "friendly bandwidth value" as
437 accepted by the BytesOption.
438
439 Valid inputs: 100, 50%, 80.5%, 123M, 45.6k, 12.4G, 100K, 786.0, 0
440 Invalid inputs: 100.1%, -4%, -500
441
442 Return value will be a int if a bandwidth value was specified or a
443 float if a percentage was given.
444
445 ValueError will be raised if input couldn't be parsed.
446 """
447 if len(s) < 1:
448 raise ValueError("no value specified")
449
450 if s[-1] == '%':
451 n = s[:-1]
452 try:
453 n = float(n)
454 except ValueError:
455 raise ValueError("couldn't convert '%s' to number" % n)
456 if n < 0 or n > 100:
457 raise ValueError("percentage is out of range")
458 return n / 100.0
459 else:
460 return BytesOption.parse(self, s)
461
463 '''
464 Base class for storing configuration definitions. Subclass when creating
465 your own definitons.
466 '''
467
474
476 out = []
477 out.append('[%s]' % self._section)
478 for name, value in self.iteritems():
479 out.append('%s: %r' % (name, value))
480 return '\n'.join(out)
481
482 - def populate(self, parser, section, parent=None):
483 '''Set option values from a INI file section.
484
485 @param parser: ConfParser instance (or subclass)
486 @param section: INI file section to read use.
487 @param parent: Optional parent BaseConfig (or subclass) instance to use
488 when doing option value inheritance.
489 '''
490 self.cfg = parser
491 self._section = section
492
493 if parser.has_section(section):
494 opts = set(parser.options(section))
495 else:
496 opts = set()
497 for name in self.iterkeys():
498 option = self.optionobj(name)
499 value = None
500 if name in opts:
501 value = parser.get(section, name)
502 else:
503
504 if parent and option.inherit:
505 value = getattr(parent, name)
506
507 if value is not None:
508 setattr(self, name, value)
509
511 '''Return the Option instance for the given name
512 '''
513 obj = getattr(cls, name, None)
514 if isinstance(obj, Option):
515 return obj
516 elif exceptions:
517 raise KeyError
518 else:
519 return None
520 optionobj = classmethod(optionobj)
521
523 '''Return True if the given name refers to a defined option
524 '''
525 return cls.optionobj(name, exceptions=False) is not None
526 isoption = classmethod(isoption)
527
529 '''Yield the names of all defined options in the instance.
530 '''
531 for name in dir(self):
532 if self.isoption(name):
533 yield name
534
536 '''Yield (name, value) pairs for every option in the instance.
537
538 The value returned is the parsed, validated option value.
539 '''
540
541 for name in self.iterkeys():
542 yield (name, getattr(self, name))
543
544 - def write(self, fileobj, section=None, always=()):
545 '''Write out the configuration to a file-like object
546
547 @param fileobj: File-like object to write to
548 @param section: Section name to use. If not-specified the section name
549 used during parsing will be used.
550 @param always: A sequence of option names to always write out.
551 Options not listed here will only be written out if they are at
552 non-default values. Set to None to dump out all options.
553 '''
554
555 if section is None:
556 if self._section is None:
557 raise ValueError("not populated, don't know section")
558 section = self._section
559
560
561 cfgOptions = self.cfg.options(section)
562 for name,value in self.iteritems():
563 option = self.optionobj(name)
564 if always is None or name in always or option.default != value or name in cfgOptions :
565 self.cfg.set(section,name, option.tostring(value))
566
567 self.cfg.write(fileobj)
568
570 warnings.warn('getConfigOption() will go away in a future version of Yum.\n'
571 'Please access option values as attributes or using getattr().',
572 DeprecationWarning)
573 if hasattr(self, option):
574 return getattr(self, option)
575 return default
576
578 warnings.warn('setConfigOption() will go away in a future version of Yum.\n'
579 'Please set option values as attributes or using setattr().',
580 DeprecationWarning)
581 if hasattr(self, option):
582 setattr(self, option, value)
583 else:
584 raise Errors.ConfigError, 'No such option %s' % option
585
606
608 '''
609 Configuration option definitions for yum.conf\'s [main] section.
610
611 Note: see also options inherited from StartupConf
612 '''
613 retries = PositiveIntOption(10, names_of_0=["<forever>"])
614 recent = IntOption(7, range_min=0)
615
616 cachedir = Option('/var/cache/yum')
617
618 keepcache = BoolOption(True)
619 logfile = Option('/var/log/yum.log')
620 reposdir = ListOption(['/etc/yum/repos.d', '/etc/yum.repos.d'])
621
622 commands = ListOption()
623 exclude = ListOption()
624 failovermethod = Option('roundrobin')
625 proxy = UrlOption(schemes=('http', 'ftp', 'https'), allow_none=True)
626 proxy_username = Option()
627 proxy_password = Option()
628 installonlypkgs = ListOption(['kernel', 'kernel-bigmem',
629 'kernel-enterprise','kernel-smp', 'kernel-modules', 'kernel-debug',
630 'kernel-unsupported', 'kernel-source', 'kernel-devel', 'kernel-PAE',
631 'kernel-PAE-debug'])
632
633
634
635 installonly_limit = PositiveIntOption(0, range_min=2,
636 names_of_0=["0", "<off>"])
637 kernelpkgnames = ListOption(['kernel','kernel-smp', 'kernel-enterprise',
638 'kernel-bigmem', 'kernel-BOOT', 'kernel-PAE', 'kernel-PAE-debug'])
639 exactarchlist = ListOption(['kernel', 'kernel-smp',
640 'kernel-hugemem', 'kernel-enterprise', 'kernel-bigmem',
641 'kernel-devel', 'kernel-PAE', 'kernel-PAE-debug'])
642 tsflags = ListOption()
643
644 assumeyes = BoolOption(False)
645 alwaysprompt = BoolOption(True)
646 exactarch = BoolOption(True)
647 tolerant = BoolOption(True)
648 diskspacecheck = BoolOption(True)
649 overwrite_groups = BoolOption(False)
650 keepalive = BoolOption(True)
651
652 gpgcheck = BoolOption(__pkgs_gpgcheck_default__)
653 repo_gpgcheck = BoolOption(__repo_gpgcheck_default__)
654 obsoletes = BoolOption(True)
655 showdupesfromrepos = BoolOption(False)
656 enabled = BoolOption(True)
657 enablegroups = BoolOption(True)
658 enable_group_conditionals = BoolOption(True)
659 group_package_types = ListOption(['mandatory', 'default'])
660
661 timeout = FloatOption(30.0)
662
663 bandwidth = BytesOption(0)
664 throttle = ThrottleOption(0)
665
666 http_caching = SelectionOption('all', ('none', 'packages', 'all'))
667 metadata_expire = SecondsOption(60 * 60 * 6)
668
669 mirrorlist_expire = SecondsOption(60 * 60 * 24)
670 rpm_check_debug = BoolOption(True)
671 disable_excludes = ListOption()
672 skip_broken = BoolOption(False)
673
674
675 mdpolicy = ListOption(['group:primary'])
676
677 multilib_policy = SelectionOption('all',('best', 'all'))
678
679
680
681 bugtracker_url = Option('http://yum.baseurl.org/report')
682
683 color = SelectionOption('auto', ('auto', 'never', 'always'),
684 mapper={'on' : 'always', 'yes' : 'always',
685 '1' : 'always', 'true' : 'always',
686 'off' : 'never', 'no' : 'never',
687 '0' : 'never', 'false' : 'never',
688 'tty' : 'auto', 'if-tty' : 'auto'})
689 color_list_installed_older = Option('bold')
690 color_list_installed_newer = Option('bold,yellow')
691 color_list_installed_reinstall = Option('normal')
692 color_list_installed_extra = Option('bold,red')
693
694 color_list_available_upgrade = Option('bold,blue')
695 color_list_available_downgrade = Option('dim,cyan')
696 color_list_available_reinstall = Option('bold,underline,green')
697 color_list_available_install = Option('normal')
698
699 color_update_installed = Option('normal')
700 color_update_local = Option('bold')
701 color_update_remote = Option('normal')
702
703 color_search_match = Option('bold')
704
705 sslcacert = Option()
706 sslverify = BoolOption(True)
707 sslclientcert = Option()
708 sslclientkey = Option()
709
710 history_record = BoolOption(True)
711 history_record_packages = ListOption(['yum', 'rpm'])
712
713 rpmverbosity = Option('info')
714
715 _reposlist = []
716
718 output = '[main]\n'
719
720 excluded_vars = ('cfg', 'uid', 'yumvar', 'progress_obj', 'failure_obj',
721 'disable_excludes', 'config_file_age', 'config_file_path',
722 )
723 for attr in dir(self):
724 if attr.startswith('_'):
725 continue
726 if attr in excluded_vars:
727 continue
728 if isinstance(getattr(self, attr), types.MethodType):
729 continue
730 res = getattr(self, attr)
731 if not res:
732 res = ''
733 if type(res) == types.ListType:
734 res = ',\n '.join(res)
735 output = output + '%s = %s\n' % (attr, res)
736
737 return output
738
796
800
801
803 '''
804 Parse Yum's main configuration file and return a StartupConf instance.
805
806 This is required in order to access configuration settings required as Yum
807 starts up.
808
809 @param configfile: The path to yum.conf.
810 @param root: The base path to use for installation (typically '/')
811 @return: A StartupConf instance.
812
813 May raise Errors.ConfigError if a problem is detected with while parsing.
814 '''
815
816
817
818 StartupConf.installroot.default = root
819 startupconf = StartupConf()
820 startupconf.config_file_path = configfile
821 parser = ConfigParser()
822 confpp_obj = ConfigPreProcessor(configfile)
823 try:
824 parser.readfp(confpp_obj)
825 except ParsingError, e:
826 raise Errors.ConfigError("Parsing file failed: %s" % e)
827 startupconf.populate(parser, 'main')
828
829
830 for path in startupconf.pluginpath:
831 if not path[0] == '/':
832 raise Errors.ConfigError("All plugin search paths must be absolute")
833
834 startupconf._parser = parser
835
836 startupconf.releasever = _getsysver(startupconf.installroot, startupconf.distroverpkg)
837 uuidfile = '%s/%s/uuid' % (startupconf.installroot, startupconf.persistdir)
838 startupconf.uuid = get_uuid(uuidfile)
839
840 return startupconf
841
842 -def readMainConfig(startupconf):
843 '''
844 Parse Yum's main configuration file
845
846 @param startupconf: StartupConf instance as returned by readStartupConfig()
847 @return: Populated YumConf instance.
848 '''
849
850
851
852
853 yumvars = _getEnvVar()
854 yumvars['basearch'] = startupconf.basearch
855 yumvars['arch'] = startupconf.arch
856 yumvars['releasever'] = startupconf.releasever
857 yumvars['uuid'] = startupconf.uuid
858
859
860 yumconf = YumConf()
861 yumconf.populate(startupconf._parser, 'main')
862
863
864 for option in ('cachedir', 'logfile', 'persistdir'):
865 path = getattr(yumconf, option)
866 ir_path = yumconf.installroot + path
867 ir_path = ir_path.replace('//', '/')
868
869 ir_path = varReplace(ir_path, yumvars)
870 setattr(yumconf, option, ir_path)
871
872
873 yumconf.yumvar = yumvars
874 yumconf.uid = 0
875 yumconf.cache = 0
876 yumconf.progess_obj = None
877
878
879 yumconf.config_file_path = startupconf.config_file_path
880 if os.path.exists(startupconf.config_file_path):
881 yumconf.config_file_age = os.stat(startupconf.config_file_path)[8]
882 else:
883 yumconf.config_file_age = 0
884
885
886 yumconf.debuglevel = startupconf.debuglevel
887 yumconf.errorlevel = startupconf.errorlevel
888
889 return yumconf
890
903
904
906 '''Convenience function to retrieve a parsed and converted value from a
907 ConfigParser.
908
909 @param conf: ConfigParser instance or similar
910 @param section: Section name
911 @param name: Option name
912 @param option: Option instance to use for conversion.
913 @return: The parsed value or default if value was not present.
914
915 Will raise ValueError if the option could not be parsed.
916 '''
917 try:
918 val = conf.get(section, name)
919 except (NoSectionError, NoOptionError):
920 return option.default
921 return option.parse(val)
922
924 '''Return variable replacements from the environment variables YUM0 to YUM9
925
926 The result is intended to be used with parser.varReplace()
927 '''
928 yumvar = {}
929 for num in range(0, 10):
930 env = 'YUM%d' % num
931 val = os.environ.get(env, '')
932 if val:
933 yumvar[env.lower()] = val
934 return yumvar
935
937 '''Calculate the release version for the system.
938
939 @param installroot: The value of the installroot option.
940 @param distroverpkg: The value of the distroverpkg option.
941 @return: The release version as a string (eg. '4' for FC4)
942 '''
943 ts = rpmUtils.transaction.initReadOnlyTransaction(root=installroot)
944 ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS))
945 try:
946 idx = ts.dbMatch('provides', distroverpkg)
947 except TypeError, e:
948
949
950 if sys.hexversion < 0x02050000:
951 if hasattr(e,'message'):
952 raise Errors.YumBaseError("Error: " + str(e.message))
953 else:
954 raise Errors.YumBaseError("Error: " + str(e))
955 raise Errors.YumBaseError("Error: " + str(e))
956
957
958 if idx.count() == 0:
959 releasever = '$releasever'
960 else:
961 hdr = idx.next()
962 releasever = hdr['version']
963 del hdr
964 del idx
965 del ts
966 return releasever
967
969 """
970 Writes changes in a repo object back to a .repo file.
971 @param repo: Repo Object
972 @param only: List of attributes to work on (None = All)
973 It work by reading the repo file, changes the values there shall be changed and write it back to disk.
974 """
975
976 if not _use_iniparse:
977 return
978
979 ini = INIConfig(open(repo.repofile))
980
981 cfgOptions = repo.cfg.options(repo.id)
982 for name,value in repo.iteritems():
983 option = repo.optionobj(name)
984 if option.default != value or name in cfgOptions :
985 if only == None or name in only:
986 ini[repo.id][name] = option.tostring(value)
987 fp =file(repo.repofile,"w")
988 fp.write(str(ini))
989 fp.close()
990
991
992
993
994
995
996
997