def __mail(self, mailingID): #{{{ try: rc = self.mailingInfo[mailingID] except KeyError: r = self.cursor.querys( 'SELECT company_id, shortname, deleted FROM mailing_tbl WHERE mailing_id = :mid', {'mid': mailingID}) if r is not None: rc = agn.mutable(companyID=r[0], name=self.__toiso(r[1]), deleted=bool(r[2])) else: rc = agn.mutable(companyID=0, name='#%d not found' % mailingID, deleted=False) self.mailingInfo[mailingID] = rc return rc
def controllerSetup (self): defaults = [ ('xmlrpc.host', 'localhost'), ('xmlrpc.port', 9400), ('xmlrpc.allow_none', True) ] for (var, value) in defaults: if var not in self.cfg: self.cfg[var] = value ctx = agn.mutable (last = 0) return ctx
def __answer(self, ok, s): if ok: agn.log(agn.LV_DEBUG, 'answer', 'OK : %s' % s) content = '+OK: %s\n' % s else: agn.log(agn.LV_INFO, 'answer', 'ERR: %s' % s) content = '-ERR: %s\n' % s return agn.mutable(code=200, mime='text/plain', headers=None, content=content)
def find (key, defaultValue): rc = agn.mutable (path = None, content = [], modified = False, hash = None) try: for element in self.mta.getlist (key): try: (hash, path) = element.split (':', 1) except ValueError: hash = None path = element if path.startswith (agn.base): if rc.path is None: rc.path = path rc.hash = hash if not os.path.isfile (path): agn.createPath (os.path.dirname (path)) open (path, 'w').close () if hash is not None: self.mta.postfixMake (path) if rc.path is not None: try: fd = open (rc.path) for line in (_l.strip () for _l in fd): try: (var, val) = [_v.strip () for _v in line.split (None, 1)] except ValueError: var = line val = defaultValue rc.modified = True if var not in [_c[0] for _c in rc.content]: rc.content.append ((var, val)) else: rc.modified = True fd.close () agn.log (agn.LV_VERBOSE, 'data', 'Read %d lines from %s' % (len (rc.content), rc.path)) except OSError, e: agn.log (agn.LV_ERROR, 'data', 'Failed to read %s: %s' % (rc.path, str (e)))
def enrich (self): rc = self.msg if not self.rule.enrich is None: mailinfo = agn.mutable (type = 'text/plain', charset = 'ISO-8859-1') inlined = False try: filled = self.rule.enrich.fill (self.makeNamespace (mailinfo, self.rule.enrich.property ('limit-clause'))) inlined = self.rule.enrich.property ('inline') except agn.error as e: filled = None agn.log (agn.LV_ERROR, 'enrich', 'Failed to fill template for rule %s: %s' % (self.rule.rid, e)) if not filled is None and len (filled.strip ()) > 0: rc = email.Message.Message () for (var, val) in self.msg.items (): rc[var] = val if inlined: if self.msg.is_multipart (): text = self.msg.as_string (False).replace ('\r\n', '\n') n = text.find ('\n\n') if n != -1: text = text[n + 2:] else: text = self.msg.get_payload (decode = True) if text is None: text = filled else: text = filled + '\n' + text rc.set_payload (text, mailinfo.charset) else: rc.set_type ('multipart/mixed') info = email.Message.Message () info.set_type (mailinfo.type) info.set_payload (filled, mailinfo.charset) rc.attach (info) rc.attach (self.msg) return rc
def reportMailings(self): #{{{ mails = [] query = 'SELECT status_id, mailing_id, genstatus, genchange, status_field, senddate FROM maildrop_status_tbl WHERE ' query += 'genstatus IN (1, 2) AND status_field IN (\'W\', \'R\', \'D\')' for (statusID, mailingID, genStatus, genChange, statusField, sendDate) in self.cursor.queryc(query): if statusField in ('R', 'D') and genStatus == 1: continue info = self.__mail(mailingID) mail = agn.mutable(statusID=statusID, statusField=statusField, mailingID=mailingID, mailingName=info.name, companyID=info.companyID, deleted=info.deleted, genChange=genChange, sendDate=sendDate) mails.append(mail) if self.report or mails: template = agn.mkpath(agn.base, 'scripts', 'recovery.tmpl') try: fd = open(template, 'r') content = fd.read() fd.close() except IOError, e: agn.log( agn.LV_ERROR, 'report', 'Unable to find template "%s": %r' % (template, e.args)) content = None if content: ns = { 'host': socket.getfqdn(), 'report': self.report, 'mails': mails } tmpl = agn.Template(content) try: body = tmpl.fill(ns) except agn.error as e: agn.log(agn.LV_ERROR, 'report', 'Failed to fill template "%s": %s' % (template, e)) body = None if body: charset = tmpl.property('charset') subject = tmpl.property('subject') if not subject: subject = tmpl['subject'] if not subject: subject = 'Recovery report for %s' % ns['host'] else: stmpl = agn.Template(subject) subject = stmpl.fill(ns) sender = tmpl.property('sender') if not sender: sender = 'openemm' receiver = tmpl.property('receiver') if not receiver: receiver = 'openemm' if self.doit: if charset: eagn.EMail.forceEncoding(charset, 'qp') mail = eagn.EMail() mail.setSender(sender) for recv in [_r.strip() for _r in receiver.split(',')]: if recv: mail.addTo(recv) if charset: mail.setCharset(charset) mail.setSubject(subject) mail.setText(body) mail.sendMail() else: print('From: %s' % sender) print('To: %s' % receiver) print('Subject: %s' % subject) print('') print(body)
def readDatabase (self, auto): rc = [] db = agn.DBaseID () if not db: agn.log (agn.LV_ERROR, 'data', 'Unable to create database connection') raise agn.error ('readDatabase.open') try: i = db.cursor () if not i: agn.log (agn.LV_ERROR, 'data', 'Unable to get database cursor') raise agn.error ('readDatabase.cursor') try: fqdn = socket.getfqdn ().lower () company_list = [] newDomains = {} forwards = [] seen = set () acceptedForwards = set () ctab = {} if self.domains: domain = self.domains[0] else: domain = self.fixdomain seen.add (domain) rc.append ('fbl@%s\taccept:rid=unsubscribe' % domain) query = 'SELECT company_id, mailloop_domain FROM company_tbl WHERE status = \'active\'' missing = [] for record in i.query (query): if record[1]: ctab[record[0]] = record[1] if record[1] not in seen: rc.append ('fbl@%s\talias:fbl@%s' % (record[1], domain)) if record[1] not in self.mtdom and record[1].lower () != fqdn: newDomains[record[1]] = agn.mutable (rid = 0, domain = record[1]) seen.add (record[1]) else: missing.append (record[0]) company_list.append (record[0]) if missing: missing.sort () agn.log (agn.LV_VERBOSE, 'data', 'Missing mailloop_domain for %s' % ', '.join ([str (m) for m in missing])) seen.clear () # query = 'SELECT rid, shortname, company_id, filter_address, forward_enable, forward, ar_enable, ar_sender, ar_subject, ar_text, ar_html, subscribe_enable, mailinglist_id, form_id, timestamp, spam_email, spam_required, spam_forward, autoresponder_mailing_id, security_token FROM mailloop_tbl' apattern = re.compile ('alias(es)?="([^"]+)"') dpattern = re.compile ('domains?="([^"]+)"') for record in i.query (query): subscribe_enable = None mailinglist_id = None form_id = None (rid, shortname, company_id, filteraddr, forward_enable, forward, ar_enable, ar_sender, ar_subject, ar_text, ar_html, subscribe_enable, mailinglist_id, form_id, timestamp, spam_email, spam_required, spam_forward, autoresponder_mailing_id, security_token) = record if not company_id in company_list: continue if not rid is None: seen.add (rid) rid = str (rid) domains = None if timestamp is None: now = time.localtime () timestamp = now[5] + now[4] * 100 + now[3] * 10000 + now[2] * 1000000 + now[1] * 100000000 + now[0] * 10000000000 else: timestamp = timestamp.second + timestamp.minute * 100 + timestamp.hour * 10000 + timestamp.day * 1000000 + timestamp.month * 100000000 + timestamp.year * 10000000000 aliases = None if not filteraddr is None: aliases = filteraddr.split () if not aliases: aliases = None collect_domains = False if not shortname is None: mtch = apattern.search (shortname) if not mtch is None: tempaliases = (mtch.groups ()[1]).split () if aliases is None: aliases = tempaliases else: aliases += tempaliases mtch = dpattern.search (shortname) if not mtch is None: domains = (mtch.groups ()[0]).split () for domain in domains: if not self.mtdom.has_key (domain) and not domain in newDomains: newDomains[domain] = agn.mutable (rid = rid, domain = domain) else: collect_domains = True if not aliases is None: for alias in aliases: if not alias.startswith (self.prefix): parts = alias.split ('@') if len (parts) == 2: if collect_domains: if domains is None: domains = [parts[1]] else: domains.append (parts[1]) if not self.mtdom.has_key (parts[1]) and not parts[1] in newDomains: newDomains[parts[1]] = agn.mutable (rid = rid, domain = parts[1]) if autoresponder_mailing_id: try: autoresponder_mailing_id = int (autoresponder_mailing_id) if not security_token: agn.log (agn.LV_ERROR, 'data', '%s: Autoresponder has mailing id, but no security token' % rid) autoresponder_mailing_id = None except ValueError, e: agn.log (agn.LV_ERROR, 'data', '%s: Failed to parse autoresponder_mailing_id %r: %s' % (rid, autoresponder_mailing_id, str (e))) autoresponder_mailing_id = None if ar_enable and not ar_text and not autoresponder_mailing_id: ar_enable = False if ar_enable: def nvl (s): if s is not None and type (s) not in (str, unicode): return str (s) return s auto.append (Autoresponder (rid, timestamp, ar_sender, ar_subject, nvl (ar_text), nvl (ar_html), autoresponder_mailing_id, security_token)) if domains is None: try: cdomain = ctab[company_id] if self.domains and cdomain != self.domains[0]: domains = [self.domains[0]] else: domains = [] if not self.domains or cdomain in self.domains: domains.append (cdomain) else: agn.log (agn.LV_ERROR, 'data', 'Companys domain "%s" not found in mailertable' % cdomain) except KeyError: agn.log (agn.LV_DEBUG, 'data', 'No domain for company found, further processing') if domains is None: domains = self.domains elif self.domains and self.domains[0] not in domains: domains.insert (0, self.domains[0]) extra = 'rid=%s' % rid if company_id: extra += ',cid=%d' % company_id if forward_enable and forward: extra += ',fwd=%s' % forward forwards.append (agn.mutable (rid = rid, address = forward)) if spam_email: extra += ',spam_email=%s' % spam_email if spam_forward is not None: extra += ',spam_fwd=%d' % spam_forward if spam_required is not None: extra += ',spam_req=%d' % spam_required if ar_enable: extra += ',ar=%s' % rid if autoresponder_mailing_id: extra += ',armid=%d' % autoresponder_mailing_id if subscribe_enable and mailinglist_id and form_id: extra += ',sub=%d:%d' % (mailinglist_id, form_id) for domain in domains: line = '%s%s@%s\taccept:%s' % (self.prefix, rid, domain, extra) agn.log (agn.LV_VERBOSE, 'data', 'Add line: ' + line) rc.append (line) if aliases and domains: for alias in aliases: rc.append ('%s\talias:%s%s@%s' % (alias, self.prefix, rid, domains[0])) acceptedForwards.add (alias) if seen: rules = {} query = 'SELECT rid, section, pattern FROM mailloop_rule_tbl' for (rid, section, pattern) in i.query (query): if rid in seen: try: rule = rules[rid] except KeyError: rule = {} rules[rid] = rule try: sect = rule[section] except KeyError: sect = [] rule[section] = sect sect.append (pattern) self.updateRules (rules) for forward in forwards: parts = forward.address.split ('@') if len (parts) == 2: fdomain = parts[1].lower () for domain in self.mtdom: if domain == fdomain and forward not in acceptedForwards: agn.log (agn.LV_WARNING, 'db', '%s: using address "%s" with local handled domain "%s"' % (forward.rid, forward.address, domain)) refuse = [] for domain in newDomains: if domain == fdomain: nd = newDomains[domain] agn.log (agn.LV_WARNING, 'db', '%s: try to add new domain for already existing forward address "%s" in %s, refused' % (nd.rid, forward.address, forward.rid)) refuse.append (domain) for domain in refuse: del newDomains[domain] if newDomains: if agn.sDNS is not None: try: dns = agn.sDNS () laddrs = dns.getAllAddresses (fqdn) for domain in newDomains: checks = dns.getMailexchanger (domain) if not checks: checks = [domain] if checks: for check in checks: caddrs = dns.getAllAddresses (check) for caddr in caddrs: if caddr not in laddrs: agn.log (agn.LV_ERROR, 'db', '%s: checked address "%s" for "%s" does not point to local machines address %s' % (domain, caddr, check, ', '.join (laddrs))) else: agn.log (agn.LV_ERROR, 'db', '%s: failed to resolve' % domain) except Exception, e: agn.log (agn.LV_ERROR, 'db', 'Failed to verify new domains: %s' % str (e)) if self.mta.mta == 'sendmail': chandler = signal.signal (signal.SIGUSR1, signal.SIG_IGN) cmd = [self.controlSendmail, 'add'] for domain in newDomains: cmd.append (domain) agn.log (agn.LV_INFO, 'db', 'Found new domains, add them using ' + `cmd`) subprocess.call (cmd) agn.log (agn.LV_INFO, 'db', 'Restarting sendmail due to domain update') subprocess.call ([self.restartSendmail]) signal.signal (signal.SIGUSR1, chandler) self.readMailertable (newDomains) finally: i.close ()
def executorSetup (self): ctx = agn.mutable ( process = Processor () ) return ctx
def makeNamespace (self, mailinfo, limitClause): ns = {'agn': agn, 'mailinfo': mailinfo, 'user': None, 'mail': None} cinfo = self.cinfo if cinfo is None: cinfo = agn.mutable (customerID = None, mailingID = None) if cinfo.customerID is None or cinfo.mailingID is None: sm = self.rule.scanMessage (cinfo, self.msg, None) if not sm.minfo is None: if cinfo.customerID is None: cinfo.customerID = sm.minfo.customerID if cinfo.mailingID is None: cinfo.mailingID = sm.minfo.mailingID if self.companyID > 0: db = agn.DBaseID () if not db is None: cursor = db.cursor () if not cursor is None: if cinfo.customerID is None: for state in [0, 1]: if state == 0: if not limitClause: continue addClause = ' AND %s' % limitClause else: addClause = '' for eMail in [self.sender, self.headerFrom and self.headerFrom[1].lower () or None]: if not eMail: continue for r in cursor.query ('SELECT customer_id FROM customer_%d_tbl WHERE email = :email%s' % (self.companyID, addClause), {'email': eMail}): if not r[0] is None: if cinfo.customerID is None or r[0] > cinfo.customerID: cinfo.customerID = r[0] if not cinfo.customerID is None: break if not cinfo.customerID is None: break if not cinfo.customerID is None: user = agn.mutable () for r in cursor.query ('SELECT * FROM customer_%d_tbl WHERE customer_id = :cid' % self.companyID, {'cid': cinfo.customerID}): desc = cursor.description () if not desc is None: n = 0 while n < len (desc): user.__dict__[desc[n][0].lower ()] = r[n] n += 1 break user.binding = {} for r in cursor.query ('SELECT mailinglist_id, user_type, user_status, user_remark, timestamp, creation_date, exit_mailing_id, mediatype FROM customer_%d_binding_tbl WHERE customer_id = :cid' % self.companyID, {'cid': cinfo.customerID}): bind = agn.mutable ( list_id = r[0], type = r[1], status = r[2], remark = r[3], timestamp = r[4], creation_date = r[5], exit_id = r[6], media = r[7] ) user.binding[r[0]] = bind ns['user'] = user if not cinfo.mailingID is None: mail = agn.mutable () for r in cursor.query ('SELECT shortname, description, creation_date, mailinglist_id FROM mailing_tbl WHERE mailing_id = :mid', {'mid': cinfo.mailingID}): mail.shortname = r[0] mail.description = r[1] mail.creation_date = r[2] mail.list_id = r[3] mail.status_field = None for r in cursor.query ('SELECT status_field, senddate FROM maildrop_status_tbl WHERE mailing_id = :mid', {'mid': cinfo.mailingID}): if not r[0] is None: if r[0] in ('W', 'D', 'R', 'E'): mail.status_field = r[0] if r[0] == 'W': mail.senddate = r[1] elif r[0] == 'R': mail.sendhour = r[1].hour elif mail.status_field is None or (mail.status_field == 'A' and r[0] == 'T'): mail.status_field = r[0] ns['mail'] = mail cursor.close () db.close () ns['rid'] = self.rid ns['company_id'] = self.companyID ns['sender'] = self.sender ns['customer_id'] = cinfo.customerID ns['mailing_id'] = cinfo.mailingID return ns
def convertToHardbounce(self): #{{{ agn.log(agn.LV_INFO, 'conv', 'Start converting softbounces to hardbounce') coll = self.__ccoll( 'SELECT distinct company_id FROM softbounce_email_tbl WHERE company_id IN (SELECT company_id FROM company_tbl WHERE status = \'active\')', None, None, None) stats = [] for company in sorted(coll): cstat = [company, 0, 0] stats.append(cstat) agn.log(agn.LV_INFO, 'conv', 'Working on %d' % company) dquery = 'DELETE FROM softbounce_email_tbl WHERE company_id = %d AND email = :email' % company dcurs = self.db.cursor() uquery = self.curs.rselect( 'UPDATE customer_%d_binding_tbl SET user_status = 2, user_remark = \'bounce:soft\', exit_mailing_id = :mailing, timestamp = %%(sysdate)s WHERE customer_id = :customer AND user_status = 1' % company) bquery = self.curs.rselect( 'INSERT INTO bounce_tbl (company_id, customer_id, detail, mailing_id, timestamp, dsn) VALUES (%d, :customer, 510, :mailing, %%(sysdate)s, 599)' % company) ucurs = self.db.cursor() squery = 'SELECT email, mailing_id, bnccnt, creation_date, timestamp FROM softbounce_email_tbl ' bnccnt = self.__cfg(company, 'convert-bounce-count', 40) daydiff = self.__cfg(company, 'convert-bounce-duration', 30) squery += self.curs.qselect( oracle= 'WHERE company_id = %d AND bnccnt > %d AND timestamp-creation_date > %d' % (company, bnccnt, daydiff), mysql= 'WHERE company_id = %d AND bnccnt > %d AND DATEDIFF(timestamp,creation_date) > %d' % (company, bnccnt, daydiff)) scurs = self.db.cursor() if None in [dcurs, ucurs, scurs]: raise agn.error('Failed to setup curses') lastClick = self.__cfg(company, 'last-click', 30) lastOpen = self.__cfg(company, 'last-open', 30) def toDatetime(offset): tm = time.localtime(time.time() - offset * 24 * 60 * 60) return datetime.datetime(tm.tm_year, tm.tm_mon, tm.tm_mday) lastClickTS = toDatetime(lastClick) lastOpenTS = toDatetime(lastOpen) if Cache is not None: rcache = Cache(1000) ocache = Cache(1000 * 1000) ccurs = self.db.cursor() if None in [rcache, ocache, ccurs ] or not rcache.valid() or not ocache.valid(): raise agn.error('Failed to setup caching') agn.log(agn.LV_INFO, 'cache', 'Setup rdir log cache for %d' % company) query = 'SELECT customer_id FROM rdirlog_%d_tbl WHERE timestamp > :ts' % company parm = {'ts': lastClickTS} for record in ccurs.query(query, parm): rcache.add(record[0]) agn.log(agn.LV_INFO, 'cache', 'Setup one pixel log cache for %d' % company) query = 'SELECT customer_id FROM onepixellog_%d_tbl WHERE timestamp > :ts' % company parm = {'ts': lastOpenTS} for record in ccurs.query(query, parm): ocache.add(record[0]) ccurs.close() agn.log(agn.LV_INFO, 'cache', 'Setup completed') ccount = 0 for record in scurs.query(squery): parm = { 'email': record[0], 'mailing': record[1], 'bouncecount': record[2], 'creationdate': record[3], 'timestamp': record[4], 'customer': None } query = 'SELECT customer_id FROM customer_%d_tbl WHERE email = :email ' % company data = self.curs.querys(query, parm, cleanup=True) if data is None: continue custs = [ agn.mutable(id=_d, click=0, open=0) for _d in data if _d ] if not custs: continue if len(custs) == 1: cclause = 'customer_id = %d' % custs[0].id else: cclause = 'customer_id IN (%s)' % ', '.join( [str(_c.id) for _c in custs]) if Cache is not None: for c in custs: c.click += rcache.get(c.id, 0) c.open += ocache.get(c.id, 0) else: parm['ts'] = lastClickTS query = 'SELECT customer_id, count(*) FROM rdirlog_%d_tbl WHERE %s AND timestamp > :ts GROUP BY customer_id' % ( company, cclause) for r in self.curs.queryc(query, parm, cleanup=True): for c in custs: if c.id == r[0]: c.click += r[1] parm['ts'] = lastOpenTS query = 'SELECT customer_id, count(*) FROM onepixellog_%d_tbl WHERE %s AND timestamp > :ts GROUP BY customer_id' % ( company, cclause) for r in self.curs.queryc(query, parm, cleanup=True): for c in custs: if c.id == r[0]: c.open += r[1] for c in custs: if c.click > 0 or c.open > 0: cstat[1] += 1 agn.log( agn.LV_INFO, 'conv', 'Email %s [%d] has %d klick(s) and %d onepix(es) -> active' % (parm['email'], c.id, c.click, c.open)) else: cstat[2] += 1 agn.log( agn.LV_INFO, 'conv', 'Email %s [%d] has no klicks and no onepixes -> hardbounce' % (parm['email'], c.id)) parm['customer'] = c.id ucurs.update(uquery, parm, cleanup=True) ucurs.execute(bquery, parm, cleanup=True) dcurs.update(dquery, parm, cleanup=True) ccount += 1 if ccount % 1000 == 0: agn.log(agn.LV_INFO, 'conv', 'Commiting at %s' % agn.numfmt(ccount)) self.db.commit() if Cache is not None: rcache.done() ocache.done() self.db.commit() scurs.close() ucurs.close() dcurs.close() for cstat in stats: agn.log( agn.LV_INFO, 'conv', 'Company %d has %d active and %d marked as hardbounced users' % tuple(cstat)) agn.log(agn.LV_INFO, 'conv', 'Converting softbounces to hardbounce done')
def execute(self, param): if len(param) == 0: command = Process.knownCommands[0] selective = None else: command = param[0] if command not in Process.knownCommands: self.fail('Command %s unknown, available commands are %s' % (command, ', '.join(Process.knownCommands))) return selective = set(param[1:]) found = set() # procs = [] depends = agn.mutable(require=set(), resolved=set()) for name in [ _n for _n in self.cfg.getSectionSequence() if not _n.startswith('_') ]: if not selective or name in selective or name in depends.require: self.cfg.push(name) self.cfg.ns['service'] = name if self.active(name): try: proc = Process( self.cfg, name, lambda method, args, default: self.call( name, method, args, default)) proc.doStatus() procs.append(proc) depend = self.cfg.lget('depends-on') if depend: depends.require.update(depend) if name in depends.require: depends.resolved.add(name) except error as e: self.fail('%s: unable to setup: %s' % (proc.name, e)) self.info(e.token, e.ns) self.cfg.pop() found.add(name) if selective: diff = selective.difference(found) if diff: self.fail('unknown modules %s selected' % ', '.join(list(diff))) if depends.require != depends.resolved: self.fail( 'Failed to resolve required modules %s' % ', '.join(list(depends.require.difference(depends.resolved)))) if self.ec != 0: return # if command in ('start', 'stop', 'restart'): ta = eagn.Transaction() if command == 'stop': use = list(reversed(procs)) else: use = procs for proc in use: if command == 'start': execute = proc.doStart if proc.can('start') else None rollback = proc.doStop if proc.can('stop') else None elif command == 'stop': execute = proc.doStop if proc.can('stop') else None rollback = proc.doStart if proc.can('start') else None elif command == 'restart': execute = proc.doRestart if proc.can('start') else None rollback = proc.doRestore else: agn.log(agn.LV_ERROR, 'execute', 'Unknown command %s for %s' % (command, proc.name)) continue # ta.add(proc.name, method=execute, rollback=rollback) def callback(name, what, e=None): if what in ('start', 'rollback'): if what == 'start': what = command self.outn('%s %s .. ' % (what, name)) else: self.out('%s%s' % (what, (' (%s)' % str(e)) if e else '')) if what == 'fail': time.sleep(2) for proc in use: try: proc.doStatus() except error as e: agn.log( agn.LV_ERROR, 'rollback', 'Failed to get status for %s before rollback' % proc.name) def handler(sig, stack): pass handlers = [] for (sig, hand) in [(signal.SIGINT, handler)]: ohand = signal.signal(sig, hand) handlers.append((sig, ohand)) try: ta.execute(rollbackFailed=True, callback=callback) except Exception as e: self.fail('Rollback executed during %s: %s' % (command, str(e))) if isinstance(e, error): self.info(e.token, e.ns) for (sig, hand) in handlers: signal.signal(sig, hand) time.sleep(2) for proc in use: try: proc.doStatus() except error as e: self.fail('%s: unable to query status: %s' % (proc.name, e)) self.info(e.token, e.ns) # if procs: nlen = max([len(_p.name) for _p in procs]) + 1 format = '%%-%d.%ds: %%s' % (nlen, nlen) for proc in procs: if proc.status: if proc.comment: status = '%s (%s)' % (proc.status, proc.comment) else: status = proc.status self.out(format % (proc.name, status)) else: self.out('No active processes')