Package entropy :: Package client :: Package interfaces :: Module client

Source Code for Module entropy.client.interfaces.client

  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   
 23  from __future__ import with_statement 
 24  import os 
 25  import sys 
 26  from entropy.core import Singleton 
 27  from entropy.output import TextInterface, bold, red, darkred, blue 
 28  from entropy.db import dbapi2 
 29  from entropy.client.interfaces.loaders import LoadersMixin 
 30  from entropy.client.interfaces.cache import CacheMixin 
 31  from entropy.client.interfaces.dep import CalculatorsMixin 
 32  from entropy.client.interfaces.methods import RepositoryMixin, MiscMixin, \ 
 33      MatchMixin 
 34  from entropy.client.interfaces.fetch import FetchersMixin 
 35  from entropy.const import etpConst, etpCache, etpUi, const_debug_write 
 36  from entropy.core import SystemSettings, SystemSettingsPlugin 
 37  from entropy.misc import LogFile 
 38  from entropy.exceptions import SystemDatabaseError, RepositoryError 
 39  from entropy.i18n import _ 
 40   
41 -class ClientSystemSettingsPlugin(SystemSettingsPlugin):
42 43 import entropy.tools as entropyTools 44
45 - def __init__(self, plugin_id, helper_interface):
46 SystemSettingsPlugin.__init__(self, plugin_id, helper_interface) 47 self.__repos_files = {} 48 self.__repos_mtime = {} 49 # this is a workaround to avoid running 50 # some post_setup hooks at class init 51 self.__hooks_on_init = True
52
53 - def __setup_repos_files(self, system_settings):
54 """ 55 This function collects available repositories configuration files 56 by filling internal dict() __repos_files and __repos_mtime. 57 58 @param system_settings: SystemSettings instance 59 @type system_settings: instance of SystemSettings 60 @return: None 61 @rtype: None 62 """ 63 64 self.__repos_mtime = { 65 'repos_license_whitelist': {}, 66 'repos_mask': {}, 67 'repos_system_mask': {}, 68 'repos_critical_updates': {}, 69 } 70 self.__repos_files = { 71 'repos_license_whitelist': {}, 72 'repos_mask': {}, 73 'repos_system_mask': {}, 74 'conflicting_tagged_packages': {}, 75 'repos_critical_updates': {}, 76 } 77 78 dmp_dir = etpConst['dumpstoragedir'] 79 for repoid in system_settings['repositories']['order']: 80 81 repos_mask_setting = {} 82 repos_mask_mtime = {} 83 repos_lic_wl_setting = {} 84 repos_lic_wl_mtime = {} 85 repo_data = system_settings['repositories']['available'][repoid] 86 repos_sm_mask_setting = {} 87 repos_sm_mask_mtime = {} 88 confl_tagged = {} 89 repos_critical_updates_setting = {} 90 repos_critical_updates_mtime = {} 91 92 maskpath = os.path.join(repo_data['dbpath'], 93 etpConst['etpdatabasemaskfile']) 94 wlpath = os.path.join(repo_data['dbpath'], 95 etpConst['etpdatabaselicwhitelistfile']) 96 sm_path = os.path.join(repo_data['dbpath'], 97 etpConst['etpdatabasesytemmaskfile']) 98 ct_path = os.path.join(repo_data['dbpath'], 99 etpConst['etpdatabaseconflictingtaggedfile']) 100 critical_path = os.path.join(repo_data['dbpath'], 101 etpConst['etpdatabasecriticalfile']) 102 103 if os.access(maskpath, os.R_OK | os.F_OK): 104 repos_mask_setting[repoid] = maskpath 105 repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \ 106 repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime" 107 108 if os.access(wlpath, os.R_OK | os.F_OK): 109 repos_lic_wl_setting[repoid] = wlpath 110 repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \ 111 repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \ 112 ".mtime" 113 114 if os.access(sm_path, os.R_OK | os.F_OK): 115 repos_sm_mask_setting[repoid] = sm_path 116 repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \ 117 repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \ 118 ".mtime" 119 if os.access(ct_path, os.R_OK | os.F_OK): 120 confl_tagged[repoid] = ct_path 121 122 if os.access(critical_path, os.R_OK | os.F_OK): 123 repos_critical_updates_setting[repoid] = critical_path 124 repos_critical_updates_mtime[repoid] = dmp_dir + "/repo_" + \ 125 repoid + "_" + etpConst['etpdatabasecriticalfile'] + \ 126 ".mtime" 127 128 self.__repos_files['repos_mask'].update(repos_mask_setting) 129 self.__repos_mtime['repos_mask'].update(repos_mask_mtime) 130 131 self.__repos_files['repos_license_whitelist'].update( 132 repos_lic_wl_setting) 133 self.__repos_mtime['repos_license_whitelist'].update( 134 repos_lic_wl_mtime) 135 136 self.__repos_files['repos_system_mask'].update( 137 repos_sm_mask_setting) 138 self.__repos_mtime['repos_system_mask'].update( 139 repos_sm_mask_mtime) 140 141 self.__repos_files['conflicting_tagged_packages'].update( 142 confl_tagged) 143 144 self.__repos_files['repos_critical_updates'].update( 145 repos_critical_updates_setting) 146 self.__repos_mtime['repos_critical_updates'].update( 147 repos_critical_updates_mtime)
148
149 - def __run_post_branch_migration_hooks(self, sys_settings_instance):
150 151 # only root can do this 152 if os.getuid() != 0: 153 return 154 155 old_branch_path = etpConst['etp_previous_branch_file'] 156 current_branch = sys_settings_instance['repositories']['branch'] 157 158 def write_current_branch(branch): 159 old_brf = open(old_branch_path, "w") 160 old_brf.write(current_branch+"\n") 161 old_brf.flush() 162 old_brf.close()
163 164 if not os.access(old_branch_path, os.F_OK | os.R_OK): 165 write_current_branch(current_branch) 166 return 167 168 old_f = open(old_branch_path, "r") 169 old_branch = old_f.readline().strip() 170 old_f.close() 171 172 if old_branch == current_branch: # all fine, no need to run 173 return 174 175 repos, err = self._helper.run_repositories_post_branch_switch_hooks( 176 old_branch, current_branch) 177 if not err: 178 write_current_branch(current_branch)
179
180 - def __run_post_branch_upgrade_hooks(self, sys_settings_instance):
181 182 # only root can do this 183 if os.getuid() != 0: 184 return 185 186 # look for updates 187 # critical_updates = False is needed to avoid 188 # issues with metadata not being available 189 try: 190 update, remove, fine, spm_fine = self._helper.calculate_world_updates( 191 critical_updates = False) 192 except (ValueError, SystemDatabaseError,): 193 update = 1 # foo! 194 195 # actually execute this only if 196 # there are no updates left 197 if not update: 198 self._helper.run_repository_post_branch_upgrade_hooks()
199
200 - def system_mask_parser(self, system_settings_instance):
201 202 parser_data = {} 203 # match installed packages of system_mask 204 mask_installed = [] 205 mask_installed_keys = {} 206 while (self._helper.clientDbconn != None): 207 try: 208 self._helper.clientDbconn.validateDatabase() 209 except SystemDatabaseError: 210 break 211 mc_cache = set() 212 repos_mask_list = self.__repositories_system_mask( 213 system_settings_instance) 214 m_list = repos_mask_list + system_settings_instance['system_mask'] 215 for atom in m_list: 216 m_ids, m_r = self._helper.clientDbconn.atomMatch(atom, 217 multiMatch = True) 218 if m_r != 0: 219 continue 220 mykey = self.entropyTools.dep_getkey(atom) 221 if mykey not in mask_installed_keys: 222 mask_installed_keys[mykey] = set() 223 for m_id in m_ids: 224 if m_id in mc_cache: 225 continue 226 mc_cache.add(m_id) 227 mask_installed.append(m_id) 228 mask_installed_keys[mykey].add(m_id) 229 break 230 231 parser_data.update({ 232 'repos_installed': mask_installed, 233 'repos_installed_keys': mask_installed_keys, 234 }) 235 return parser_data
236
237 - def masking_validation_parser(self, system_settings_instance):
238 data = { 239 'cache': {}, # package masking validation cache 240 } 241 return data
242
243 - def __repositories_system_mask(self, sys_settings_instance):
244 """ 245 Parser returning system packages mask metadata read from 246 packages.db.system_mask file inside the repository directory. 247 This file contains packages that should be always kept 248 installed, extending the already defined (in repository database) 249 set of atoms. 250 """ 251 system_mask = [] 252 for repoid in self.__repos_files['repos_system_mask']: 253 sys_settings_instance.validate_entropy_cache( 254 self.__repos_files['repos_system_mask'][repoid], 255 self.__repos_mtime['repos_system_mask'][repoid], 256 repoid = repoid) 257 system_mask += [x for x in \ 258 self.entropyTools.generic_file_content_parser( 259 self.__repos_files['repos_system_mask'][repoid]) if x \ 260 not in system_mask] 261 return system_mask
262
263 - def repositories_parser(self, sys_settings_instance):
264 """ 265 Parser that generates repository settings metadata. 266 267 @param sys_settings_instance: SystemSettings instance 268 @type sys_settings_instance: instance of SystemSettings 269 @return: parsed metadata 270 @rtype: dict 271 """ 272 273 # fill repositories metadata dictionaries 274 self.__setup_repos_files(sys_settings_instance) 275 276 data = { 277 'license_whitelist': {}, 278 'mask': {}, 279 'system_mask': [], 280 'critical_updates': {}, 281 'conflicting_tagged_packages': {}, 282 } 283 284 # parse license whitelist 285 """ 286 Parser returning licenses considered accepted by default 287 (= GPL compatibles) read from package.lic_whitelist. 288 """ 289 for repoid in self.__repos_files['repos_license_whitelist']: 290 sys_settings_instance.validate_entropy_cache( 291 self.__repos_files['repos_license_whitelist'][repoid], 292 self.__repos_mtime['repos_license_whitelist'][repoid], 293 repoid = repoid) 294 295 data['license_whitelist'][repoid] = \ 296 self.entropyTools.generic_file_content_parser( 297 self.__repos_files['repos_license_whitelist'][repoid]) 298 299 # package masking 300 """ 301 Parser returning packages masked at repository level read from 302 packages.db.mask inside the repository database directory. 303 """ 304 for repoid in self.__repos_files['repos_mask']: 305 sys_settings_instance.validate_entropy_cache( 306 self.__repos_files['repos_mask'][repoid], 307 self.__repos_mtime['repos_mask'][repoid], repoid = repoid) 308 309 data['mask'][repoid] = \ 310 self.entropyTools.generic_file_content_parser( 311 self.__repos_files['repos_mask'][repoid]) 312 313 # system masking 314 data['system_mask'] = self.__repositories_system_mask( 315 sys_settings_instance) 316 317 # critical updates 318 """ 319 Parser returning critical packages list metadata read from 320 packages.db.critical file inside the repository directory. 321 This file contains packages that should be always updated 322 before anything else. 323 """ 324 for repoid in self.__repos_files['repos_critical_updates']: 325 sys_settings_instance.validate_entropy_cache( 326 self.__repos_files['repos_critical_updates'][repoid], 327 self.__repos_mtime['repos_critical_updates'][repoid], 328 repoid = repoid) 329 data['critical_updates'][repoid] = \ 330 self.entropyTools.generic_file_content_parser( 331 self.__repos_files['repos_critical_updates'][repoid]) 332 333 334 # conflicts map 335 """ 336 Parser returning packages that could have been installed because 337 they aren't in the same scope, but ending up creating critical 338 issues. You can see it as a configurable conflict map. 339 """ 340 # keep priority order 341 repoids = [x for x in sys_settings_instance['repositories']['order'] \ 342 if x in self.__repos_files['conflicting_tagged_packages']] 343 for repoid in repoids: 344 filepath = self.__repos_files['conflicting_tagged_packages'].get( 345 repoid) 346 if os.access(filepath, os.R_OK | os.F_OK): 347 confl_f = open(filepath,"r") 348 content = confl_f.readlines() 349 confl_f.close() 350 content = [x.strip().rsplit("#", 1)[0].strip().split() for x \ 351 in content if not x.startswith("#") and x.strip()] 352 for mydata in content: 353 if len(mydata) < 2: 354 continue 355 data['conflicting_tagged_packages'][mydata[0]] = mydata[1:] 356 357 return data
358 359
360 - def misc_parser(self, sys_settings_instance):
361 362 """ 363 Parses Entropy client system configuration file. 364 365 @return dict data 366 """ 367 368 data = { 369 'filesbackup': etpConst['filesbackup'], 370 'forcedupdates': etpConst['forcedupdates'], 371 'ignore_spm_downgrades': etpConst['spm']['ignore-spm-downgrades'], 372 'collisionprotect': etpConst['collisionprotect'], 373 'configprotect': etpConst['configprotect'][:], 374 'configprotectmask': etpConst['configprotectmask'][:], 375 'configprotectskip': etpConst['configprotectskip'][:], 376 } 377 378 cli_conf = etpConst['clientconf'] 379 if not (os.path.isfile(cli_conf) and os.access(cli_conf, os.R_OK)): 380 return data 381 382 client_f = open(cli_conf,"r") 383 clientconf = [x.strip() for x in client_f.readlines() if \ 384 x.strip() and not x.strip().startswith("#")] 385 client_f.close() 386 for line in clientconf: 387 388 split_line = line.split("|") 389 split_line_len = len(split_line) 390 391 if line.startswith("filesbackup|") and (split_line_len == 2): 392 393 compatopt = split_line[1].strip().lower() 394 if compatopt in ("disable", "disabled","false", "0", "no",): 395 data['filesbackup'] = False 396 397 if line.startswith("forcedupdates|") and (split_line_len == 2): 398 399 compatopt = split_line[1].strip().lower() 400 if compatopt in ("disable", "disabled","false", "0", "no",): 401 data['forcedupdates'] = False 402 else: 403 data['forcedupdates'] = True 404 405 elif line.startswith("ignore-spm-downgrades|") and \ 406 (split_line_len == 2): 407 408 compatopt = split_line[1].strip().lower() 409 if compatopt in ("enable", "enabled", "true", "1", "yes"): 410 data['ignore_spm_downgrades'] = True 411 412 elif line.startswith("collisionprotect|") and (split_line_len == 2): 413 414 collopt = split_line[1].strip() 415 if collopt.lower() in ("0", "1", "2",): 416 data['collisionprotect'] = int(collopt) 417 418 elif line.startswith("configprotect|") and (split_line_len == 2): 419 420 configprotect = split_line[1].strip() 421 for myprot in configprotect.split(): 422 data['configprotect'].append( 423 unicode(myprot,'raw_unicode_escape')) 424 425 elif line.startswith("configprotectmask|") and \ 426 (split_line_len == 2): 427 428 configprotect = split_line[1].strip() 429 for myprot in configprotect.split(): 430 data['configprotectmask'].append( 431 unicode(myprot,'raw_unicode_escape')) 432 433 elif line.startswith("configprotectskip|") and \ 434 (split_line_len == 2): 435 436 configprotect = split_line[1].strip() 437 for myprot in configprotect.split(): 438 data['configprotectskip'].append( 439 etpConst['systemroot']+unicode(myprot, 440 'raw_unicode_escape')) 441 442 return data
443
444 - def post_setup(self, system_settings_instance):
445 """ 446 Reimplemented from SystemSettingsPlugin. 447 """ 448 if not self.__hooks_on_init: 449 # run post-branch migration scripts if branch setting got changed 450 self.__run_post_branch_migration_hooks(system_settings_instance) 451 # run post-branch upgrade migration scripts if the function 452 # above created migration files to handle 453 self.__run_post_branch_upgrade_hooks(system_settings_instance) 454 self.__hooks_on_init = False
455 456
457 -class Client(Singleton, TextInterface, LoadersMixin, CacheMixin, CalculatorsMixin, \ 458 RepositoryMixin, MiscMixin, MatchMixin, FetchersMixin):
459
460 - def init_singleton(self, indexing = True, noclientdb = 0, 461 xcache = True, user_xcache = False, repo_validation = True, 462 load_ugc = True, url_fetcher = None, 463 multiple_url_fetcher = None):
464 465 const_debug_write(__name__, "debug enabled") 466 self.sys_settings_client_plugin_id = \ 467 etpConst['system_settings_plugins_ids']['client_plugin'] 468 self.__instance_destroyed = False 469 self.atomMatchCacheKey = etpCache['atomMatch'] 470 self.dbapi2 = dbapi2 # export for third parties 471 self.FileUpdates = None 472 self.validRepositories = [] 473 self.UGC = None 474 # supporting external updateProgress stuff, you can point self.progress 475 # to your progress bar and reimplement updateProgress 476 self.progress = None 477 self.clientDbconn = None 478 self.safe_mode = 0 479 self.indexing = indexing 480 self.repo_validation = repo_validation 481 self.noclientdb = False 482 self.openclientdb = True 483 484 # setup package settings (masking and other stuff) 485 self.SystemSettings = SystemSettings() 486 const_debug_write(__name__, "SystemSettings loaded") 487 488 # modules import 489 import entropy.dump as dumpTools 490 import entropy.tools as entropyTools 491 self.dumpTools = dumpTools 492 self.entropyTools = entropyTools 493 self.clientLog = LogFile(level = self.SystemSettings['system']['log_level'], 494 filename = etpConst['equologfile'], header = "[client]") 495 496 self.MultipleUrlFetcher = multiple_url_fetcher 497 self.urlFetcher = url_fetcher 498 if self.urlFetcher == None: 499 from entropy.transceivers import UrlFetcher 500 self.urlFetcher = UrlFetcher 501 if self.MultipleUrlFetcher == None: 502 from entropy.transceivers import MultipleUrlFetcher 503 self.MultipleUrlFetcher = MultipleUrlFetcher 504 505 from entropy.cache import EntropyCacher 506 self.Cacher = EntropyCacher() 507 508 from entropy.client.misc import FileUpdates 509 self.FileUpdates = FileUpdates(self) 510 511 from entropy.client.mirrors import StatusInterface 512 # mirror status interface 513 self.MirrorStatus = StatusInterface() 514 515 if noclientdb in (False,0): 516 self.noclientdb = False 517 elif noclientdb in (True,1): 518 self.noclientdb = True 519 elif noclientdb == 2: 520 self.noclientdb = True 521 self.openclientdb = False 522 523 # load User Generated Content Interface 524 if load_ugc: 525 from entropy.client.services.ugc.interfaces import Client as ugc_cl 526 self.UGC = ugc_cl(self) 527 528 # class init 529 LoadersMixin.__init__(self) 530 531 self.xcache = xcache 532 shell_xcache = os.getenv("ETP_NOCACHE") 533 if shell_xcache: 534 self.xcache = False 535 536 do_validate_repo_cache = False 537 # now if we are on live, we should disable it 538 # are we running on a livecd? (/proc/cmdline has "cdroot") 539 if self.entropyTools.islive(): 540 self.xcache = False 541 elif (not self.entropyTools.is_user_in_entropy_group()) and not user_xcache: 542 self.xcache = False 543 elif not user_xcache: 544 do_validate_repo_cache = True 545 546 if not self.xcache and (self.entropyTools.is_user_in_entropy_group()): 547 try: 548 self.purge_cache(False) 549 except: 550 pass 551 552 if self.openclientdb: 553 self.open_client_repository() 554 555 # create our SystemSettings plugin 556 self.sys_settings_client_plugin = ClientSystemSettingsPlugin( 557 self.sys_settings_client_plugin_id, self) 558 559 # needs to be started here otherwise repository cache will be 560 # always dropped 561 if self.xcache: 562 self.Cacher.start() 563 564 if do_validate_repo_cache: 565 self.validate_repositories_cache() 566 567 if self.repo_validation: 568 self.validate_repositories() 569 else: 570 self.validRepositories.extend( 571 self.SystemSettings['repositories']['order']) 572 573 # add our SystemSettings plugin 574 # Make sure we connect Entropy Client plugin AFTER client db init 575 self.SystemSettings.add_plugin(self.sys_settings_client_plugin) 576 577 const_debug_write(__name__, "singleton loaded")
578 579
580 - def destroy(self):
581 self.__instance_destroyed = True 582 if hasattr(self,'clientDbconn'): 583 if self.clientDbconn != None: 584 self.clientDbconn.closeDB() 585 del self.clientDbconn 586 if hasattr(self,'FileUpdates'): 587 del self.FileUpdates 588 if hasattr(self,'clientLog'): 589 self.clientLog.close() 590 if hasattr(self,'SystemSettings') and \ 591 hasattr(self,'sys_settings_client_plugin_id'): 592 593 if hasattr(self.SystemSettings,'remove_plugin'): 594 try: 595 self.SystemSettings.remove_plugin( 596 self.sys_settings_client_plugin_id) 597 except KeyError: 598 pass 599 600 self.close_all_repositories(mask_clear = False) 601 self.closeAllSecurity() 602 self.closeAllQA()
603
604 - def repository_packages_spm_sync(self, repository_identifier, repo_db, 605 force = False):
606 """ 607 Service method used to sync package names with Source Package Manager 608 via metadata stored in Repository dbs collected at server-time. 609 Source Package Manager can change package names, categories or slot 610 and Entropy repositories must be kept in sync. 611 612 In other words, it checks for /usr/portage/profiles/updates changes, 613 of course indirectly, since there is no way entropy.client can directly 614 depend on Portage. 615 616 @param repository_identifier: repository identifier which repo_db 617 parameter is bound 618 @type repository_identifier: string 619 @param repo_db: repository database instance 620 @type repo_db: entropy.db.EntropyRepository 621 @return: bool stating if changes have been made 622 @rtype: bool 623 """ 624 if not self.clientDbconn: 625 # nothing to do if client db is not availabe 626 return False 627 628 etpConst['client_treeupdatescalled'].add(repository_identifier) 629 630 doRescan = False 631 shell_rescan = os.getenv("ETP_TREEUPDATES_RESCAN") 632 if shell_rescan: 633 doRescan = True 634 635 # check database digest 636 stored_digest = repo_db.retrieveRepositoryUpdatesDigest( 637 repository_identifier) 638 if stored_digest == -1: 639 doRescan = True 640 641 # check stored value in client database 642 client_digest = "0" 643 if not doRescan: 644 client_digest = self.clientDbconn.retrieveRepositoryUpdatesDigest( 645 repository_identifier) 646 647 if doRescan or (str(stored_digest) != str(client_digest)) or force: 648 649 # reset database tables 650 self.clientDbconn.clearTreeupdatesEntries(repository_identifier) 651 652 # load updates 653 update_actions = repo_db.retrieveTreeUpdatesActions( 654 repository_identifier) 655 # now filter the required actions 656 update_actions = self.clientDbconn.filterTreeUpdatesActions( 657 update_actions) 658 659 if update_actions: 660 661 mytxt = "%s: %s." % ( 662 bold(_("ATTENTION")), 663 red(_("forcing packages metadata update")), 664 ) 665 self.updateProgress( 666 mytxt, 667 importance = 1, 668 type = "info", 669 header = darkred(" * ") 670 ) 671 mytxt = "%s %s." % ( 672 red(_("Updating system database using repository")), 673 blue(repository_identifier), 674 ) 675 self.updateProgress( 676 mytxt, 677 importance = 1, 678 type = "info", 679 header = darkred(" * ") 680 ) 681 # run stuff 682 self.clientDbconn.runTreeUpdatesActions(update_actions) 683 684 # store new digest into database 685 self.clientDbconn.setRepositoryUpdatesDigest(repository_identifier, 686 stored_digest) 687 # store new actions 688 self.clientDbconn.addRepositoryUpdatesActions(etpConst['clientdbid'], 689 update_actions, self.SystemSettings['repositories']['branch']) 690 self.clientDbconn.commitChanges() 691 # clear client cache 692 self.clientDbconn.clearCache() 693 return True
694
695 - def is_destroyed(self):
696 return self.__instance_destroyed
697
698 - def __del__(self):
699 self.destroy()
700