Package entropy :: Package core :: Package settings :: Module base

Source Code for Module entropy.core.settings.base

   1  # -*- coding: utf-8 -*- 
   2  """ 
   3   
   4      @author: Fabio Erculiani <lxnay@sabayonlinux.org> 
   5      @contact: lxnay@sabayonlinux.org 
   6      @copyright: Fabio Erculiani 
   7      @license: GPL-2 
   8   
   9      B{Entropy Framework SystemSettings module}. 
  10   
  11      SystemSettings is a singleton, pluggable interface which contains 
  12      all the runtime settings (mostly parsed from configuration files 
  13      and inherited from entropy.const -- which contains almost all the 
  14      default values). 
  15      SystemSettings works as a I{dict} object. Due to limitations of 
  16      multiple inherittance when using the Singleton class, SystemSettings 
  17      ONLY mimics a I{dict} AND it's not a subclass of it. 
  18   
  19  """ 
  20  import os 
  21  import sys 
  22  from threading import RLock 
  23   
  24  from entropy.const import etpConst, etpUi, etpSys, const_setup_perms, \ 
  25      const_secure_config_file, const_set_nice_level, \ 
  26      const_extract_cli_repo_params, etpCache, const_isunicode, \ 
  27      const_convert_to_unicode, const_convert_to_rawstring 
  28  from entropy.core import Singleton, EntropyPluginStore 
  29  from entropy.core.settings.plugins.skel import SystemSettingsPlugin 
  30   
31 -class SystemSettings(Singleton, EntropyPluginStore):
32 33 """ 34 This is the place where all the Entropy settings are stored if 35 they are not considered instance constants (etpConst). 36 For example, here we store package masking cache information and 37 settings, client-side, server-side and services settings. 38 Also, this class mimics a dictionary (even if not inheriting it 39 due to development choices). 40 41 Sample code: 42 43 >>> from entropy.core.settings.base import SystemSettings 44 >>> system_settings = SystemSettings() 45 >>> system_settings.clear() 46 >>> system_settings.destroy() 47 48 """ 49 50 import entropy.tools as entropyTools
51 - def init_singleton(self):
52 53 """ 54 Replaces __init__ because SystemSettings is a Singleton. 55 see Singleton API reference for more information. 56 57 """ 58 EntropyPluginStore.__init__(self) 59 60 from entropy.core.settings.plugins.factory import get_available_plugins 61 self.__get_external_plugins = get_available_plugins 62 63 from entropy.cache import EntropyCacher 64 self.__cacher = EntropyCacher() 65 self.__data = {} 66 self.__is_destroyed = False 67 self.__mutex = RLock() # reentrant lock on purpose 68 69 self.__external_plugins = {} 70 self.__setting_files_order = [] 71 self.__setting_files_pre_run = [] 72 self.__setting_files = {} 73 self.__mtime_files = {} 74 self.__persistent_settings = { 75 'pkg_masking_reasons': etpConst['pkg_masking_reasons'].copy(), 76 'pkg_masking_reference': etpConst['pkg_masking_reference'].copy(), 77 'backed_up': {}, 78 # package masking, live 79 'live_packagemasking': { 80 'unmask_matches': set(), 81 'mask_matches': set(), 82 }, 83 } 84 85 self.__setup_const() 86 self.__scan()
87
88 - def destroy(self):
89 """ 90 Overloaded method from Singleton. 91 "Destroys" the instance. 92 93 @return: None 94 @rtype: None 95 """ 96 with self.__mutex: 97 self.__is_destroyed = True
98
99 - def add_plugin(self, system_settings_plugin_instance):
100 """ 101 This method lets you add custom parsers to SystemSettings. 102 Mind that you are responsible of handling your plugin instance 103 and remove it before it is destroyed. You can remove the plugin 104 instance at any time by issuing remove_plugin. 105 Every add_plugin or remove_plugin method will also issue clear() 106 for you. This could be bad and it might be removed in future. 107 108 @param system_settings_plugin_instance: valid SystemSettingsPlugin 109 instance 110 @type system_settings_plugin_instance: SystemSettingsPlugin instance 111 @return: None 112 @rtype: None 113 """ 114 inst = system_settings_plugin_instance 115 if not isinstance(inst, SystemSettingsPlugin): 116 raise AttributeError("SystemSettings: expected valid " + \ 117 "SystemSettingsPlugin instance") 118 with self.__mutex: 119 EntropyPluginStore.add_plugin(self, inst.get_id(), inst) 120 self.clear()
121
122 - def remove_plugin(self, plugin_id):
123 """ 124 This method lets you remove previously added custom parsers from 125 SystemSettings through its plugin identifier. If plugin_id is not 126 available, KeyError exception will be raised. 127 Every add_plugin or remove_plugin method will also issue clear() 128 for you. This could be bad and it might be removed in future. 129 130 @param plugin_id: plugin identifier 131 @type plugin_id: basestring 132 @return: None 133 @rtype: None 134 """ 135 with self.__mutex: 136 EntropyPluginStore.remove_plugin(self, plugin_id) 137 self.clear()
138
139 - def __setup_const(self):
140 141 """ 142 Internal method. Does constants initialization. 143 144 @return: None 145 @rtype: None 146 """ 147 148 del self.__setting_files_order[:] 149 del self.__setting_files_pre_run[:] 150 self.__setting_files.clear() 151 self.__mtime_files.clear() 152 153 self.__setting_files.update({ 154 # keywording configuration files 155 'keywords': etpConst['confpackagesdir']+"/package.keywords", 156 # unmasking configuration files 157 'unmask': etpConst['confpackagesdir']+"/package.unmask", 158 # masking configuration files 159 'mask': etpConst['confpackagesdir']+"/package.mask", 160 # satisfied packages configuration file 161 'satisfied': etpConst['confpackagesdir']+"/package.satisfied", 162 # masking configuration files 163 'license_mask': etpConst['confpackagesdir']+"/license.mask", 164 'system_mask': etpConst['confpackagesdir']+"/system.mask", 165 'system_dirs': etpConst['confdir']+"/fsdirs.conf", 166 'system_dirs_mask': etpConst['confdir']+"/fsdirsmask.conf", 167 'system_rev_symlinks': etpConst['confdir']+"/fssymlinks.conf", 168 'broken_syms': etpConst['confdir']+"/brokensyms.conf", 169 'broken_libs_mask': etpConst['confdir']+"/brokenlibsmask.conf", 170 'hw_hash': etpConst['confdir']+"/.hw.hash", 171 'socket_service': etpConst['socketconf'], 172 'system': etpConst['entropyconf'], 173 'repositories': etpConst['repositoriesconf'], 174 'system_package_sets': {}, 175 }) 176 self.__setting_files_order.extend([ 177 'keywords', 'unmask', 'mask', 'satisfied', 'license_mask', 178 'system_mask', 'system_package_sets', 'system_dirs', 179 'system_dirs_mask', 'socket_service', 'system', 180 'system_rev_symlinks', 'hw_hash', 'broken_syms', 'broken_libs_mask' 181 ]) 182 self.__setting_files_pre_run.extend(['repositories']) 183 184 dmp_dir = etpConst['dumpstoragedir'] 185 self.__mtime_files.update({ 186 'keywords_mtime': dmp_dir+"/keywords.mtime", 187 'unmask_mtime': dmp_dir+"/unmask.mtime", 188 'mask_mtime': dmp_dir+"/mask.mtime", 189 'satisfied_mtime': dmp_dir+"/satisfied.mtime", 190 'license_mask_mtime': dmp_dir+"/license_mask.mtime", 191 'system_mask_mtime': dmp_dir+"/system_mask.mtime", 192 })
193 194
195 - def __scan(self):
196 197 """ 198 Internal method. Scan settings and fill variables. 199 200 @return: None 201 @rtype: None 202 """ 203 204 def enforce_persistent(): 205 # merge persistent settings back 206 self.__data.update(self.__persistent_settings) 207 # restore backed-up settings 208 self.__data.update(self.__persistent_settings['backed_up'].copy())
209 210 self.__parse() 211 enforce_persistent() 212 213 # plugins support 214 local_plugins = self.get_plugins() 215 for plugin_id in sorted(local_plugins): 216 local_plugins[plugin_id].parse(self) 217 218 # external plugins support 219 external_plugins = self.__get_external_plugins() 220 for external_plugin_id in sorted(external_plugins): 221 external_plugin = external_plugins[external_plugin_id]() 222 external_plugin.parse(self) 223 self.__external_plugins[external_plugin_id] = external_plugin 224 225 enforce_persistent() 226 227 # run post-SystemSettings setup, plugins hook 228 for plugin_id in sorted(local_plugins): 229 local_plugins[plugin_id].post_setup(self) 230 231 # run post-SystemSettings setup for external plugins too 232 for external_plugin_id in sorted(self.__external_plugins): 233 self.__external_plugins[external_plugin_id].post_setup(self)
234
235 - def __setitem__(self, mykey, myvalue):
236 """ 237 dict method. See Python dict API reference. 238 """ 239 with self.__mutex: 240 # backup here too 241 if mykey in self.__persistent_settings: 242 self.__persistent_settings[mykey] = myvalue 243 self.__data[mykey] = myvalue
244
245 - def __getitem__(self, mykey):
246 """ 247 dict method. See Python dict API reference. 248 """ 249 with self.__mutex: 250 return self.__data[mykey]
251
252 - def __delitem__(self, mykey):
253 """ 254 dict method. See Python dict API reference. 255 """ 256 with self.__mutex: 257 del self.__data[mykey]
258
259 - def __iter__(self):
260 """ 261 dict method. See Python dict API reference. 262 """ 263 with self.__mutex: 264 return iter(self.__data)
265
266 - def __contains__(self, item):
267 """ 268 dict method. See Python dict API reference. 269 """ 270 with self.__mutex: 271 return item in self.__data
272
273 - def __hash__(self):
274 """ 275 dict method. See Python dict API reference. 276 """ 277 with self.__mutex: 278 return hash(self.__data)
279
280 - def __len__(self):
281 """ 282 dict method. See Python dict API reference. 283 """ 284 with self.__mutex: 285 return len(self.__data)
286
287 - def get(self, mykey, alt_obj = None):
288 """ 289 dict method. See Python dict API reference. 290 """ 291 with self.__mutex: 292 return self.__data.get(mykey, alt_obj)
293
294 - def copy(self):
295 """ 296 dict method. See Python dict API reference. 297 """ 298 with self.__mutex: 299 return self.__data.copy()
300
301 - def fromkeys(self, seq, val = None):
302 """ 303 dict method. See Python dict API reference. 304 """ 305 with self.__mutex: 306 return self.__data.fromkeys(seq, val)
307
308 - def items(self):
309 """ 310 dict method. See Python dict API reference. 311 """ 312 with self.__mutex: 313 return self.__data.items()
314
315 - def iteritems(self):
316 """ 317 dict method. See Python dict API reference. 318 """ 319 with self.__mutex: 320 return self.__data.iteritems()
321
322 - def iterkeys(self):
323 """ 324 dict method. See Python dict API reference. 325 """ 326 with self.__mutex: 327 return self.__data.iterkeys()
328
329 - def keys(self):
330 """ 331 dict method. See Python dict API reference. 332 """ 333 with self.__mutex: 334 return self.__data.keys()
335
336 - def pop(self, mykey, default = None):
337 """ 338 dict method. See Python dict API reference. 339 """ 340 with self.__mutex: 341 return self.__data.pop(mykey, default)
342
343 - def popitem(self):
344 """ 345 dict method. See Python dict API reference. 346 """ 347 with self.__mutex: 348 return self.__data.popitem()
349
350 - def setdefault(self, mykey, default = None):
351 """ 352 dict method. See Python dict API reference. 353 """ 354 with self.__mutex: 355 return self.__data.setdefault(mykey, default)
356
357 - def update(self, kwargs):
358 """ 359 dict method. See Python dict API reference. 360 """ 361 with self.__mutex: 362 return self.__data.update(kwargs)
363
364 - def values(self):
365 """ 366 dict method. See Python dict API reference. 367 """ 368 with self.__mutex: 369 return self.__data.values()
370
371 - def clear(self):
372 """ 373 dict method. See Python dict API reference. 374 Settings are also re-initialized here. 375 376 @return None 377 """ 378 with self.__mutex: 379 self.__data.clear() 380 self.__setup_const() 381 self.__scan()
382
383 - def set_persistent_setting(self, persistent_dict):
384 """ 385 Make metadata persistent, the input dict will be merged 386 with the base one at every reset call (clear()). 387 388 @param persistent_dict: dictionary to merge 389 @type persistent_dict: dict 390 391 @return: None 392 @rtype: None 393 """ 394 with self.__mutex: 395 self.__persistent_settings.update(persistent_dict)
396
397 - def unset_persistent_setting(self, persistent_key):
398 """ 399 Remove dict key from persistent dictionary 400 401 @param persistent_key: key to remove 402 @type persistent_dict: dict 403 404 @return: None 405 @rtype: None 406 """ 407 with self.__mutex: 408 del self.__persistent_settings[persistent_key] 409 del self.__data[persistent_key]
410
411 - def __setup_package_sets_vars(self):
412 413 """ 414 This function setups the *files* dictionary about package sets 415 that will be read and parsed afterwards by the respective 416 internal parser. 417 418 @return: None 419 @rtype: None 420 """ 421 422 # user defined package sets 423 sets_dir = etpConst['confsetsdir'] 424 pkg_set_data = {} 425 if (os.path.isdir(sets_dir) and os.access(sets_dir, os.R_OK)): 426 set_files = [x for x in os.listdir(sets_dir) if \ 427 (os.path.isfile(os.path.join(sets_dir, x)) and \ 428 os.access(os.path.join(sets_dir, x), os.R_OK))] 429 for set_file in set_files: 430 try: 431 set_file = const_convert_to_unicode(set_file, 'utf-8') 432 except UnicodeDecodeError: 433 set_file = const_convert_to_unicode(set_file, 434 sys.getfilesystemencoding()) 435 436 path = os.path.join(sets_dir, set_file) 437 if sys.hexversion < 0x3000000: 438 if const_isunicode(path): 439 path = const_convert_to_rawstring(path, 'utf-8') 440 pkg_set_data[set_file] = path 441 442 self.__setting_files['system_package_sets'].update(pkg_set_data)
443
444 - def __parse(self):
445 """ 446 This is the main internal parsing method. 447 *files* and *mtimes* dictionaries are prepared and 448 parsed just a few lines later. 449 450 @return: None 451 @rtype: None 452 """ 453 # some parsers must be run BEFORE everything: 454 for item in self.__setting_files_pre_run: 455 myattr = '_%s_parser' % (item,) 456 if not hasattr(self, myattr): 457 continue 458 func = getattr(self, myattr) 459 self.__data[item] = func() 460 461 # parse main settings 462 self.__setup_package_sets_vars() 463 464 for item in self.__setting_files_order: 465 myattr = '_%s_parser' % (item,) 466 if not hasattr(self, myattr): 467 continue 468 func = getattr(self, myattr) 469 self.__data[item] = func()
470
471 - def get_setting_files_data(self):
472 """ 473 Return a copy of the internal *files* dictionary. 474 This dict contains config file paths and their identifiers. 475 476 @return: dict __setting_files 477 @rtype: dict 478 """ 479 with self.__mutex: 480 return self.__setting_files.copy()
481
482 - def _keywords_parser(self):
483 """ 484 Parser returning package keyword masking metadata 485 read from package.keywords file. 486 This file contains package mask or unmask directives 487 based on package keywords. 488 489 @return: parsed metadata 490 @rtype: dict 491 """ 492 # merge universal keywords 493 data = { 494 'universal': set(), 495 'packages': {}, 496 'repositories': {}, 497 } 498 499 self.validate_entropy_cache(self.__setting_files['keywords'], 500 self.__mtime_files['keywords_mtime']) 501 content = [x.split() for x in \ 502 self.__generic_parser(self.__setting_files['keywords']) \ 503 if len(x.split()) < 4] 504 for keywordinfo in content: 505 # skip wrong lines 506 if len(keywordinfo) > 3: 507 continue 508 # inversal keywording, check if it's not repo= 509 if len(keywordinfo) == 1: 510 if keywordinfo[0].startswith("repo="): 511 continue 512 # convert into entropy format 513 if keywordinfo[0] == "**": 514 keywordinfo[0] = "" 515 data['universal'].add(keywordinfo[0]) 516 continue 517 # inversal keywording, check if it's not repo= 518 if len(keywordinfo) in (2, 3,): 519 # repo=? 520 if keywordinfo[0].startswith("repo="): 521 continue 522 # add to repo? 523 items = keywordinfo[1:] 524 # convert into entropy format 525 if keywordinfo[0] == "**": 526 keywordinfo[0] = "" 527 reponame = [x for x in items if x.startswith("repo=") \ 528 and (len(x.split("=")) == 2)] 529 if reponame: 530 reponame = reponame[0].split("=")[1] 531 if reponame not in data['repositories']: 532 data['repositories'][reponame] = {} 533 # repository unmask or package in repository unmask? 534 if keywordinfo[0] not in data['repositories'][reponame]: 535 data['repositories'][reponame][keywordinfo[0]] = set() 536 if len(items) == 1: 537 # repository unmask 538 data['repositories'][reponame][keywordinfo[0]].add('*') 539 elif "*" not in \ 540 data['repositories'][reponame][keywordinfo[0]]: 541 542 item = [x for x in items if not x.startswith("repo=")] 543 data['repositories'][reponame][keywordinfo[0]].add( 544 item[0]) 545 elif len(items) == 2: 546 # it's going to be a faulty line!!?? 547 # can't have two items and no repo= 548 continue 549 else: 550 # add keyword to packages 551 if keywordinfo[0] not in data['packages']: 552 data['packages'][keywordinfo[0]] = set() 553 data['packages'][keywordinfo[0]].add(items[0]) 554 555 # merge universal keywords 556 etpConst['keywords'].clear() 557 etpConst['keywords'].update(etpSys['keywords']) 558 for keyword in data['universal']: 559 etpConst['keywords'].add(keyword) 560 561 return data
562 563
564 - def _unmask_parser(self):
565 """ 566 Parser returning package unmasking metadata read from 567 package.unmask file. 568 This file contains package unmask directives, allowing 569 to enable experimental or *secret* packages. 570 571 @return: parsed metadata 572 @rtype: dict 573 """ 574 self.validate_entropy_cache(self.__setting_files['unmask'], 575 self.__mtime_files['unmask_mtime']) 576 return self.__generic_parser(self.__setting_files['unmask'])
577
578 - def _mask_parser(self):
579 """ 580 Parser returning package masking metadata read from 581 package.mask file. 582 This file contains package mask directives, allowing 583 to disable experimental or *secret* packages. 584 585 @return: parsed metadata 586 @rtype: dict 587 """ 588 self.validate_entropy_cache(self.__setting_files['mask'], 589 self.__mtime_files['mask_mtime']) 590 return self.__generic_parser(self.__setting_files['mask'])
591
592 - def _satisfied_parser(self):
593 """ 594 Parser returning package forced satisfaction metadata 595 read from package.satisfied file. 596 This file contains packages which updates as dependency are 597 filtered out. 598 599 @return: parsed metadata 600 @rtype: dict 601 """ 602 self.validate_entropy_cache(self.__setting_files['satisfied'], 603 self.__mtime_files['satisfied_mtime']) 604 return self.__generic_parser(self.__setting_files['satisfied'])
605
606 - def _system_mask_parser(self):
607 """ 608 Parser returning system packages mask metadata read from 609 package.system_mask file. 610 This file contains packages that should be always kept 611 installed, extending the already defined (in repository database) 612 set of atoms. 613 614 @return: parsed metadata 615 @rtype: dict 616 """ 617 self.validate_entropy_cache(self.__setting_files['system_mask'], 618 self.__mtime_files['system_mask_mtime']) 619 return self.__generic_parser(self.__setting_files['system_mask'])
620
621 - def _license_mask_parser(self):
622 """ 623 Parser returning packages masked by license metadata read from 624 license.mask file. 625 Packages shipped with licenses listed there will be masked. 626 627 @return: parsed metadata 628 @rtype: dict 629 """ 630 self.validate_entropy_cache(self.__setting_files['license_mask'], 631 self.__mtime_files['license_mask_mtime']) 632 return self.__generic_parser(self.__setting_files['license_mask'])
633
634 - def _system_package_sets_parser(self):
635 """ 636 Parser returning system defined package sets read from 637 /etc/entropy/packages/sets. 638 639 @return: parsed metadata 640 @rtype: dict 641 """ 642 data = {} 643 for set_name in self.__setting_files['system_package_sets']: 644 set_filepath = self.__setting_files['system_package_sets'][set_name] 645 set_elements = self.entropyTools.extract_packages_from_set_file( 646 set_filepath) 647 if set_elements: 648 data[set_name] = set_elements.copy() 649 return data
650
651 - def _system_dirs_parser(self):
652 """ 653 Parser returning directories considered part of the base system. 654 655 @return: parsed metadata 656 @rtype: dict 657 """ 658 return self.__generic_parser(self.__setting_files['system_dirs'])
659
660 - def _system_dirs_mask_parser(self):
661 """ 662 Parser returning directories NOT considered part of the base system. 663 Settings here overlay system_dirs_parser. 664 665 @return: parsed metadata 666 @rtype: dict 667 """ 668 return self.__generic_parser(self.__setting_files['system_dirs_mask'])
669
670 - def _broken_syms_parser(self):
671 """ 672 Parser returning a list of shared objects symbols that can be used by 673 QA tools to scan the filesystem or a subset of it. 674 675 @return: parsed metadata 676 @rtype: dict 677 """ 678 return self.__generic_parser(self.__setting_files['broken_syms'])
679
680 - def _broken_libs_mask_parser(self):
681 """ 682 Parser returning a list of broken shared libraries which are 683 always considered sane. 684 685 @return: parsed metadata 686 @rtype: dict 687 """ 688 return self.__generic_parser(self.__setting_files['broken_libs_mask'])
689
690 - def _hw_hash_parser(self):
691 """ 692 Hardware hash metadata parser and generator. It returns a theorically 693 unique SHA256 hash bound to the computer running this Framework. 694 695 @return: string containing SHA256 hexdigest 696 @rtype: string 697 """ 698 hw_hash_file = self.__setting_files['hw_hash'] 699 if os.access(hw_hash_file, os.R_OK) and os.path.isfile(hw_hash_file): 700 hash_f = open(hw_hash_file, "r") 701 hash_data = hash_f.readline().strip() 702 hash_f.close() 703 return hash_data 704 705 hash_file_dir = os.path.dirname(hw_hash_file) 706 hw_hash_exec = etpConst['etp_hw_hash_gen'] 707 if os.access(hash_file_dir, os.W_OK) and \ 708 os.access(hw_hash_exec, os.X_OK | os.R_OK) and \ 709 os.path.isfile(hw_hash_exec): 710 711 pipe = os.popen('{ ' + hw_hash_exec + '; } 2>&1', 'r') 712 hash_data = pipe.read().strip() 713 sts = pipe.close() 714 if sts != None: 715 return None 716 hash_f = open(hw_hash_file, "w") 717 hash_f.write(hash_data) 718 hash_f.flush() 719 hash_f.close() 720 return hash_data
721 740
741 - def _socket_service_parser(self):
742 """ 743 Parses socket service configuration file. 744 This file contains information about Entropy remote service ports 745 and SSL. 746 747 @return: parsed metadata 748 @rtype: dict 749 """ 750 751 data = etpConst['socket_service'].copy() 752 753 sock_conf = self.__setting_files['socket_service'] 754 if not (os.path.isfile(sock_conf) and \ 755 os.access(sock_conf, os.R_OK)): 756 return data 757 758 socket_f = open(sock_conf, "r") 759 socketconf = [x.strip() for x in socket_f.readlines() if \ 760 x.strip() and not x.strip().startswith("#")] 761 socket_f.close() 762 763 for line in socketconf: 764 765 split_line = line.split("|") 766 split_line_len = len(split_line) 767 768 if line.startswith("listen|") and (split_line_len > 1): 769 770 item = split_line[1].strip() 771 if item: 772 data['hostname'] = item 773 774 elif line.startswith("listen-port|") and \ 775 (split_line_len > 1): 776 777 item = split_line[1].strip() 778 try: 779 item = int(item) 780 data['port'] = item 781 except ValueError: 782 continue 783 784 elif line.startswith("listen-timeout|") and \ 785 (split_line_len > 1): 786 787 item = split_line[1].strip() 788 try: 789 item = int(item) 790 data['timeout'] = item 791 except ValueError: 792 continue 793 794 elif line.startswith("listen-threads|") and \ 795 (split_line_len > 1): 796 797 item = split_line[1].strip() 798 try: 799 item = int(item) 800 data['threads'] = item 801 except ValueError: 802 continue 803 804 elif line.startswith("session-ttl|") and \ 805 (split_line_len > 1): 806 807 item = split_line[1].strip() 808 try: 809 item = int(item) 810 data['session_ttl'] = item 811 except ValueError: 812 continue 813 814 elif line.startswith("max-connections|") and \ 815 (split_line_len > 1): 816 817 item = split_line[1].strip() 818 try: 819 item = int(item) 820 data['max_connections'] = item 821 except ValueError: 822 continue 823 824 elif line.startswith("ssl-port|") and \ 825 (split_line_len > 1): 826 827 item = split_line[1].strip() 828 try: 829 item = int(item) 830 data['ssl_port'] = item 831 except ValueError: 832 continue 833 834 elif line.startswith("disabled-commands|") and \ 835 (split_line_len > 1): 836 837 disabled_cmds = split_line[1].strip().split() 838 for disabled_cmd in disabled_cmds: 839 data['disabled_cmds'].add(disabled_cmd) 840 841 elif line.startswith("ip-blacklist|") and \ 842 (split_line_len > 1): 843 844 ips_blacklist = split_line[1].strip().split() 845 for ip_blacklist in ips_blacklist: 846 data['ip_blacklist'].add(ip_blacklist) 847 848 return data
849
850 - def _system_parser(self):
851 852 """ 853 Parses Entropy system configuration file. 854 855 @return: parsed metadata 856 @rtype: dict 857 """ 858 859 data = { 860 'proxy': etpConst['proxy'].copy(), 861 'name': etpConst['systemname'], 862 'log_level': etpConst['entropyloglevel'], 863 'spm_backend': None, 864 } 865 866 etp_conf = self.__setting_files['system'] 867 if not (os.path.isfile(etp_conf) and \ 868 os.access(etp_conf, os.R_OK)): 869 return data 870 871 const_secure_config_file(etp_conf) 872 entropy_f = open(etp_conf, "r") 873 entropyconf = [x.strip() for x in entropy_f.readlines() if \ 874 x.strip() and not x.strip().startswith("#")] 875 entropy_f.close() 876 877 for line in entropyconf: 878 879 split_line = line.split("|") 880 split_line_len = len(split_line) 881 882 if line.startswith("loglevel|") and \ 883 (len(line.split("loglevel|")) == 2): 884 885 loglevel = line.split("loglevel|")[1] 886 try: 887 loglevel = int(loglevel) 888 except ValueError: 889 pass 890 if (loglevel > -1) and (loglevel < 3): 891 data['log_level'] = loglevel 892 893 elif line.startswith("ftp-proxy|") and \ 894 (split_line_len == 2): 895 896 ftpproxy = split_line[1].strip().split() 897 if ftpproxy: 898 data['proxy']['ftp'] = ftpproxy[-1] 899 900 elif line.startswith("http-proxy|") and \ 901 (split_line_len == 2): 902 903 httpproxy = split_line[1].strip().split() 904 if httpproxy: 905 data['proxy']['http'] = httpproxy[-1] 906 907 elif line.startswith("proxy-username|") and \ 908 (split_line_len == 2): 909 910 httpproxy = split_line[1].strip().split() 911 if httpproxy: 912 data['proxy']['username'] = httpproxy[-1] 913 914 elif line.startswith("proxy-password|") and \ 915 (split_line_len == 2): 916 917 httpproxy = split_line[1].strip().split() 918 if httpproxy: 919 data['proxy']['password'] = httpproxy[-1] 920 921 elif line.startswith("system-name|") and \ 922 (split_line_len == 2): 923 924 data['name'] = split_line[1].strip() 925 926 elif line.startswith("spm-backend|") and \ 927 (split_line_len == 2): 928 929 data['spm_backend'] = split_line[1].strip() 930 931 elif line.startswith("nice-level|") and \ 932 (split_line_len == 2): 933 934 mylevel = split_line[1].strip() 935 try: 936 mylevel = int(mylevel) 937 if (mylevel >= -19) and (mylevel <= 19): 938 const_set_nice_level(mylevel) 939 except (ValueError,): 940 continue 941 942 return data
943
944 - def _repositories_parser(self):
945 946 """ 947 Setup Entropy Client repository settings reading them from 948 the relative config file specified in etpConst['repositoriesconf'] 949 950 @return: parsed metadata 951 @rtype: dict 952 """ 953 954 data = { 955 'available': {}, 956 'excluded': {}, 957 'order': [], 958 'product': etpConst['product'], 959 'branch': etpConst['branch'], 960 'default_repository': etpConst['officialrepositoryid'], 961 'transfer_limit': etpConst['downloadspeedlimit'], 962 'timeout': etpConst['default_download_timeout'], 963 'security_advisories_url': etpConst['securityurl'], 964 } 965 966 repo_conf = etpConst['repositoriesconf'] 967 if not (os.path.isfile(repo_conf) and os.access(repo_conf, os.R_OK)): 968 return data 969 970 repo_f = open(repo_conf, "r") 971 repositoriesconf = [x.strip() for x in repo_f.readlines() if x.strip()] 972 repo_f.close() 973 974 # setup product and branch first 975 for line in repositoriesconf: 976 977 split_line = line.split("|") 978 split_line_len = len(split_line) 979 980 if (line.find("product|") != -1) and \ 981 (not line.startswith("#")) and (split_line_len == 2): 982 983 data['product'] = split_line[1] 984 985 elif (line.find("branch|") != -1) and \ 986 (not line.startswith("#")) and (split_line_len == 2): 987 988 branch = split_line[1].strip() 989 data['branch'] = branch 990 if not os.path.isdir(etpConst['packagesbindir']+"/"+branch) \ 991 and (etpConst['uid'] == 0): 992 993 try: 994 os.makedirs(etpConst['packagesbindir']+"/"+branch) 995 except (OSError, IOError,): 996 continue 997 998 for line in repositoriesconf: 999 1000 split_line = line.split("|") 1001 split_line_len = len(split_line) 1002 1003 # populate data['available'] 1004 if (line.find("repository|") != -1) and (split_line_len == 5): 1005 1006 excluded = False 1007 my_repodata = data['available'] 1008 if line.startswith("##"): 1009 continue 1010 elif line.startswith("#"): 1011 excluded = True 1012 my_repodata = data['excluded'] 1013 line = line[1:] 1014 1015 reponame, repodata = const_extract_cli_repo_params(line, 1016 data['branch'], data['product']) 1017 if reponame in my_repodata: 1018 1019 my_repodata[reponame]['plain_packages'].extend( 1020 repodata['plain_packages']) 1021 my_repodata[reponame]['packages'].extend( 1022 repodata['packages']) 1023 1024 if (not my_repodata[reponame]['plain_database']) and \ 1025 repodata['plain_database']: 1026 1027 my_repodata[reponame]['plain_database'] = \ 1028 repodata['plain_database'] 1029 my_repodata[reponame]['database'] = \ 1030 repodata['database'] 1031 my_repodata[reponame]['dbrevision'] = \ 1032 repodata['dbrevision'] 1033 my_repodata[reponame]['dbcformat'] = \ 1034 repodata['dbcformat'] 1035 else: 1036 1037 my_repodata[reponame] = repodata.copy() 1038 if not excluded: 1039 data['order'].append(reponame) 1040 1041 elif (line.find("officialrepositoryid|") != -1) and \ 1042 (not line.startswith("#")) and (split_line_len == 2): 1043 1044 officialreponame = split_line[1] 1045 data['default_repository'] = officialreponame 1046 1047 elif (line.find("downloadspeedlimit|") != -1) and \ 1048 (not line.startswith("#")) and (split_line_len == 2): 1049 1050 try: 1051 myval = int(split_line[1]) 1052 if myval > 0: 1053 data['transfer_limit'] = myval 1054 else: 1055 data['transfer_limit'] = None 1056 except (ValueError, IndexError,): 1057 data['transfer_limit'] = None 1058 1059 elif (line.find("downloadtimeout|") != -1) and \ 1060 (not line.startswith("#")) and (split_line_len == 2): 1061 1062 try: 1063 myval = int(split_line[1]) 1064 data['timeout'] = myval 1065 except (ValueError, IndexError,): 1066 continue 1067 1068 elif (line.find("securityurl|") != -1) and \ 1069 (not line.startswith("#")) and (split_line_len == 2): 1070 1071 try: 1072 data['security_advisories_url'] = split_line[1] 1073 except (IndexError, ValueError, TypeError,): 1074 continue 1075 1076 try: 1077 tx_limit = int(os.getenv("ETP_DOWNLOAD_KB")) 1078 except (ValueError, TypeError,): 1079 tx_limit = None 1080 if tx_limit is not None: 1081 data['transfer_limit'] = tx_limit 1082 1083 return data
1084
1085 - def _clear_repository_cache(self, repoid = None):
1086 """ 1087 Internal method, go away! 1088 """ 1089 self.__cacher.discard() 1090 self._clear_dump_cache(etpCache['world_available']) 1091 self._clear_dump_cache(etpCache['world_update']) 1092 self._clear_dump_cache(etpCache['critical_update']) 1093 self._clear_dump_cache(etpCache['check_package_update']) 1094 self._clear_dump_cache(etpCache['filter_satisfied_deps']) 1095 self._clear_dump_cache(etpCache['atomMatch']) 1096 self._clear_dump_cache(etpCache['dep_tree']) 1097 if repoid != None: 1098 self._clear_dump_cache("%s/%s%s/" % ( 1099 etpCache['dbMatch'], etpConst['dbnamerepoprefix'], repoid,)) 1100 self._clear_dump_cache("%s/%s%s/" % ( 1101 etpCache['dbSearch'], etpConst['dbnamerepoprefix'], repoid,))
1102
1103 - def _clear_dump_cache(self, dump_name, skip = []):
1104 """ 1105 Internal method, go away! 1106 """ 1107 dump_path = os.path.join(etpConst['dumpstoragedir'], dump_name) 1108 dump_dir = os.path.dirname(dump_path) 1109 #dump_file = os.path.basename(dump_path) 1110 for currentdir, subdirs, files in os.walk(dump_dir): 1111 path = os.path.join(dump_dir, currentdir) 1112 if skip: 1113 found = False 1114 for myskip in skip: 1115 if path.find(myskip) != -1: 1116 found = True 1117 break 1118 if found: continue 1119 for item in files: 1120 if item.endswith(etpConst['cachedumpext']): 1121 item = os.path.join(path, item) 1122 try: 1123 os.remove(item) 1124 except (OSError, IOError,): 1125 pass 1126 try: 1127 if not os.listdir(path): 1128 os.rmdir(path) 1129 except (OSError, IOError,): 1130 pass
1131
1132 - def __generic_parser(self, filepath):
1133 """ 1134 Internal method. This is the generic file parser here. 1135 1136 @param filepath: valid path 1137 @type filepath: string 1138 @return: raw text extracted from file 1139 @rtype: list 1140 """ 1141 return self.entropyTools.generic_file_content_parser(filepath)
1142
1143 - def __remove_repo_cache(self, repoid = None):
1144 """ 1145 Internal method. Remove repository cache, because not valid anymore. 1146 1147 @keyword repoid: repository identifier or None 1148 @type repoid: string or None 1149 @return: None 1150 @rtype: None 1151 """ 1152 if os.path.isdir(etpConst['dumpstoragedir']): 1153 if repoid: 1154 self._clear_repository_cache(repoid = repoid) 1155 return 1156 for repoid in self['repositories']['order']: 1157 self._clear_repository_cache(repoid = repoid) 1158 else: 1159 try: 1160 os.makedirs(etpConst['dumpstoragedir']) 1161 except IOError as e: 1162 if e.errno == 30: # readonly filesystem 1163 etpUi['pretend'] = True 1164 return 1165 except OSError: 1166 return
1167
1168 - def __save_file_mtime(self, toread, tosaveinto):
1169 """ 1170 Internal method. Save mtime of a file to another file. 1171 1172 @param toread: file path to read 1173 @type toread: string 1174 @param tosaveinto: path where to save retrieved mtime information 1175 @type tosaveinto: string 1176 @return: None 1177 @rtype: None 1178 """ 1179 if not os.path.isfile(toread): 1180 currmtime = 0.0 1181 else: 1182 currmtime = os.path.getmtime(toread) 1183 1184 if not os.path.isdir(etpConst['dumpstoragedir']): 1185 try: 1186 os.makedirs(etpConst['dumpstoragedir'], 0o775) 1187 const_setup_perms(etpConst['dumpstoragedir'], 1188 etpConst['entropygid']) 1189 except IOError as e: 1190 if e.errno == 30: # readonly filesystem 1191 etpUi['pretend'] = True 1192 return 1193 except (OSError,) as e: 1194 # unable to create the storage directory 1195 # useless to continue 1196 return 1197 1198 try: 1199 mtime_f = open(tosaveinto, "w") 1200 except IOError as e: # unable to write? 1201 if e.errno == 30: # readonly filesystem 1202 etpUi['pretend'] = True 1203 return 1204 else: 1205 mtime_f.write(str(currmtime)) 1206 mtime_f.flush() 1207 mtime_f.close() 1208 os.chmod(tosaveinto, 0o664) 1209 if etpConst['entropygid'] != None: 1210 os.chown(tosaveinto, 0, etpConst['entropygid'])
1211 1212
1213 - def validate_entropy_cache(self, settingfile, mtimefile, repoid = None):
1214 """ 1215 Internal method. Validates Entropy Cache based on a setting file 1216 and its stored (cache bound) mtime. 1217 1218 @param settingfile: path of the setting file 1219 @type settingfile: string 1220 @param mtimefile: path where to save retrieved mtime information 1221 @type mtimefile: string 1222 @keyword repoid: repository identifier or None 1223 @type repoid: string or None 1224 @return: None 1225 @rtype: None 1226 """ 1227 1228 # can't validate if running as user, moreover 1229 # users can't make changes, so... 1230 if os.getuid() != 0: 1231 return 1232 1233 # handle on-disk cache validation 1234 # in this case, repositories cache 1235 # if file is changed, we must destroy cache 1236 if not os.path.isfile(mtimefile): 1237 # we can't know if it has been updated 1238 # remove repositories caches 1239 self.__remove_repo_cache(repoid = repoid) 1240 self.__save_file_mtime(settingfile, mtimefile) 1241 else: 1242 # check mtime 1243 try: 1244 mtime_f = open(mtimefile, "r") 1245 mtime = mtime_f.readline().strip() 1246 mtime_f.close() 1247 # compare with current mtime 1248 try: 1249 currmtime = str(os.path.getmtime(settingfile)) 1250 except OSError: 1251 currmtime = "0.0" 1252 if mtime != currmtime: 1253 self.__remove_repo_cache(repoid = repoid) 1254 self.__save_file_mtime(settingfile, mtimefile) 1255 except (OSError, IOError,): 1256 self.__remove_repo_cache(repoid = repoid) 1257 self.__save_file_mtime(settingfile, mtimefile)
1258