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

Source Code for Module entropy.client.interfaces.trigger

  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 Package Manager Client Package installation triggers Interface}. 
 10   
 11  """ 
 12   
 13  import subprocess 
 14  import shutil 
 15  from entropy.client.interfaces.client import Client 
 16  from entropy.const import * 
 17  from entropy.exceptions import * 
 18  from entropy.output import * 
 19  from entropy.i18n import _ 
 20  import entropy.tools 
 21   
22 -class Trigger:
23 24 VALID_PHASES = ("preinstall", "postinstall", "preremove", "postremove",) 25 26 import entropy.tools as entropyTools
27 - def __init__(self, entropy_client, phase, pkgdata, package_action = None):
28 29 if not isinstance(entropy_client, Client): 30 mytxt = "A valid Entropy Instance is needed" 31 raise AttributeError(mytxt) 32 33 self.Entropy = entropy_client 34 self.pkgdata = pkgdata 35 self.prepared = False 36 self.triggers = [] 37 self._trigger_data = {} 38 self.package_action = package_action 39 40 self.spm_support = True 41 try: 42 Spm = self.Entropy.Spm() 43 self.Spm = Spm 44 except Exception as e: 45 self.entropyTools.print_traceback() 46 mytxt = darkred("%s, %s: %s, %s !") % ( 47 _("Source Package Manager interface can't be loaded"), 48 _("Error"), 49 e, 50 _("please fix"), 51 ) 52 self.Entropy.updateProgress( 53 mytxt, 54 importance = 0, 55 header = bold(" !!! ") 56 ) 57 self.spm_support = False 58 59 self.phase = phase 60 # validate phase 61 if self.phase not in Trigger.VALID_PHASES: 62 mytxt = "Valid phases: %s" % (Trigger.VALID_PHASES,) 63 raise AttributeError(mytxt)
64
65 - def prepare(self):
66 func = getattr(self, self.phase) 67 self.triggers = func() 68 self.prepared = True 69 return len(self.triggers) > 0
70
71 - def run(self):
72 for trigger_func in self.triggers: 73 trigger_func()
74
75 - def kill(self):
76 self.prepared = False 77 self._trigger_data.clear() 78 del self.triggers[:]
79
80 - def postinstall(self):
81 82 functions = [] 83 84 if self.spm_support: 85 spm_class = self.Entropy.Spm_class() 86 phases_map = spm_class.package_phases_map() 87 while True: 88 if self.pkgdata['spm_phases'] is not None: 89 if phases_map.get('postinstall') not \ 90 in self.pkgdata['spm_phases']: 91 break 92 functions.append(self.trigger_spm_postinstall) 93 break 94 95 cont_dirs = set((os.path.dirname(x) for x in self.pkgdata['content'])) 96 ldpaths = entropy.tools.collect_linker_paths() 97 if len(cont_dirs) != len(cont_dirs - set(ldpaths)): 98 functions.append(self.trigger_env_update) 99 100 if self.pkgdata['trigger']: 101 functions.append(self.trigger_call_ext_postinstall) 102 103 return functions
104
105 - def preinstall(self):
106 107 functions = [] 108 109 # Portage phases 110 if self.spm_support: 111 spm_class = self.Entropy.Spm_class() 112 phases_map = spm_class.package_phases_map() 113 while True: 114 if self.pkgdata['spm_phases'] != None: 115 if phases_map.get('preinstall') not \ 116 in self.pkgdata['spm_phases']: 117 break 118 functions.append(self.trigger_spm_preinstall) 119 break 120 121 if self.pkgdata['trigger']: 122 functions.append(self.trigger_call_ext_preinstall) 123 124 return functions
125
126 - def postremove(self):
127 128 functions = [] 129 130 cont_dirs = set((os.path.dirname(x) for x in \ 131 self.pkgdata['removecontent'])) 132 133 ldpaths = entropy.tools.collect_linker_paths() 134 if len(cont_dirs) != len(cont_dirs - set(ldpaths)): 135 functions.append(self.trigger_env_update) 136 137 if self.pkgdata['trigger']: 138 functions.append(self.trigger_call_ext_postremove) 139 140 return functions
141 142
143 - def preremove(self):
144 145 functions = [] 146 147 # Portage hook 148 if self.spm_support: 149 spm_class = self.Entropy.Spm_class() 150 phases_map = spm_class.package_phases_map() 151 152 while True: 153 if self.pkgdata['spm_phases'] != None: 154 if phases_map.get('preremove') not \ 155 in self.pkgdata['spm_phases']: 156 break 157 functions.append(self.trigger_spm_preremove) 158 break 159 160 # doing here because we need /var/db/pkg stuff 161 # in place and also because doesn't make any difference 162 while True: 163 if self.pkgdata['spm_phases'] != None: 164 if phases_map.get('postremove') not \ 165 in self.pkgdata['spm_phases']: 166 break 167 functions.append(self.trigger_spm_postremove) 168 break 169 170 if self.pkgdata['trigger']: 171 functions.append(self.trigger_call_ext_preremove) 172 173 return functions
174 175
177 return self.trigger_call_ext_generic()
178
180 return self.trigger_call_ext_generic()
181
183 return self.trigger_call_ext_generic()
184
186 return self.trigger_call_ext_generic()
187
188 - def trigger_call_ext_generic(self):
189 try: 190 return self.do_trigger_call_ext_generic() 191 except Exception as e: 192 mykey = self.pkgdata['category']+"/"+self.pkgdata['name'] 193 tb = self.entropyTools.get_traceback() 194 self.Entropy.updateProgress(tb, importance = 0, type = "error") 195 self.Entropy.clientLog.write(tb) 196 self.Entropy.clientLog.log( 197 ETP_LOGPRI_INFO, 198 ETP_LOGLEVEL_NORMAL, 199 "[POST] ATTENTION Cannot run External trigger for " + \ 200 mykey + "!! " + str(Exception) + ": " + str(e) 201 ) 202 mytxt = "%s: %s %s. %s." % ( 203 bold(_("QA")), 204 brown(_("Cannot run External trigger for")), 205 bold(mykey), 206 brown(_("Please report it")), 207 ) 208 self.Entropy.updateProgress( 209 mytxt, 210 importance = 0, 211 header = red(" ## ") 212 ) 213 return 0
214
215 - class EntropyShSandbox:
216
217 - def __init__(self, Entropy):
218 self.Entropy = Entropy 219 import entropy.tools as entropyTools 220 self.entropyTools = entropyTools
221
222 - def __env_setup(self, stage, pkgdata):
223 224 # mandatory variables 225 category = pkgdata.get('category') 226 if const_isunicode(category): 227 category = category.encode('utf-8') 228 229 pn = pkgdata.get('name') 230 if const_isunicode(pn): 231 pn = pn.encode('utf-8') 232 233 pv = pkgdata.get('version') 234 if const_isunicode(pv): 235 pv = pv.encode('utf-8') 236 237 pr = self.entropyTools.dep_get_spm_revision(pv) 238 pvr = pv 239 if pr == "r0": pvr += "-%s" % (pr,) 240 241 pet = pkgdata.get('versiontag') 242 if const_isunicode(pet): 243 pet = pet.encode('utf-8') 244 245 per = pkgdata.get('revision') 246 if const_isunicode(per): 247 per = per.encode('utf-8') 248 249 etp_branch = pkgdata.get('branch') 250 if const_isunicode(etp_branch): 251 etp_branch = etp_branch.encode('utf-8') 252 253 slot = pkgdata.get('slot') 254 if const_isunicode(slot): 255 slot = slot.encode('utf-8') 256 257 pkgatom = pkgdata.get('atom') 258 pkgkey = self.entropyTools.dep_getkey(pkgatom) 259 pvrte = pkgatom[len(pkgkey)+1:] 260 if const_isunicode(pvrte): 261 pvrte = pvrte.encode('utf-8') 262 263 etpapi = pkgdata.get('etpapi') 264 if const_isunicode(etpapi): 265 etpapi = etpapi.encode('utf-8') 266 267 p = pkgatom 268 if const_isunicode(p): 269 p = p.encode('utf-8') 270 271 chost, cflags, cxxflags = pkgdata.get('chost'), \ 272 pkgdata.get('cflags'), pkgdata.get('cxxflags') 273 274 chost = pkgdata.get('etpapi') 275 if const_isunicode(chost): 276 chost = chost.encode('utf-8') 277 278 cflags = pkgdata.get('etpapi') 279 if const_isunicode(cflags): 280 cflags = cflags.encode('utf-8') 281 282 cxxflags = pkgdata.get('etpapi') 283 if const_isunicode(cxxflags): 284 cxxflags = cxxflags.encode('utf-8') 285 286 # Not mandatory variables 287 288 eclasses = ' '.join(pkgdata.get('eclasses', [])) 289 if const_isunicode(eclasses): 290 eclasses = eclasses.encode('utf-8') 291 292 unpackdir = pkgdata.get('unpackdir', '') 293 if const_isunicode(unpackdir): 294 unpackdir = unpackdir.encode('utf-8') 295 296 imagedir = pkgdata.get('imagedir', '') 297 if const_isunicode(imagedir): 298 imagedir = imagedir.encode('utf-8') 299 300 sb_dirs = [unpackdir, imagedir] 301 sb_write = ':'.join(sb_dirs) 302 303 myenv = { 304 "ETP_API": etpSys['api'], 305 "ETP_LOG": self.Entropy.clientLog.get_fpath(), 306 "ETP_STAGE": stage, # entropy trigger stage 307 "ETP_PHASE": self.__get_sh_stage(), # entropy trigger phase 308 "ETP_BRANCH": etp_branch, 309 "CATEGORY": category, # package category 310 "PN": pn, # package name 311 "PV": pv, # package version 312 "PR": pr, # package revision (portage) 313 "PVR": pvr, # package version+revision 314 "PVRTE": pvrte, # package version+revision+entropy tag+entropy rev 315 "PER": per, # package entropy revision 316 "PET": pet, # package entropy tag 317 "SLOT": slot, # package slot 318 "PAPI": etpapi, # package entropy api 319 "P": p, # complete package atom 320 "WORKDIR": unpackdir, # temporary package workdir 321 "B": unpackdir, # unpacked binary package directory? 322 "D": imagedir, # package unpack destination (before merging to live) 323 "ENTROPY_TMPDIR": etpConst['packagestmpdir'], # entropy temporary directory 324 "CFLAGS": cflags, # compile flags 325 "CXXFLAGS": cxxflags, # compile flags 326 "CHOST": chost, # *nix CHOST 327 "PORTAGE_ECLASSES": eclasses, # portage eclasses, " " separated 328 "ROOT": etpConst['systemroot'], 329 "SANDBOX_WRITE": sb_write, 330 } 331 sysenv = os.environ.copy() 332 sysenv.update(myenv) 333 return sysenv
334
335 - def __get_sh_stage(self, stage):
336 mydict = { 337 "preinstall": "pkg_preinst", 338 "postinstall": "pkg_postinst", 339 "preremove": "pkg_prerm", 340 "postremove": "pkg_postrm", 341 } 342 return mydict.get(stage)
343
344 - def run(self, stage, pkgdata, trigger_file):
345 env = self.__env_setup(stage, pkgdata) 346 p = subprocess.Popen([trigger_file, stage], 347 stdout = sys.stdout, stderr = sys.stderr, 348 env = env 349 ) 350 rc = p.wait() 351 if os.path.isfile(trigger_file): 352 os.remove(trigger_file) 353 return rc
354
355 - class EntropyPySandbox:
356
357 - def __init__(self, Entropy):
358 self.Entropy = Entropy
359
360 - def run(self, stage, pkgdata, trigger_file):
361 my_ext_status = 1 362 if os.path.isfile(trigger_file): 363 with open(trigger_file) as trig_f: 364 exec(compile(trig_f.read(), trigger_file, 'exec')) 365 if os.path.isfile(trigger_file): 366 os.remove(trigger_file) 367 return my_ext_status
368
370 371 # if mute, supress portage output 372 if etpUi['mute']: 373 oldsystderr = sys.stderr 374 oldsysstdout = sys.stdout 375 stdfile = open("/dev/null", "w") 376 sys.stdout = stdfile 377 sys.stderr = stdfile 378 379 tg_pfx = "%s/trigger-" % (etpConst['entropyunpackdir'],) 380 while True: 381 triggerfile = "%s%s" % (tg_pfx, entropy.tools.get_random_number(),) 382 if not os.path.isfile(triggerfile): break 383 384 triggerdir = os.path.dirname(triggerfile) 385 if not os.path.isdir(triggerdir): 386 os.makedirs(triggerdir) 387 388 f = open(triggerfile, "w") 389 chunk = 1024 390 start = 0 391 while True: 392 buf = self.pkgdata['trigger'][start:] 393 if not buf: break 394 f.write(buf) 395 start += chunk 396 f.flush() 397 f.close() 398 399 # if mute, restore old stdout/stderr 400 if etpUi['mute']: 401 sys.stderr = oldsystderr 402 sys.stdout = oldsysstdout 403 stdfile.close() 404 405 f = open(triggerfile, "r") 406 interpreter = f.readline().strip() 407 f.close() 408 entropy_sh = etpConst['trigger_sh_interpreter'] 409 if interpreter == "#!%s" % (entropy_sh,): 410 os.chmod(triggerfile, 0o775) 411 my = self.EntropyShSandbox(self.Entropy) 412 else: 413 my = self.EntropyPySandbox(self.Entropy) 414 return my.run(self.phase, self.pkgdata, triggerfile)
415
416 - def trigger_env_update(self):
417 self.Entropy.clientLog.log( 418 ETP_LOGPRI_INFO, 419 ETP_LOGLEVEL_NORMAL, 420 "[POST] Running env_update" 421 ) 422 self.Spm.environment_update(stderr = self.Entropy.clientLog, 423 stdout = self.Entropy.clientLog)
424
425 - def trigger_spm_postinstall(self):
426 427 self.Entropy.updateProgress( 428 "SPM: %s" % (brown(_("post-install phase")),), 429 importance = 0, 430 header = red(" ## ") 431 ) 432 return self.Spm.execute_package_phase(self.pkgdata, 'postinstall')
433
434 - def trigger_spm_preinstall(self):
435 436 self.Entropy.updateProgress( 437 "SPM: %s" % (brown(_("pre-install phase")),), 438 importance = 0, 439 header = red(" ## ") 440 ) 441 return self.Spm.execute_package_phase(self.pkgdata, 'preinstall')
442
443 - def trigger_spm_preremove(self):
444 445 self.Entropy.updateProgress( 446 "SPM: %s" % (brown(_("pre-remove phase")),), 447 importance = 0, 448 header = red(" ## ") 449 ) 450 return self.Spm.execute_package_phase(self.pkgdata, 'preremove')
451
452 - def trigger_spm_postremove(self):
453 454 self.Entropy.updateProgress( 455 "SPM: %s" % (brown(_("post-remove phase")),), 456 importance = 0, 457 header = red(" ## ") 458 ) 459 return self.Spm.execute_package_phase(self.pkgdata, 'postremove')
460