def run(threads=1, bulk=100, once=False, sleep_time=60): """ Starts up the minos threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') if once: logging.info('Will run only one iteration in a single threaded mode') minos_tu_expiration(bulk=bulk, once=once) else: logging.info('Starting Minos Temporary Expiration threads') thread_list = [ threading.Thread(target=minos_tu_expiration, kwargs={ 'once': once, 'sleep_time': sleep_time, 'bulk': bulk }) for _ in range(0, threads) ] [thread.start() for thread in thread_list] logging.info('Waiting for interrupts') # Interruptible joins require a timeout. while thread_list: thread_list = [ thread.join(timeout=3.14) for thread in thread_list if thread and thread.is_alive() ]
def run(once=False, threads=1, sleep_time=10, limit=1000): """ Starts up the Abacus-Collection-Replica threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') if once: logging.info('main: executing one iteration only') collection_replica_update(once) else: logging.info('main: starting threads') threads = [ threading.Thread(target=collection_replica_update, kwargs={ 'once': once, 'sleep_time': sleep_time, 'limit': limit }) for _ in range(0, threads) ] [t.start() for t in threads] logging.info('main: waiting for interrupts') # Interruptible joins require a timeout. while threads[0].is_alive(): [t.join(timeout=3.14) for t in threads]
def run(threads=1, bulk=100, date_check=None, dry_run=True, grace_period=86400, once=True, unlock=False, spread_period=0, purge_replicas=False, sleep_time=60): """ Starts up the atropos threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') if not date_check: date_check = datetime.datetime.now() else: date_check = datetime.datetime.strptime(date_check, '%Y-%m-%d') if once: logging.info('Will run only one iteration') logging.info('starting atropos threads') thread_list = [threading.Thread(target=atropos, kwargs={'once': once, 'thread': i, 'date_check': date_check, 'dry_run': dry_run, 'grace_period': grace_period, 'bulk': bulk, 'unlock': unlock, 'spread_period': spread_period, 'purge_replicas': purge_replicas, 'sleep_time': sleep_time}) for i in range(0, threads)] [t.start() for t in thread_list] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while thread_list: thread_list = [t.join(timeout=3.14) for t in thread_list if t and t.is_alive()]
def run(total_workers=1, once=False, inputfile=None, sleep_time=-1): """ Starts up the automatix threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') try: sites = [s.strip() for s in config_get('automatix', 'sites').split(',')] except (NoOptionError, NoSectionError, RuntimeError): raise Exception('Could not load sites from configuration') if not inputfile: inputfile = '/opt/rucio/etc/automatix.json' if sleep_time == -1: try: sleep_time = config_get('automatix', 'sleep_time') except (NoOptionError, NoSectionError, RuntimeError): sleep_time = 30 try: account = config_get('automatix', 'account') except (NoOptionError, NoSectionError, RuntimeError): account = 'root' try: dataset_lifetime = config_get('automatix', 'dataset_lifetime') except (NoOptionError, NoSectionError, RuntimeError): dataset_lifetime = None try: set_metadata = config_get('automatix', 'set_metadata') except (NoOptionError, NoSectionError, RuntimeError): set_metadata = False try: scope = config_get('automatix', 'scope') client = Client() filters = {'scope': InternalScope('*', vo=client.vo)} if InternalScope(scope, vo=client.vo) not in list_scopes(filter_=filters): logging.log(logging.ERROR, 'Scope %s does not exist. Exiting', scope) GRACEFUL_STOP.set() except Exception: scope = False threads = list() for worker_number in range(0, total_workers): kwargs = {'worker_number': worker_number, 'total_workers': total_workers, 'once': once, 'sites': sites, 'sleep_time': sleep_time, 'account': account, 'inputfile': inputfile, 'set_metadata': set_metadata, 'scope': scope, 'dataset_lifetime': dataset_lifetime} threads.append(threading.Thread(target=automatix, kwargs=kwargs)) [thread.start() for thread in threads] while threads[0].is_alive(): logging.log(logging.DEBUG, 'Still %i active threads', len(threads)) [thread.join(timeout=3.14) for thread in threads]
def run(once=False, threads=1, sleep_time=10, bulk=100): """ Running the preparer daemon either once or by default in a loop until stop is called. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') def preparer_kwargs(): # not sure if this is needed for threading.Thread, but it always returns a fresh dictionary return {'once': once, 'sleep_time': sleep_time, 'bulk': bulk} threads = [threading.Thread(target=preparer, name=f'conveyor-preparer-{i}', kwargs=preparer_kwargs(), daemon=True) for i in range(threads)] for thr in threads: thr.start() all_running = True while all_running: for thr in threads: thr.join(timeout=3.14) if not thr.is_alive() or graceful_stop.is_set(): all_running = False break if graceful_stop.is_set() or once: logging.info('conveyor-preparer: gracefully stopping') else: logging.warning('conveyor-preparer: stopping out of the ordinary') graceful_stop.set() for thr in threads: thr.join(timeout=3.14) logging.info('conveyor-preparer: stopped')
def run(once=False, threads=1, fill_history_table=False, sleep_time=10): """ Starts up the Abacus-RSE threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') executable = 'abacus-rse' hostname = socket.gethostname() sanity_check(executable=executable, hostname=hostname) if once: logging.info('main: executing one iteration only') rse_update(once) else: logging.info('main: starting threads') threads = [ threading.Thread(target=rse_update, kwargs={ 'once': once, 'sleep_time': sleep_time }) for i in range(0, threads) ] if fill_history_table: threads.append( get_thread_with_periodic_running_function( 3600, fill_rse_counter_history_table, graceful_stop)) [t.start() for t in threads] logging.info('main: waiting for interrupts') # Interruptible joins require a timeout. while threads[0].is_alive(): [t.join(timeout=3.14) for t in threads]
def run(once=False, threads=1): """ Starts up the follower threads """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') hostname = socket.gethostname() sanity_check(executable='rucio-follower', hostname=hostname) if once: logging.info("executing one follower iteration only") aggregate_events(once) else: logging.info("starting follower threads") # Run the follower daemon thrice a day threads = [ get_thread_with_periodic_running_function(28800, aggregate_events, graceful_stop) for i in range(threads) ] [t.start() for t in threads] logging.info("waiting for interrupts") # Interruptible joins require a timeout. while threads[0].is_alive(): [t.join(timeout=3.14) for t in threads]
def run(once=False, sleep_time=600): """ Starts up the conveyer threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') if once: logging.info('running throttler one iteration only') throttler(once=True, sleep_time=sleep_time) else: threads = [] logging.info('starting throttler thread') throttler_thread = threading.Thread(target=throttler, kwargs={ 'once': once, 'sleep_time': sleep_time }) threads.append(throttler_thread) [thread.start() for thread in threads] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while threads: threads = [ thread.join(timeout=3.14) for thread in threads if thread and thread.is_alive() ]
def run(num_thread=1): """ Starts up the rucio cache consumer thread """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') logging.info('starting consumer thread') threads = [ threading.Thread(target=consumer, kwargs={ 'id': i, 'num_thread': num_thread }) for i in range(0, num_thread) ] [t.start() for t in threads] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while threads[0].isAlive(): [t.join(timeout=3.14) for t in threads]
def run(threads=1, bulk=100, once=False): """ Starts up the necromancer threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') if once: logging.info('Will run only one iteration in a single threaded mode') necromancer(bulk=bulk, once=once) else: logging.info('starting necromancer threads') thread_list = [ threading.Thread(target=necromancer, kwargs={ 'once': once, 'thread': i, 'bulk': bulk }) for i in range(0, threads) ] [t.start() for t in thread_list] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while thread_list: thread_list = [ thread.join(timeout=3.14) for thread in thread_list if thread and thread.isAlive() ]
def run(once=False, threads=1): """ Starts up the Judge-Clean threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') client_time, db_time = datetime.utcnow(), get_db_time() max_offset = timedelta(hours=1, seconds=10) if type(db_time) is datetime: if db_time - client_time > max_offset or client_time - db_time > max_offset: logging.critical( 'Offset between client and db time too big. Stopping Cleaner') return executable = 'judge-cleaner' hostname = socket.gethostname() sanity_check(executable=executable, hostname=hostname) if once: rule_cleaner(once) else: logging.info('Cleaner starting %s threads' % str(threads)) threads = [ threading.Thread(target=rule_cleaner, kwargs={'once': once}) for i in range(0, threads) ] [t.start() for t in threads] # Interruptible joins require a timeout. while threads[0].is_alive(): [t.join(timeout=3.14) for t in threads]
def run(once=False, total_threads=1, full_mode=False): """ Starts up the receiver thread """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') logging.info('starting receiver thread') threads = [ threading.Thread(target=receiver, kwargs={ 'id_': i, 'full_mode': full_mode, 'total_threads': total_threads }) for i in range(0, total_threads) ] [thread.start() for thread in threads] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while threads: threads = [ thread.join(timeout=3.14) for thread in threads if thread and thread.is_alive() ]
def add_identity(identity, type, email, password=None, session=None): """ Creates a user identity. :param identity: The identity key name. For example x509 DN, or a username. :param type: The type of the authentication (x509, gss, userpass, ssh, saml, oidc) :param email: The Email address associated with the identity. :param password: If type==userpass, this sets the password. :param session: The database session in use. """ if type == IdentityType.USERPASS and password is None: raise exception.IdentityError('You must provide a password!') new_id = models.Identity() new_id.update({'identity': identity, 'identity_type': type, 'email': email}) if type == IdentityType.USERPASS and password is not None: salt = os.urandom(255) # make sure the salt has the length of the hash if six.PY3: decoded_salt = b64encode(salt).decode() salted_password = ('%s%s' % (decoded_salt, password)).encode() else: salted_password = '******' % (salt, str(password)) password = hashlib.sha256(salted_password).hexdigest() # hash it new_id.update({'salt': salt, 'password': password, 'email': email}) try: new_id.save(session=session) except IntegrityError as e: if match('.*IntegrityError.*1062.*Duplicate entry.*for key.*', e.args[0]): raise exception.Duplicate('Identity pair \'%s\',\'%s\' already exists!' % (identity, type)) raise exception.DatabaseException(str(e))
def run(once=False, threads=1): """ Starts up the Abacus-Collection-Replica threads. """ if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') executable = 'abacus-collection-replica' hostname = socket.gethostname() sanity_check(executable=executable, hostname=hostname) if once: logging.info('main: executing one iteration only') collection_replica_update(once) else: logging.info('main: starting threads') threads = [ threading.Thread(target=collection_replica_update, kwargs={'once': once}) for i in range(0, threads) ] [t.start() for t in threads] logging.info('main: waiting for interrupts') # Interruptible joins require a timeout. while threads[0].is_alive(): [t.join(timeout=3.14) for t in threads]
def run(once=False, last_nhours=1, external_hosts=None, fts_wait=1800, total_threads=1): """ Starts up the conveyer threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') if not external_hosts: external_hosts = [] if once: logging.info('executing one poller iteration only') poller_latest(external_hosts, once=once, last_nhours=last_nhours) else: logging.info('starting poller threads') threads = [threading.Thread(target=poller_latest, kwargs={'external_hosts': external_hosts, 'fts_wait': fts_wait, 'last_nhours': last_nhours}) for _ in range(0, total_threads)] [thread.start() for thread in threads] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while threads: threads = [thread.join(timeout=3.14) for thread in threads if thread and thread.isAlive()]
def run(once=False, threads=1): """ Starts up the Judge-Repairer threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') executable = 'judge-repairer' hostname = socket.gethostname() sanity_check(executable=executable, hostname=hostname) if once: rule_repairer(once) else: logging.info('Repairer starting %s threads' % str(threads)) threads = [ threading.Thread(target=rule_repairer, kwargs={'once': once}) for i in range(0, threads) ] [t.start() for t in threads] # Interruptible joins require a timeout. while threads[0].is_alive(): [t.join(timeout=3.14) for t in threads]
def run(once=False, group_bulk=1, group_policy='rule', mock=False, rses=None, include_rses=None, exclude_rses=None, vos=None, bulk=100, source_strategy=None, activities=None, exclude_activities=None, sleep_time=600, max_sources=4, retry_other_fts=False, total_threads=1): """ Starts up the conveyer threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') multi_vo = config_get_bool('common', 'multi_vo', raise_exception=False, default=False) working_rses = None if rses or include_rses or exclude_rses: working_rses = get_conveyor_rses(rses, include_rses, exclude_rses, vos) logging.info("RSE selection: RSEs: %s, Include: %s, Exclude: %s", rses, include_rses, exclude_rses) elif multi_vo: working_rses = get_conveyor_rses(rses, include_rses, exclude_rses, vos) logging.info("RSE selection: automatic for relevant VOs") else: logging.info("RSE selection: automatic") logging.info('starting submitter threads') if exclude_activities: if not activities: if not multi_vo: vos = ['def'] if vos and len(vos) == 1: activities = get_schema_value('ACTIVITY', vos[0]) elif vos and len(vos) > 1: logging.warning('Cannot get activity list from schema when multiple VOs given, either provide `activities` argument or run on a single VO') activities = [None] else: logging.warning('Cannot get activity list from schema when no VO given, either provide `activities` argument or `vos` with a single entry') activities = [None] for activity in exclude_activities: if activity in activities: activities.remove(activity) threads = [threading.Thread(target=submitter, kwargs={'once': once, 'rses': working_rses, 'bulk': bulk, 'group_bulk': group_bulk, 'group_policy': group_policy, 'activities': activities, 'sleep_time': sleep_time, 'max_sources': max_sources, 'source_strategy': source_strategy, 'retry_other_fts': retry_other_fts}) for _ in range(0, total_threads)] [thread.start() for thread in threads] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while threads: threads = [thread.join(timeout=3.14) for thread in threads if thread and thread.isAlive()]
def run(): """ Runs the distribution daemon """ if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') thread = threading.Thread(target=run_distribution, kwargs={}) thread.start() while thread and thread.isAlive(): thread.join(timeout=3.14)
def run(once=False, total_threads=1, group_bulk=1, group_policy='rule', rses=None, include_rses=None, exclude_rses=None, vos=None, bulk=100, source_strategy=None, activities=[], sleep_time=600): """ Starts up the conveyer threads. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') multi_vo = config_get_bool('common', 'multi_vo', raise_exception=False, default=False) working_rses = None if rses or include_rses or exclude_rses: working_rses = get_conveyor_rses(rses, include_rses, exclude_rses, vos) logging.info("RSE selection: RSEs: %s, Include: %s, Exclude: %s" % (rses, include_rses, exclude_rses)) elif multi_vo: working_rses = get_conveyor_rses(rses, include_rses, exclude_rses, vos) logging.info("RSE selection: automatic for relevant VOs") else: logging.info("RSE selection: automatic") if once: logging.info('executing one stager iteration only') stager(once, rses=working_rses, bulk=bulk, group_bulk=group_bulk, group_policy=group_policy, source_strategy=source_strategy, activities=activities) else: logging.info('starting stager threads') threads = [threading.Thread(target=stager, kwargs={'rses': working_rses, 'bulk': bulk, 'group_bulk': group_bulk, 'group_policy': group_policy, 'activities': activities, 'sleep_time': sleep_time, 'source_strategy': source_strategy}) for _ in range(0, total_threads)] [thread.start() for thread in threads] logging.info('waiting for interrupts') # Interruptible joins require a timeout. while threads: threads = [thread.join(timeout=3.14) for thread in threads if thread and thread.is_alive()]
def run(once=False, cycle_interval=3600): """ Starts up the conveyer fts throttler thread. """ if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') logging.info('starting throttler thread') fts_throttler_thread = threading.Thread(target=fts_throttler, kwargs={'once': once, 'cycle_interval': cycle_interval}) fts_throttler_thread.start() logging.info('waiting for interrupts') # Interruptible joins require a timeout. fts_throttler_thread.join(timeout=3.14)
def run(total_workers=1, chunk_size=100, once=False, rses=[], scheme=None, exclude_rses=None, include_rses=None, vos=None, delay_seconds=0, sleep_time=60): """ Starts up the reaper threads. :param total_workers: The total number of workers. :param chunk_size: the size of chunk for deletion. :param once: If True, only runs one iteration of the main loop. :param rses: List of RSEs the reaper should work against. If empty, it considers all RSEs (Single-VO only). :param scheme: Force the reaper to use a particular protocol/scheme, e.g., mock. :param exclude_rses: RSE expression to exclude RSEs from the Reaper. :param include_rses: RSE expression to include RSEs. :param vos: VOs on which to look for RSEs. Only used in multi-VO mode. If None, we either use all VOs if run from "def", or the current VO otherwise. """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') logging.info('main: starting processes') multi_vo = config_get_bool('common', 'multi_vo', raise_exception=False, default=False) if not multi_vo: if vos: logging.warning( 'Ignoring argument vos, this is only applicable in a multi-VO setup.' ) vos = ['def'] else: if vos: invalid = set(vos) - set([v['vo'] for v in list_vos()]) if invalid: msg = 'VO{} {} cannot be found'.format( 's' if len(invalid) > 1 else '', ', '.join([repr(v) for v in invalid])) raise VONotFound(msg) else: vos = [v['vo'] for v in list_vos()] logging.info('Dark Reaper: This instance will work on VO%s: %s' % ('s' if len(vos) > 1 else '', ', '.join([v for v in vos]))) all_rses = [] for vo in vos: all_rses.extend( [rse['id'] for rse in rse_core.list_rses(filters={'vo': vo})]) if rses: invalid = set(rses) - set([rse['rse'] for rse in all_rses]) if invalid: msg = 'RSE{} {} cannot be found'.format( 's' if len(invalid) > 1 else '', ', '.join([repr(rse) for rse in invalid])) raise RSENotFound(msg) rses = [rse for rse in all_rses if rse['rse'] in rses] else: rses = all_rses if exclude_rses: excluded_rses = [rse['id'] for rse in parse_expression(exclude_rses)] rses = [rse for rse in rses if rse not in excluded_rses] if include_rses: included_rses = [rse['id'] for rse in parse_expression(include_rses)] rses = [rse for rse in rses if rse in included_rses] if not rses: logging.error('Dark Reaper: No RSEs found. Exiting.') return threads = [] for worker in range(total_workers): kwargs = { 'worker_number': worker, 'total_workers': total_workers, 'rses': rses, 'once': once, 'chunk_size': chunk_size, 'scheme': scheme, 'sleep_time': sleep_time } threads.append( threading.Thread(target=reaper, kwargs=kwargs, name='Worker: %s, Total_Workers: %s' % (worker, total_workers))) [t.start() for t in threads] while threads[0].is_alive(): [t.join(timeout=3.14) for t in threads]
def run(once=False, threads=1, only_workload=False, dry_run=False, sampling=False, algorithms='t2_free_space_only_pop_with_network', datatypes='NTUP,DAOD', dest_rse_expr='type=DATADISK', max_bytes_hour=100000000000000, max_files_hour=100000, max_bytes_hour_rse=50000000000000, max_files_hour_rse=10000, min_popularity=8, min_recent_requests=5, max_replicas=5, waiting_time_read_free_space=1800, waiting_time_read_workload=1800, waiting_time_print_workload=600, waiting_time_read_dids=60, waiting_time_place_replica=100, sleep_time=10): """ Starts up the main thread """ setup_logging() if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException('Database was not updated, daemon won\'t start') logging.info('activating C-3PO') thread_list = [] try: if only_workload: logging.info('running in workload-collector-only mode') thread_list.append(Thread(target=read_workload, name='read_workload', kwargs={'thread': 0, 'waiting_time': waiting_time_read_workload, 'sleep_time': sleep_time})) thread_list.append(Thread(target=print_workload, name='print_workload', kwargs={'thread': 0, 'waiting_time': waiting_time_print_workload, 'sleep_time': sleep_time})) else: logging.info('running in placement mode') did_queue = Queue() dc = JediDIDCollector(did_queue) thread_list.append(Thread(target=read_free_space, name='read_free_space', kwargs={'thread': 0, 'waiting_time': waiting_time_read_free_space, 'sleep_time': sleep_time})) thread_list.append(Thread(target=read_dids, name='read_dids', kwargs={'thread': 0, 'did_collector': dc, 'waiting_time': waiting_time_read_dids, 'sleep_time': sleep_time})) thread_list.append(Thread(target=place_replica, name='place_replica', kwargs={'thread': 0, 'did_queue': did_queue, 'waiting_time': waiting_time_place_replica, 'algorithms': algorithms, 'dry_run': dry_run, 'sampling': sampling, 'datatypes': datatypes, 'dest_rse_expr': dest_rse_expr, 'max_bytes_hour': max_bytes_hour, 'max_files_hour': max_files_hour, 'max_bytes_hour_rse': max_bytes_hour_rse, 'max_files_hour_rse': max_files_hour_rse, 'min_popularity': min_popularity, 'min_recent_requests': min_recent_requests, 'max_replicas': max_replicas, 'sleep_time': sleep_time})) for t in thread_list: t.start() logging.info('waiting for interrupts') while len(thread_list) > 0: [t.join(timeout=3) for t in thread_list if t and t.is_alive()] except Exception as error: logging.critical(error)
def run(once=False, send_email=True, threads=1, bulk=1000, delay=10, broker_timeout=3, broker_retry=3): ''' Starts up the hermes threads. ''' if rucio.db.sqla.util.is_old_db(): raise exception.DatabaseException( 'Database was not updated, daemon won\'t start') logging.info('resolving brokers') brokers_alias = [] brokers_resolved = [] try: brokers_alias = [ b.strip() for b in config_get('messaging-hermes', 'brokers').split(',') ] except: raise Exception('Could not load brokers from configuration') logging.info('resolving broker dns alias: %s', brokers_alias) brokers_resolved = [] for broker in brokers_alias: try: addrinfos = socket.getaddrinfo(broker, 0, socket.AF_INET, 0, socket.IPPROTO_TCP) brokers_resolved.extend(ai[4][0] for ai in addrinfos) except socket.gaierror as ex: logging.error('Cannot resolve domain name %s (%s)', broker, str(ex)) logging.debug('brokers resolved to %s', brokers_resolved) if once: logging.info('executing one hermes iteration only') deliver_messages(once=once, brokers_resolved=brokers_resolved, bulk=bulk, delay=delay, broker_timeout=broker_timeout, broker_retry=broker_retry) deliver_emails(once=once, send_email=send_email, bulk=bulk, delay=delay) else: logging.info('starting hermes threads') thread_list = [ threading.Thread(target=deliver_messages, kwargs={ 'brokers_resolved': brokers_resolved, 'thread': i, 'bulk': bulk, 'delay': delay, 'broker_timeout': broker_timeout, 'broker_retry': broker_retry }) for i in range(0, threads) ] for thrd in range(0, 1): thread_list.append( threading.Thread(target=deliver_emails, kwargs={ 'thread': thrd, 'bulk': bulk, 'delay': delay })) for thrd in thread_list: thrd.start() logging.info('waiting for interrupts') # Interruptible joins require a timeout. while thread_list: thread_list = [ t.join(timeout=3.14) for t in thread_list if t and t.isAlive() ]