Package entropy :: Module core

Source Code for Module entropy.core

   1  # -*- coding: utf-8 -*- 
   2  ''' 
   3      # DESCRIPTION: 
   4      # Entropy Object Oriented Interface 
   5   
   6      Copyright (C) 2007-2009 Fabio Erculiani 
   7   
   8      This program is free software; you can redistribute it and/or modify 
   9      it under the terms of the GNU General Public License as published by 
  10      the Free Software Foundation; either version 2 of the License, or 
  11      (at your option) any later version. 
  12   
  13      This program is distributed in the hope that it will be useful, 
  14      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  15      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  16      GNU General Public License for more details. 
  17   
  18      You should have received a copy of the GNU General Public License 
  19      along with this program; if not, write to the Free Software 
  20      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  21  ''' 
  22  # pylint ~ ok 
  23  from __future__ import with_statement 
  24  import os 
  25  from entropy.exceptions import IncorrectParameter, SystemDatabaseError 
  26  from entropy.const import etpConst, etpUi, etpSys, const_setup_perms, \ 
  27      const_secure_config_file, const_set_nice_level, \ 
  28      const_extract_cli_repo_params, etpCache 
  29  from entropy.i18n import _ 
  30   
31 -class Singleton(object):
32 33 """ 34 If your class wants to become a sexy Singleton, 35 subclass this and replace __init__ with init_singleton 36 """ 37 38 __is_destroyed = False 39 __is_singleton = True
40 - def __new__(cls, *args, **kwds):
41 instance = cls.__dict__.get("__it__") 42 if instance != None: 43 if not instance.is_destroyed(): 44 return instance 45 cls.__it__ = instance = object.__new__(cls) 46 instance.init_singleton(*args, **kwds) 47 return instance
48
49 - def is_destroyed(self):
50 """ 51 In our world, Singleton instances may be destroyed, 52 this is done by setting a private bool var __is_destroyed 53 54 @rtype: bool 55 @return: instance status, if destroyed or not 56 """ 57 return self.__is_destroyed
58
59 - def is_singleton(self):
60 """ 61 Return if the instance is a singleton 62 63 @rtype: bool 64 @return: class singleton property, if singleton or not 65 """ 66 return self.__is_singleton
67
68 -class SystemSettingsPlugin:
69 70 """ 71 This is a plugin base class for all SystemSettings plugins. 72 It allows to add extra parsers (though metadata) to 73 SystemSettings. 74 Just inherit from this class and call add_parser to add 75 your custom parsers. 76 SystemSettings will call the parse method, as explained below. 77 """ 78
79 - def __init__(self, plugin_id, helper_interface):
80 """ 81 SystemSettingsPlugin constructor. 82 83 @param plugin_id -- plugin identifier, must be unique 84 @type plugin_id basestring 85 @param helper_interface -- any Python instance that could 86 be of help to your parsers 87 @type handler_instance instance 88 """ 89 self.__parsers = [] 90 self.__plugin_id = plugin_id 91 self._helper = helper_interface 92 parser_postfix = "_parser" 93 for method in sorted(dir(self)): 94 if method == "add_parser": 95 continue 96 elif method.endswith(parser_postfix) and (method != parser_postfix): 97 parser_id = method[:len(parser_postfix)*-1] 98 self.__parsers.append((parser_id, getattr(self, method),))
99
100 - def get_id(self):
101 """ 102 Returns the unique plugin id passed at construction time. 103 104 @return plugin identifier 105 """ 106 return self.__plugin_id
107
108 - def add_parser(self, parser_id, parser_callable):
109 """ 110 You must call this method in order to add your custom 111 parsers to the plugin. 112 Please note, if your parser method ends with "_parser" 113 it will be automatically added this way: 114 115 method: foo_parser 116 parser_id => foo 117 method: another_fabulous_parser 118 parser_id => another_fabulous 119 120 @param parser_id -- parser identifier, must be unique 121 @type parser_id basestring 122 @param parser_callable -- any callable function which has 123 the following signature: callable(system_settings_instance) 124 can return True to stop further parsers calls 125 @type parser_callable callable 126 """ 127 self.__parsers.append((parser_id, parser_callable,))
128
129 - def parse(self, system_settings_instance):
130 """ 131 This method is called by SystemSettings instance 132 when building its settings metadata. 133 134 Returned data from parser will be put into the SystemSettings 135 dict using plugin_id and parser_id keys. 136 If returned data is None, SystemSettings dict won't be changed. 137 138 @param system_settings_instance -- SystemSettings instance 139 @type system_settings_instance SystemSettings instance 140 """ 141 plugin_id = self.get_id() 142 for parser_id, parser in self.__parsers: 143 data = parser(system_settings_instance) 144 if data == None: 145 continue 146 if not system_settings_instance.has_key(plugin_id): 147 system_settings_instance[plugin_id] = {} 148 system_settings_instance[plugin_id][parser_id] = data
149
150 -class SystemSettings(Singleton):
151 152 """ 153 This is the place where all the Entropy settings are stored if 154 they are not considered instance constants (etpConst). 155 For example, here we store package masking cache information and 156 settings. 157 Also, this class mimics a dictionary (even if not inheriting it 158 due to issues with the Singleton class). 159 """ 160 161 import entropy.tools as entropyTools
162 - def init_singleton(self):
163 164 """ 165 Replaces __init__ because SystemSettings is a Singleton. 166 see Singleton API reference for more information. 167 168 """ 169 170 from entropy.cache import EntropyCacher 171 self.__cacher = EntropyCacher() 172 self.__data = {} 173 self.__is_destroyed = False 174 175 self.__plugins = {} 176 self.__setting_files_order = [] 177 self.__setting_files_pre_run = [] 178 self.__setting_files = {} 179 self.__mtime_files = {} 180 self.__persistent_settings = { 181 'pkg_masking_reasons': { 182 0: _('reason not available'), 183 1: _('user package.mask'), 184 2: _('system keywords'), 185 3: _('user package.unmask'), 186 4: _('user repo package.keywords (all packages)'), 187 5: _('user repo package.keywords'), 188 6: _('user package.keywords'), 189 7: _('completely masked'), 190 8: _('repository general packages.db.mask'), 191 10: _('user license.mask'), 192 11: _('user live unmask'), 193 12: _('user live mask'), 194 }, 195 'pkg_masking_reference': { 196 'reason_not_avail': 0, 197 'user_package_mask': 1, 198 'system_keyword': 2, 199 'user_package_unmask': 3, 200 'user_repo_package_keywords_all': 4, 201 'user_repo_package_keywords': 5, 202 'user_package_keywords': 6, 203 'completely_masked': 7, 204 'repository_packages_db_mask': 8, 205 'repository_in_branch_pacakges_db_mask': 9, 206 'user_license_mask': 10, 207 'user_live_unmask': 11, 208 'user_live_mask': 12, 209 }, 210 'backed_up': {}, 211 # package masking, live 212 'live_packagemasking': { 213 'unmask_matches': set(), 214 'mask_matches': set(), 215 }, 216 } 217 218 self.__setup_const() 219 self.__scan()
220
221 - def destroy(self):
222 """ 223 Overloaded method from Singleton. 224 "Destroys" the instance. 225 226 @return None 227 """ 228 self.__is_destroyed = True
229
230 - def add_plugin(self, system_settings_plugin_instance):
231 """ 232 This method lets you add custom parsers to SystemSettings. 233 Mind that you are responsible of handling your plugin instance 234 and remove it before it is destroyed. You can remove the plugin 235 instance at any time by issuing remove_plugin. 236 Every add_plugin or remove_plugin method will also issue clear() 237 for you. This could be bad and it might be removed in future. 238 239 @param plugin_id -- plugin identifier 240 @type plugin_id basestring 241 @param system_settings_plugin_instance -- valid SystemSettingsPlugin 242 instance 243 @type system_settings_plugin_instance SystemSettingsPlugin instance 244 """ 245 inst = system_settings_plugin_instance 246 if not isinstance(inst,SystemSettingsPlugin): 247 raise AttributeError("SystemSettings: expected valid " + \ 248 "SystemSettingsPlugin instance") 249 self.__plugins[inst.get_id()] = inst 250 self.clear()
251
252 - def remove_plugin(self, plugin_id):
253 """ 254 This method lets you remove previously added custom parsers from 255 SystemSettings through its plugin identifier. If plugin_id is not 256 available, KeyError exception will be raised. 257 Every add_plugin or remove_plugin method will also issue clear() 258 for you. This could be bad and it might be removed in future. 259 260 @param plugin_id -- plugin identifier 261 @type plugin_id basestring 262 """ 263 del self.__plugins[plugin_id] 264 self.clear()
265
266 - def __setup_const(self):
267 268 """ 269 Internal method. Does the constants initialization. 270 271 @return None 272 """ 273 274 del self.__setting_files_order[:] 275 del self.__setting_files_pre_run[:] 276 self.__setting_files.clear() 277 self.__mtime_files.clear() 278 279 self.__setting_files.update({ 280 # keywording configuration files 281 'keywords': etpConst['confpackagesdir']+"/package.keywords", 282 # unmasking configuration files 283 'unmask': etpConst['confpackagesdir']+"/package.unmask", 284 # masking configuration files 285 'mask': etpConst['confpackagesdir']+"/package.mask", 286 # masking configuration files 287 'license_mask': etpConst['confpackagesdir']+"/license.mask", 288 'repos_system_mask': {}, 289 'system_mask': etpConst['confpackagesdir']+"/system.mask", 290 'repos_mask': {}, 291 'repos_license_whitelist': {}, 292 'system_package_sets': {}, 293 'conflicting_tagged_packages': {}, 294 'system_dirs': etpConst['confdir']+"/fsdirs.conf", 295 'system_dirs_mask': etpConst['confdir']+"/fsdirsmask.conf", 296 'system_rev_symlinks': etpConst['confdir']+"/fssymlinks.conf", 297 'hw_hash': etpConst['confdir']+"/.hw.hash", 298 'socket_service': etpConst['socketconf'], 299 'system': etpConst['entropyconf'], 300 'repositories': etpConst['repositoriesconf'], 301 }) 302 self.__setting_files_order.extend([ 303 'keywords', 'unmask', 'mask', 'license_mask', 304 'repos_system_mask', 'system_mask', 'repos_mask', 305 'repos_license_whitelist', 'system_package_sets', 306 'conflicting_tagged_packages', 'system_dirs', 307 'system_dirs_mask', 'socket_service', 'system', 308 'system_rev_symlinks', 'hw_hash' 309 ]) 310 self.__setting_files_pre_run.extend(['repositories']) 311 312 ## XXX trunk support, for a while - exp. date 10/10/2009 313 trunk_fsdirs_conf = "../conf/fsdirs.conf" 314 trunk_fsdirsmask_conf = "../conf/fsdirsmask.conf" 315 trunk_fssymlinks_conf = "../conf/fssymlinks.conf" 316 if os.path.isfile(trunk_fsdirs_conf): 317 self.__setting_files['system_dirs'] = trunk_fsdirs_conf 318 if os.path.isfile(trunk_fsdirsmask_conf): 319 self.__setting_files['system_dirs_mask'] = trunk_fsdirsmask_conf 320 if os.path.isfile(trunk_fssymlinks_conf): 321 self.__setting_files['system_rev_symlinks'] = trunk_fssymlinks_conf 322 323 dmp_dir = etpConst['dumpstoragedir'] 324 self.__mtime_files.update({ 325 'keywords_mtime': dmp_dir+"/keywords.mtime", 326 'unmask_mtime': dmp_dir+"/unmask.mtime", 327 'mask_mtime': dmp_dir+"/mask.mtime", 328 'license_mask_mtime': dmp_dir+"/license_mask.mtime", 329 'system_mask_mtime': dmp_dir+"/system_mask.mtime", 330 'repos_system_mask': {}, 331 'repos_mask': {}, 332 'repos_license_whitelist': {}, 333 })
334 335
336 - def __scan(self):
337 338 """ 339 Internal method. Scan settings and fill variables. 340 341 @return None 342 """ 343 344 self.__parse() 345 # merge universal keywords 346 for keyword in self.__data['keywords']['universal']: 347 etpConst['keywords'].add(keyword) 348 349 # plugins support 350 for plugin_id in sorted(self.__plugins): 351 self.__plugins[plugin_id].parse(self) 352 353 # merge persistent settings back 354 self.__data.update(self.__persistent_settings) 355 # restore backed-up settings 356 self.__data.update(self.__persistent_settings['backed_up'].copy())
357
358 - def __setitem__(self, mykey, myvalue):
359 """ 360 dict method. See Python dict API reference. 361 """ 362 if self.__persistent_settings.has_key(mykey): # backup here too 363 self.__persistent_settings[mykey] = myvalue 364 self.__data[mykey] = myvalue
365
366 - def __getitem__(self, mykey):
367 """ 368 dict method. See Python dict API reference. 369 """ 370 return self.__data[mykey]
371
372 - def __delitem__(self, mykey):
373 """ 374 dict method. See Python dict API reference. 375 """ 376 del self.__data[mykey]
377
378 - def __iter__(self):
379 """ 380 dict method. See Python dict API reference. 381 """ 382 return iter(self.__data)
383
384 - def __contains__(self, item):
385 """ 386 dict method. See Python dict API reference. 387 """ 388 return item in self.__data
389
390 - def __cmp__(self, other):
391 """ 392 dict method. See Python dict API reference. 393 """ 394 return cmp(self.__data, other)
395
396 - def __hash__(self):
397 """ 398 dict method. See Python dict API reference. 399 """ 400 return hash(self.__data)
401
402 - def __len__(self):
403 """ 404 dict method. See Python dict API reference. 405 """ 406 return len(self.__data)
407
408 - def get(self, mykey):
409 """ 410 dict method. See Python dict API reference. 411 """ 412 return self.__data.get(mykey)
413
414 - def has_key(self, mykey):
415 """ 416 dict method. See Python dict API reference. 417 """ 418 return self.__data.has_key(mykey)
419
420 - def copy(self):
421 """ 422 dict method. See Python dict API reference. 423 """ 424 return self.__data.copy()
425
426 - def fromkeys(self, seq, val = None):
427 """ 428 dict method. See Python dict API reference. 429 """ 430 return self.__data.fromkeys(seq, val)
431
432 - def items(self):
433 """ 434 dict method. See Python dict API reference. 435 """ 436 return self.__data.items()
437
438 - def iteritems(self):
439 """ 440 dict method. See Python dict API reference. 441 """ 442 return self.__data.iteritems()
443
444 - def iterkeys(self):
445 """ 446 dict method. See Python dict API reference. 447 """ 448 return self.__data.iterkeys()
449
450 - def keys(self):
451 """ 452 dict method. See Python dict API reference. 453 """ 454 return self.__data.keys()
455
456 - def pop(self, mykey, default = None):
457 """ 458 dict method. See Python dict API reference. 459 """ 460 return self.__data.pop(mykey, default)
461
462 - def popitem(self):
463 """ 464 dict method. See Python dict API reference. 465 """ 466 return self.__data.popitem()
467
468 - def setdefault(self, mykey, default = None):
469 """ 470 dict method. See Python dict API reference. 471 """ 472 return self.__data.setdefault(mykey, default)
473
474 - def update(self, kwargs):
475 """ 476 dict method. See Python dict API reference. 477 """ 478 return self.__data.update(kwargs)
479
480 - def values(self):
481 """ 482 dict method. See Python dict API reference. 483 """ 484 return self.__data.values()
485
486 - def clear(self):
487 """ 488 dict method. See Python dict API reference. 489 Settings are also re-initialized here. 490 491 @return None 492 """ 493 self.__data.clear() 494 self.__setup_const() 495 self.__scan()
496
497 - def set_persistent_setting(self, persistent_dict):
498 """ 499 Make metadata persistent, the input dict will be merged 500 with the base one at every reset call (clear()). 501 502 @param persistent_dict -- dictionary to merge 503 @type persistent_dict dict 504 505 @return None 506 """ 507 self.__persistent_settings.update(persistent_dict)
508
509 - def unset_persistent_setting(self, persistent_key):
510 """ 511 Remove dict key from persistent dictionary 512 513 @param persistent_key -- key to remove 514 @type persistent_dict dict 515 516 @return None 517 """ 518 del self.__persistent_settings[persistent_key] 519 del self.__data[persistent_key]
520
521 - def __setup_setting_vars(self):
522 523 """ 524 This function setups the *mtimes* and *files* dictionaries 525 that will be read and parsed afterwards by respective 526 internal parsers. 527 528 @return None 529 """ 530 531 dmp_dir = etpConst['dumpstoragedir'] 532 for repoid in self['repositories']['order']: 533 534 repos_mask_setting = {} 535 repos_mask_mtime = {} 536 repos_lic_wl_setting = {} 537 repos_lic_wl_mtime = {} 538 repo_data = self['repositories']['available'][repoid] 539 repos_sm_mask_setting = {} 540 repos_sm_mask_mtime = {} 541 confl_tagged = {} 542 543 maskpath = os.path.join(repo_data['dbpath'], 544 etpConst['etpdatabasemaskfile']) 545 wlpath = os.path.join(repo_data['dbpath'], 546 etpConst['etpdatabaselicwhitelistfile']) 547 sm_path = os.path.join(repo_data['dbpath'], 548 etpConst['etpdatabasesytemmaskfile']) 549 ct_path = os.path.join(repo_data['dbpath'], 550 etpConst['etpdatabaseconflictingtaggedfile']) 551 552 if os.path.isfile(maskpath) and os.access(maskpath, os.R_OK): 553 repos_mask_setting[repoid] = maskpath 554 repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \ 555 repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime" 556 557 if os.path.isfile(wlpath) and os.access(wlpath, os.R_OK): 558 repos_lic_wl_setting[repoid] = wlpath 559 repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \ 560 repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \ 561 ".mtime" 562 563 if os.path.isfile(sm_path) and os.access(sm_path, os.R_OK): 564 repos_sm_mask_setting[repoid] = sm_path 565 repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \ 566 repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \ 567 ".mtime" 568 if os.path.isfile(ct_path) and os.access(ct_path, os.R_OK): 569 confl_tagged[repoid] = ct_path 570 571 self.__setting_files['repos_mask'].update(repos_mask_setting) 572 self.__mtime_files['repos_mask'].update(repos_mask_mtime) 573 574 self.__setting_files['repos_license_whitelist'].update( 575 repos_lic_wl_setting) 576 self.__mtime_files['repos_license_whitelist'].update( 577 repos_lic_wl_mtime) 578 579 self.__setting_files['repos_system_mask'].update( 580 repos_sm_mask_setting) 581 self.__mtime_files['repos_system_mask'].update( 582 repos_sm_mask_mtime) 583 584 self.__setting_files['conflicting_tagged_packages'].update( 585 confl_tagged)
586
587 - def __setup_package_sets_vars(self):
588 589 """ 590 This function setups the *files* dictionary about package sets 591 that will be read and parsed afterwards by the respective 592 internal parser. 593 594 @return None 595 """ 596 597 # user defined package sets 598 sets_dir = etpConst['confsetsdir'] 599 pkg_set_data = {} 600 if (os.path.isdir(sets_dir) and os.access(sets_dir, os.R_OK)): 601 set_files = [x for x in os.listdir(sets_dir) if \ 602 (os.path.isfile(os.path.join(sets_dir, x)) and \ 603 os.access(os.path.join(sets_dir, x),os.R_OK))] 604 for set_file in set_files: 605 try: 606 set_file = str(set_file) 607 except (UnicodeDecodeError, UnicodeEncodeError,): 608 continue 609 pkg_set_data[set_file] = os.path.join(sets_dir, set_file) 610 self.__setting_files['system_package_sets'].update(pkg_set_data)
611
612 - def __parse(self):
613 """ 614 This is the main internal parsing method. 615 *files* and *mtimes* dictionaries are prepared and 616 parsed just a few lines later. 617 618 @return dict settings metadata 619 """ 620 # some parsers must be run BEFORE everything: 621 for item in self.__setting_files_pre_run: 622 myattr = '%s_parser' % (item,) 623 if not hasattr(self, myattr): 624 continue 625 func = getattr(self, myattr) 626 self.__data[item] = func() 627 628 # parse main settings 629 self.__setup_setting_vars() 630 self.__setup_package_sets_vars() 631 632 data = {} 633 for item in self.__setting_files_order: 634 myattr = '%s_parser' % (item,) 635 if not hasattr(self, myattr): 636 continue 637 func = getattr(self, myattr) 638 self.__data[item] = func()
639
640 - def get_setting_files_data(self):
641 """ 642 Return a copy of the internal *files* dictionary. 643 This dict contains config file paths and their identifiers. 644 645 @return dict __setting_files 646 """ 647 return self.__setting_files.copy()
648
649 - def get_mtime_files_data(self):
650 """ 651 Return a copy of the internal *mtime* dictionary. 652 This dict contains config file paths and their current mtime. 653 654 @return dict __mtime_files 655 """ 656 return self.__mtime_files.copy()
657
658 - def keywords_parser(self):
659 """ 660 Parser returning package keyword masking metadata 661 read from package.keywords file. 662 This file contains package mask or unmask directives 663 based on package keywords. 664 665 @return dict data 666 """ 667 data = { 668 'universal': set(), 669 'packages': {}, 670 'repositories': {}, 671 } 672 673 self.__validate_entropy_cache(self.__setting_files['keywords'], 674 self.__mtime_files['keywords_mtime']) 675 content = [x.split() for x in \ 676 self.__generic_parser(self.__setting_files['keywords']) \ 677 if len(x.split()) < 4] 678 for keywordinfo in content: 679 # skip wrong lines 680 if len(keywordinfo) > 3: 681 continue 682 # inversal keywording, check if it's not repo= 683 if len(keywordinfo) == 1: 684 if keywordinfo[0].startswith("repo="): 685 continue 686 # convert into entropy format 687 if keywordinfo[0] == "**": 688 keywordinfo[0] = "" 689 data['universal'].add(keywordinfo[0]) 690 continue 691 # inversal keywording, check if it's not repo= 692 if len(keywordinfo) in (2, 3,): 693 # repo=? 694 if keywordinfo[0].startswith("repo="): 695 continue 696 # add to repo? 697 items = keywordinfo[1:] 698 # convert into entropy format 699 if keywordinfo[0] == "**": 700 keywordinfo[0] = "" 701 reponame = [x for x in items if x.startswith("repo=") \ 702 and (len(x.split("=")) == 2)] 703 if reponame: 704 reponame = reponame[0].split("=")[1] 705 if reponame not in data['repositories']: 706 data['repositories'][reponame] = {} 707 # repository unmask or package in repository unmask? 708 if keywordinfo[0] not in data['repositories'][reponame]: 709 data['repositories'][reponame][keywordinfo[0]] = set() 710 if len(items) == 1: 711 # repository unmask 712 data['repositories'][reponame][keywordinfo[0]].add('*') 713 elif "*" not in \ 714 data['repositories'][reponame][keywordinfo[0]]: 715 716 item = [x for x in items if not x.startswith("repo=")] 717 data['repositories'][reponame][keywordinfo[0]].add( 718 item[0]) 719 elif len(items) == 2: 720 # it's going to be a faulty line!!?? 721 # can't have two items and no repo= 722 continue 723 else: 724 # add keyword to packages 725 if keywordinfo[0] not in data['packages']: 726 data['packages'][keywordinfo[0]] = set() 727 data['packages'][keywordinfo[0]].add(items[0]) 728 return data
729 730
731 - def unmask_parser(self):
732 """ 733 Parser returning package unmasking metadata read from 734 package.unmask file. 735 This file contains package unmask directives, allowing 736 to enable experimental or *secret* packages. 737 738 @return list parsed data 739 """ 740 self.__validate_entropy_cache(self.__setting_files['unmask'], 741 self.__mtime_files['unmask_mtime']) 742 return self.__generic_parser(self.__setting_files['unmask'])
743
744 - def mask_parser(self):
745 """ 746 Parser returning package masking metadata read from 747 package.mask file. 748 This file contains package mask directives, allowing 749 to disable experimental or *secret* packages. 750 751 @return list parsed data 752 """ 753 self.__validate_entropy_cache(self.__setting_files['mask'], 754 self.__mtime_files['mask_mtime']) 755 return self.__generic_parser(self.__setting_files['mask'])
756
757 - def system_mask_parser(self):
758 """ 759 Parser returning system packages mask metadata read from 760 package.system_mask file. 761 This file contains packages that should be always kept 762 installed, extending the already defined (in repository database) 763 set of atoms. 764 765 @return list parsed data 766 """ 767 self.__validate_entropy_cache(self.__setting_files['system_mask'], 768 self.__mtime_files['system_mask_mtime']) 769 return self.__generic_parser(self.__setting_files['system_mask'])
770
771 - def license_mask_parser(self):
772 """ 773 Parser returning packages masked by license metadata read from 774 license.mask file. 775 Packages shipped with licenses listed there will be masked. 776 777 @return list parsed data 778 """ 779 self.__validate_entropy_cache(self.__setting_files['license_mask'], 780 self.__mtime_files['license_mask_mtime']) 781 return self.__generic_parser(self.__setting_files['license_mask'])
782
784 """ 785 Parser returning licenses considered accepted by default 786 (= GPL compatibles) read from package.lic_whitelist. 787 788 @return dict parsed data 789 """ 790 data = {} 791 for repoid in self.__setting_files['repos_license_whitelist']: 792 self.__validate_entropy_cache( 793 self.__setting_files['repos_license_whitelist'][repoid], 794 self.__mtime_files['repos_license_whitelist'][repoid], 795 repoid = repoid) 796 data[repoid] = self.__generic_parser( 797 self.__setting_files['repos_license_whitelist'][repoid]) 798 return data
799
800 - def repos_mask_parser(self):
801 """ 802 Parser returning packages masked at repository level read from 803 packages.db.mask inside the repository database directory. 804 805 @return dict parsed data 806 """ 807 data = {} 808 for repoid in self.__setting_files['repos_mask']: 809 self.__validate_entropy_cache( 810 self.__setting_files['repos_mask'][repoid], 811 self.__mtime_files['repos_mask'][repoid], repoid = repoid) 812 data[repoid] = self.__generic_parser( 813 self.__setting_files['repos_mask'][repoid]) 814 # why ? line = line.split()[0] in the previous one? 815 return data
816
817 - def repos_system_mask_parser(self):
818 """ 819 Parser returning system packages mask metadata read from 820 packages.db.system_mask file inside the repository directory. 821 This file contains packages that should be always kept 822 installed, extending the already defined (in repository database) 823 set of atoms. 824 825 @return dict parsed data 826 """ 827 data = [] 828 for repoid in self.__setting_files['repos_system_mask']: 829 self.__validate_entropy_cache( 830 self.__setting_files['repos_system_mask'][repoid], 831 self.__mtime_files['repos_system_mask'][repoid], 832 repoid = repoid) 833 data += [x for x in self.__generic_parser( 834 self.__setting_files['repos_system_mask'][repoid]) if x \ 835 not in data] 836 # why ? line = line.split()[0] in the previous one? 837 return data
838
839 - def system_package_sets_parser(self):
840 """ 841 Parser returning system defined package sets read from 842 /etc/entropy/packages/sets. 843 844 @return dict parsed data 845 """ 846 data = {} 847 for set_name in self.__setting_files['system_package_sets']: 848 set_filepath = self.__setting_files['system_package_sets'][set_name] 849 set_elements = self.entropyTools.extract_packages_from_set_file( 850 set_filepath) 851 if set_elements: 852 data[set_name] = set_elements.copy() 853 return data
854
855 - def system_dirs_parser(self):
856 """ 857 Parser returning directories considered part of the base system. 858 859 @return list parsed data 860 """ 861 return self.__generic_parser(self.__setting_files['system_dirs'])
862
863 - def system_dirs_mask_parser(self):
864 """ 865 Parser returning directories NOT considered part of the base system. 866 Settings here overlay system_dirs_parser. 867 868 @return list parsed data 869 """ 870 return self.__generic_parser(self.__setting_files['system_dirs_mask'])
871
872 - def hw_hash_parser(self):
873 """ 874 Hardware hash metadata parser and generator. It returns a theorically 875 unique SHA256 hash bound to the computer running this Framework. 876 877 @return basestring containing SHA256 hexdigest 878 """ 879 hw_hash_file = self.__setting_files['hw_hash'] 880 if os.access(hw_hash_file, os.R_OK | os.F_OK): 881 hash_f = open(hw_hash_file, "r") 882 hash_data = hash_f.readline().strip() 883 hash_f.close() 884 return hash_data 885 886 hash_file_dir = os.path.dirname(hw_hash_file) 887 hw_hash_exec = etpConst['etp_hw_hash_gen'] 888 if os.access(hash_file_dir, os.W_OK) and \ 889 os.access(hw_hash_exec, os.X_OK | os.F_OK | os.R_OK): 890 pipe = os.popen('{ ' + hw_hash_exec + '; } 2>&1', 'r') 891 hash_data = pipe.read().strip() 892 sts = pipe.close() 893 if sts != None: 894 return None 895 hash_f = open(hw_hash_file, "w") 896 hash_f.write(hash_data) 897 hash_f.flush() 898 hash_f.close() 899 return hash_data
900 918
920 """ 921 Parser returning packages that could have been installed because 922 they aren't in the same scope, but ending up creating critical 923 issues. You can see it as a configurable conflict map. 924 925 @return dict parsed data 926 """ 927 data = {} 928 # keep priority order 929 repoids = [x for x in self['repositories']['order'] if x in \ 930 self.__setting_files['conflicting_tagged_packages']] 931 for repoid in repoids: 932 filepath = self.__setting_files['conflicting_tagged_packages'].get( 933 repoid) 934 if os.path.isfile(filepath) and os.access(filepath, os.R_OK): 935 confl_f = open(filepath,"r") 936 content = confl_f.readlines() 937 confl_f.close() 938 content = [x.strip().rsplit("#", 1)[0].strip().split() for x \ 939 in content if not x.startswith("#") and x.strip()] 940 for mydata in content: 941 if len(mydata) < 2: 942 continue 943 data[mydata[0]] = mydata[1:] 944 return data
945
946 - def socket_service_parser(self):
947 """ 948 Parses socket service configuration file. 949 This file contains information about Entropy remote service ports 950 and SSL. 951 952 @return dict data 953 """ 954 955 data = etpConst['socket_service'].copy() 956 957 sock_conf = self.__setting_files['socket_service'] 958 if not (os.path.isfile(sock_conf) and \ 959 os.access(sock_conf,os.R_OK)): 960 return data 961 962 socket_f = open(sock_conf,"r") 963 socketconf = [x.strip() for x in socket_f.readlines() if \ 964 x.strip() and not x.strip().startswith("#")] 965 socket_f.close() 966 967 for line in socketconf: 968 969 split_line = line.split("|") 970 split_line_len = len(split_line) 971 972 if line.startswith("listen|") and (split_line_len > 1): 973 974 item = split_line[1].strip() 975 if item: 976 data['hostname'] = item 977 978 elif line.startswith("listen-port|") and \ 979 (split_line_len > 1): 980 981 item = split_line[1].strip() 982 try: 983 item = int(item) 984 data['port'] = item 985 except ValueError: 986 continue 987 988 elif line.startswith("listen-timeout|") and \ 989 (split_line_len > 1): 990 991 item = split_line[1].strip() 992 try: 993 item = int(item) 994 data['timeout'] = item 995 except ValueError: 996 continue 997 998 elif line.startswith("listen-threads|") and \ 999 (split_line_len > 1): 1000 1001 item = split_line[1].strip() 1002 try: 1003 item = int(item) 1004 data['threads'] = item 1005 except ValueError: 1006 continue 1007 1008 elif line.startswith("session-ttl|") and \ 1009 (split_line_len > 1): 1010 1011 item = split_line[1].strip() 1012 try: 1013 item = int(item) 1014 data['session_ttl'] = item 1015 except ValueError: 1016 continue 1017 1018 elif line.startswith("max-connections|") and \ 1019 (split_line_len > 1): 1020 1021 item = split_line[1].strip() 1022 try: 1023 item = int(item) 1024 data['max_connections'] = item 1025 except ValueError: 1026 continue 1027 1028 elif line.startswith("ssl-port|") and \ 1029 (split_line_len > 1): 1030 1031 item = split_line[1].strip() 1032 try: 1033 item = int(item) 1034 data['ssl_port'] = item 1035 except ValueError: 1036 continue 1037 1038 elif line.startswith("disabled-commands|") and \ 1039 (split_line_len > 1): 1040 1041 disabled_cmds = split_line[1].strip().split() 1042 for disabled_cmd in disabled_cmds: 1043 data['disabled_cmds'].add(disabled_cmd) 1044 1045 elif line.startswith("ip-blacklist|") and \ 1046 (split_line_len > 1): 1047 1048 ips_blacklist = split_line[1].strip().split() 1049 for ip_blacklist in ips_blacklist: 1050 data['ip_blacklist'].add(ip_blacklist) 1051 1052 return data
1053
1054 - def system_parser(self):
1055 1056 """ 1057 Parses Entropy system configuration file. 1058 1059 @return dict data 1060 """ 1061 1062 data = {} 1063 data['proxy'] = etpConst['proxy'].copy() 1064 data['name'] = etpConst['systemname'] 1065 data['log_level'] = etpConst['entropyloglevel'] 1066 1067 etp_conf = self.__setting_files['system'] 1068 if not os.path.isfile(etp_conf) and \ 1069 os.access(etp_conf,os.R_OK): 1070 return data 1071 1072 const_secure_config_file(etp_conf) 1073 entropy_f = open(etp_conf,"r") 1074 entropyconf = [x.strip() for x in entropy_f.readlines() if \ 1075 x.strip() and not x.strip().startswith("#")] 1076 entropy_f.close() 1077 1078 for line in entropyconf: 1079 1080 split_line = line.split("|") 1081 split_line_len = len(split_line) 1082 1083 if line.startswith("loglevel|") and \ 1084 (len(line.split("loglevel|")) == 2): 1085 1086 loglevel = line.split("loglevel|")[1] 1087 try: 1088 loglevel = int(loglevel) 1089 except ValueError: 1090 pass 1091 if (loglevel > -1) and (loglevel < 3): 1092 data['log_level'] = loglevel 1093 1094 elif line.startswith("ftp-proxy|") and \ 1095 (split_line_len == 2): 1096 1097 ftpproxy = split_line[1].strip().split() 1098 if ftpproxy: 1099 data['proxy']['ftp'] = ftpproxy[-1] 1100 1101 elif line.startswith("http-proxy|") and \ 1102 (split_line_len == 2): 1103 1104 httpproxy = split_line[1].strip().split() 1105 if httpproxy: 1106 data['proxy']['http'] = httpproxy[-1] 1107 1108 elif line.startswith("proxy-username|") and \ 1109 (split_line_len == 2): 1110 1111 httpproxy = split_line[1].strip().split() 1112 if httpproxy: 1113 data['proxy']['username'] = httpproxy[-1] 1114 1115 elif line.startswith("proxy-password|") and \ 1116 (split_line_len == 2): 1117 1118 httpproxy = split_line[1].strip().split() 1119 if httpproxy: 1120 data['proxy']['password'] = httpproxy[-1] 1121 1122 elif line.startswith("system-name|") and \ 1123 (split_line_len == 2): 1124 1125 data['name'] = split_line[1].strip() 1126 1127 elif line.startswith("nice-level|") and \ 1128 (split_line_len == 2): 1129 1130 mylevel = split_line[1].strip() 1131 try: 1132 mylevel = int(mylevel) 1133 if (mylevel >= -19) and (mylevel <= 19): 1134 const_set_nice_level(mylevel) 1135 except (ValueError,): 1136 continue 1137 1138 return data
1139
1140 - def repositories_parser(self):
1141 1142 """ 1143 Setup Entropy Client repository settings reading them from 1144 the relative config file specified in etpConst['repositoriesconf'] 1145 1146 @return None 1147 """ 1148 1149 data = { 1150 'available': {}, 1151 'excluded': {}, 1152 'order': [], 1153 'product': etpConst['product'], 1154 'branch': etpConst['branch'], 1155 'default_repository': etpConst['officialrepositoryid'], 1156 'transfer_limit': etpConst['downloadspeedlimit'], 1157 'security_advisories_url': etpConst['securityurl'], 1158 } 1159 1160 repo_conf = etpConst['repositoriesconf'] 1161 if not (os.path.isfile(repo_conf) and os.access(repo_conf, os.R_OK)): 1162 return data 1163 1164 repo_f = open(repo_conf,"r") 1165 repositoriesconf = [x.strip() for x in repo_f.readlines() if x.strip()] 1166 repo_f.close() 1167 1168 # setup product and branch first 1169 for line in repositoriesconf: 1170 1171 split_line = line.split("|") 1172 split_line_len = len(split_line) 1173 1174 if (line.find("product|") != -1) and \ 1175 (not line.startswith("#")) and (split_line_len == 2): 1176 1177 data['product'] = split_line[1] 1178 1179 elif (line.find("branch|") != -1) and \ 1180 (not line.startswith("#")) and (split_line_len == 2): 1181 1182 branch = split_line[1].strip() 1183 data['branch'] = branch 1184 if not os.path.isdir(etpConst['packagesbindir']+"/"+branch) \ 1185 and (etpConst['uid'] == 0): 1186 1187 try: 1188 os.makedirs(etpConst['packagesbindir']+"/"+branch) 1189 except (OSError, IOError,): 1190 continue 1191 1192 for line in repositoriesconf: 1193 1194 split_line = line.split("|") 1195 split_line_len = len(split_line) 1196 1197 # populate data['available'] 1198 if (line.find("repository|") != -1) and (split_line_len == 5): 1199 1200 excluded = False 1201 my_repodata = data['available'] 1202 if line.startswith("##"): 1203 continue 1204 elif line.startswith("#"): 1205 excluded = True 1206 my_repodata = data['excluded'] 1207 line = line[1:] 1208 1209 reponame, repodata = const_extract_cli_repo_params(line, 1210 data['branch'], data['product']) 1211 if my_repodata.has_key(reponame): 1212 1213 my_repodata[reponame]['plain_packages'].extend( 1214 repodata['plain_packages']) 1215 my_repodata[reponame]['packages'].extend( 1216 repodata['packages']) 1217 1218 if (not my_repodata[reponame]['plain_database']) and \ 1219 repodata['plain_database']: 1220 1221 my_repodata[reponame]['plain_database'] = \ 1222 repodata['plain_database'] 1223 my_repodata[reponame]['database'] = \ 1224 repodata['database'] 1225 my_repodata[reponame]['dbrevision'] = \ 1226 repodata['dbrevision'] 1227 my_repodata[reponame]['dbcformat'] = \ 1228 repodata['dbcformat'] 1229 else: 1230 1231 my_repodata[reponame] = repodata.copy() 1232 if not excluded: 1233 data['order'].append(reponame) 1234 1235 elif (line.find("officialrepositoryid|") != -1) and \ 1236 (not line.startswith("#")) and (split_line_len == 2): 1237 1238 officialreponame = split_line[1] 1239 data['default_repository'] = officialreponame 1240 1241 elif (line.find("downloadspeedlimit|") != -1) and \ 1242 (not line.startswith("#")) and (split_line_len == 2): 1243 1244 try: 1245 myval = int(split_line[1]) 1246 if myval > 0: 1247 data['transfer_limit'] = myval 1248 else: 1249 data['transfer_limit'] = None 1250 except (ValueError, IndexError,): 1251 data['transfer_limit'] = None 1252 1253 elif (line.find("securityurl|") != -1) and \ 1254 (not line.startswith("#")) and (split_line_len == 2): 1255 1256 try: 1257 data['security_advisories_url'] = split_line[1] 1258 except (IndexError, ValueError, TypeError,): 1259 continue 1260 1261 return data
1262
1263 - def _clear_repository_cache(self, repoid = None):
1264 """ 1265 Internal method, go away! 1266 """ 1267 self.__cacher.discard() 1268 self._clear_dump_cache(etpCache['world_available']) 1269 self._clear_dump_cache(etpCache['world_update']) 1270 self._clear_dump_cache(etpCache['check_package_update']) 1271 self._clear_dump_cache(etpCache['filter_satisfied_deps']) 1272 self._clear_dump_cache(etpCache['atomMatch']) 1273 self._clear_dump_cache(etpCache['dep_tree']) 1274 if repoid != None: 1275 self._clear_dump_cache("%s/%s%s/" % ( 1276 etpCache['dbMatch'],etpConst['dbnamerepoprefix'],repoid,)) 1277 self._clear_dump_cache("%s/%s%s/" % ( 1278 etpCache['dbSearch'],etpConst['dbnamerepoprefix'],repoid,))
1279
1280 - def _clear_dump_cache(self, dump_name, skip = []):
1281 """ 1282 Internal method, go away! 1283 """ 1284 dump_path = os.path.join(etpConst['dumpstoragedir'],dump_name) 1285 dump_dir = os.path.dirname(dump_path) 1286 #dump_file = os.path.basename(dump_path) 1287 for currentdir, subdirs, files in os.walk(dump_dir): 1288 path = os.path.join(dump_dir,currentdir) 1289 if skip: 1290 found = False 1291 for myskip in skip: 1292 if path.find(myskip) != -1: 1293 found = True 1294 break 1295 if found: continue 1296 for item in files: 1297 if item.endswith(etpConst['cachedumpext']): 1298 item = os.path.join(path,item) 1299 try: 1300 os.remove(item) 1301 except (OSError, IOError,): 1302 pass 1303 try: 1304 if not os.listdir(path): 1305 os.rmdir(path) 1306 except (OSError, IOError,): 1307 pass
1308
1309 - def __generic_parser(self, filepath):
1310 """ 1311 Internal method. This is the generic file parser here. 1312 1313 @param filepath valid path 1314 @type filepath basestring 1315 @return list parsed data 1316 """ 1317 return self.entropyTools.generic_file_content_parser(filepath)
1318
1319 - def __remove_repo_cache(self, repoid = None):
1320 """ 1321 Internal method. Remove repository cache, because not valid anymore. 1322 1323 @param repoid repository identifier or None 1324 @type repoid basestring or None 1325 @return None 1326 """ 1327 if os.path.isdir(etpConst['dumpstoragedir']): 1328 if repoid: 1329 self._clear_repository_cache(repoid = repoid) 1330 return 1331 for repoid in self['repositories']['order']: 1332 self._clear_repository_cache(repoid = repoid) 1333 else: 1334 try: 1335 os.makedirs(etpConst['dumpstoragedir']) 1336 except IOError, e: 1337 if e.errno == 30: # readonly filesystem 1338 etpUi['pretend'] = True 1339 return 1340 except OSError: 1341 return
1342
1343 - def __save_file_mtime(self, toread, tosaveinto):
1344 """ 1345 Internal method. Save mtime of a file to another file. 1346 1347 @param toread file path to read 1348 @type toread basestring 1349 @param tosaveinto path where to save retrieved mtime information 1350 @type tosaveinto basestring 1351 @return None 1352 """ 1353 if not os.path.isfile(toread): 1354 currmtime = 0.0 1355 else: 1356 currmtime = os.path.getmtime(toread) 1357 1358 if not os.path.isdir(etpConst['dumpstoragedir']): 1359 try: 1360 os.makedirs(etpConst['dumpstoragedir'], 0775) 1361 const_setup_perms(etpConst['dumpstoragedir'], 1362 etpConst['entropygid']) 1363 except IOError, e: 1364 if e.errno == 30: # readonly filesystem 1365 etpUi['pretend'] = True 1366 return 1367 except (OSError,), e: 1368 # unable to create the storage directory 1369 # useless to continue 1370 return 1371 1372 try: 1373 mtime_f = open(tosaveinto,"w") 1374 except IOError, e: # unable to write? 1375 if e.errno == 30: # readonly filesystem 1376 etpUi['pretend'] = True 1377 return 1378 else: 1379 mtime_f.write(str(currmtime)) 1380 mtime_f.flush() 1381 mtime_f.close() 1382 os.chmod(tosaveinto, 0664) 1383 if etpConst['entropygid'] != None: 1384 os.chown(tosaveinto, 0, etpConst['entropygid'])
1385 1386
1387 - def __validate_entropy_cache(self, maskfile, mtimefile, repoid = None):
1388 """ 1389 Internal method. Validates Entropy Cache 1390 1391 @param maskfile path of the setting file 1392 @type maskfile basestring 1393 @param mtimefile path where to save retrieved mtime information 1394 @type mtimefile basestring 1395 @param repoid repository identifier or None 1396 @type repoid basestring or None 1397 @return None 1398 """ 1399 1400 # can't validate if running as user, moreover 1401 # users can't make changes, so... 1402 if os.getuid() != 0: 1403 return 1404 1405 # handle on-disk cache validation 1406 # in this case, repositories cache 1407 # if file is changed, we must destroy cache 1408 if not os.path.isfile(mtimefile): 1409 # we can't know if it has been updated 1410 # remove repositories caches 1411 self.__remove_repo_cache(repoid = repoid) 1412 self.__save_file_mtime(maskfile, mtimefile) 1413 else: 1414 # check mtime 1415 try: 1416 mtime_f = open(mtimefile,"r") 1417 mtime = mtime_f.readline().strip() 1418 mtime_f.close() 1419 # compare with current mtime 1420 try: 1421 currmtime = str(os.path.getmtime(maskfile)) 1422 except OSError: 1423 currmtime = "0.0" 1424 if mtime != currmtime: 1425 self.__remove_repo_cache(repoid = repoid) 1426 self.__save_file_mtime(maskfile, mtimefile) 1427 except (OSError, IOError,): 1428 self.__remove_repo_cache(repoid = repoid) 1429 self.__save_file_mtime(maskfile, mtimefile)
1430