def main(trackerbot_url, mark_usable=None, selected_provider=None): api = trackerbot.api(trackerbot_url) thread_q = [] thread_lock = Lock() template_providers = defaultdict(list) all_providers = (set(list_provider_keys()) if not selected_provider else set(selected_provider)) unresponsive_providers = set() # Queue up list_template calls for provider_key in all_providers: ipaddress = cfme_data.management_systems[provider_key].get('ipaddress') if ipaddress and not net.is_pingable(ipaddress): continue thread = Thread(target=get_provider_templates, args=(provider_key, template_providers, unresponsive_providers, thread_lock)) thread_q.append(thread) thread.start() # Join the queued calls for thread in thread_q: thread.join() seen_templates = set() if mark_usable is None: usable = {} else: usable = {'usable': mark_usable} existing_provider_templates = [ pt['id'] for pt in trackerbot.depaginate( api, api.providertemplate.get())['objects'] ] # Find some templates and update the API for template_name, providers in template_providers.items(): template_name = str(template_name) template_info = TemplateName.parse_template(template_name) # Don't want sprout templates if template_info.group_name in ('sprout', 'rhevm-internal'): logger.info('Ignoring %s from group %s', template_name, template_info.group_name) continue seen_templates.add(template_name) group = trackerbot.Group(template_info.group_name, stream=template_info.stream) try: template = trackerbot.Template(template_name, group, template_info.datestamp) except ValueError: logger.exception('Failure parsing provider %s template: %s', provider_key, template_name) continue for provider_key in providers: provider = trackerbot.Provider(provider_key) if '{}_{}'.format(template_name, provider_key) in existing_provider_templates: logger.info('Template %s already tracked for provider %s', template_name, provider_key) continue try: trackerbot.mark_provider_template(api, provider, template, **usable) logger.info( 'Added %s template %s on provider %s (datestamp: %s)', template_info.group_name, template_name, provider_key, template_info.datestamp) except SlumberHttpBaseException: logger.exception('%s: exception marking template %s', provider, template) # Remove provider relationships where they no longer exist, skipping unresponsive providers, # and providers not known to this environment for pt in trackerbot.depaginate(api, api.providertemplate.get())['objects']: key, template_name = pt['provider']['key'], pt['template']['name'] if key not in template_providers[ template_name] and key not in unresponsive_providers: if key in all_providers: logger.info("Cleaning up template %s on %s", template_name, key) trackerbot.delete_provider_template(api, key, template_name) else: logger.info( "Skipping template cleanup %s on unknown provider %s", template_name, key) # Remove templates that aren't on any providers anymore for template in trackerbot.depaginate(api, api.template.get())['objects']: if not template['providers']: logger.info("Deleting template %s (no providers)", template['name']) api.template(template['name']).delete()
def main(trackerbot_url, mark_usable=None): api = trackerbot.api(trackerbot_url) thread_q = [] thread_lock = Lock() template_providers = defaultdict(list) all_providers = set(list_provider_keys()) unresponsive_providers = set() # Queue up list_template calls for provider_key in all_providers: ipaddress = cfme_data['management_systems'][provider_key].get('ipaddress') if ipaddress and not net.is_pingable(ipaddress): continue thread = Thread(target=get_provider_templates, args=(provider_key, template_providers, unresponsive_providers, thread_lock)) thread_q.append(thread) thread.start() # Join the queued calls for thread in thread_q: thread.join() seen_templates = set() if mark_usable is None: usable = {} else: usable = {'usable': mark_usable} existing_provider_templates = [ pt['id'] for pt in trackerbot.depaginate(api, api.providertemplate.get())['objects']] # Find some templates and update the API for template_name, providers in template_providers.items(): template_name = str(template_name) group_name, datestamp, stream = trackerbot.parse_template(template_name) # Don't want sprout templates if group_name in ('sprout', 'rhevm-internal'): print('Ignoring {} from group {}'.format(template_name, group_name)) continue seen_templates.add(template_name) group = trackerbot.Group(group_name, stream=stream) template = trackerbot.Template(template_name, group, datestamp) for provider_key in providers: provider = trackerbot.Provider(provider_key) if '{}_{}'.format(template_name, provider_key) in existing_provider_templates: print('Template {} already tracked for provider {}'.format( template_name, provider_key)) continue try: trackerbot.mark_provider_template(api, provider, template, **usable) print('Added {} template {} on provider {} (datestamp: {})'.format( group_name, template_name, provider_key, datestamp)) except SlumberHttpBaseException as ex: print("{}\t{}".format(ex.response.status_code, ex.content)) # Remove provider relationships where they no longer exist, skipping unresponsive providers, # and providers not known to this environment for pt in trackerbot.depaginate(api, api.providertemplate.get())['objects']: provider_key, template_name = pt['provider']['key'], pt['template']['name'] if provider_key not in template_providers[template_name] \ and provider_key not in unresponsive_providers: if provider_key in all_providers: print("Cleaning up template {} on {}".format(template_name, provider_key)) trackerbot.delete_provider_template(api, provider_key, template_name) else: print("Skipping template cleanup {} on unknown provider {}".format( template_name, provider_key)) # Remove templates that aren't on any providers anymore for template in trackerbot.depaginate(api, api.template.get())['objects']: if not template['providers']: print("Deleting template {} (no providers)".format(template['name'])) api.template(template['name']).delete()
def main(trackerbot_url, mark_usable=None, selected_provider=None): api = trackerbot.api(trackerbot_url) thread_q = [] thread_lock = Lock() template_providers = defaultdict(list) all_providers = (set(list_provider_keys()) if not selected_provider else set(selected_provider)) unresponsive_providers = set() # Queue up list_template calls for provider_key in all_providers: ipaddress = cfme_data.management_systems[provider_key].get('ipaddress') if ipaddress and not net.is_pingable(ipaddress): continue thread = Thread(target=get_provider_templates, args=(provider_key, template_providers, unresponsive_providers, thread_lock)) thread_q.append(thread) thread.start() # Join the queued calls for thread in thread_q: thread.join() seen_templates = set() if mark_usable is None: usable = {} else: usable = {'usable': mark_usable} existing_provider_templates = [ pt['id'] for pt in trackerbot.depaginate(api, api.providertemplate.get())['objects']] # Find some templates and update the API for template_name, providers in template_providers.items(): template_name = str(template_name) template_info = TemplateName.parse_template(template_name) # it turned out that some providers like ec2 may have templates w/o names. # this is easy protection against such issue. if not template_name.strip(): logger.warn('Ignoring template w/o name on provider %s', provider_key) continue # Don't want sprout templates if template_info.group_name in ('sprout', 'rhevm-internal'): logger.info('Ignoring %s from group %s', template_name, template_info.group_name) continue seen_templates.add(template_name) group = trackerbot.Group(template_info.group_name, stream=template_info.stream) try: template = trackerbot.Template(template_name, group, template_info.datestamp) except ValueError: logger.exception('Failure parsing provider %s template: %s', provider_key, template_name) continue for provider_key in providers: provider = trackerbot.Provider(provider_key) if '{}_{}'.format(template_name, provider_key) in existing_provider_templates: logger.info('Template %s already tracked for provider %s', template_name, provider_key) continue try: trackerbot.mark_provider_template(api, provider, template, **usable) logger.info('Added %s template %s on provider %s (datestamp: %s)', template_info.group_name, template_name, provider_key, template_info.datestamp) except SlumberHttpBaseException: logger.exception('%s: exception marking template %s', provider, template) # Remove provider relationships where they no longer exist, skipping unresponsive providers, # and providers not known to this environment for pt in trackerbot.depaginate(api, api.providertemplate.get())['objects']: key, template_name = pt['provider']['key'], pt['template']['name'] if key not in template_providers[template_name] and key not in unresponsive_providers: if key in all_providers: logger.info("Cleaning up template %s on %s", template_name, key) trackerbot.delete_provider_template(api, key, template_name) else: logger.info("Skipping template cleanup %s on unknown provider %s", template_name, key) # Remove templates that aren't on any providers anymore for template in trackerbot.depaginate(api, api.template.get())['objects']: if not template['providers'] and template['name'].strip(): logger.info("Deleting template %s (no providers)", template['name']) api.template(template['name']).delete()
def main(trackerbot_url, mark_usable=None, selected_provider=None, **kwargs): tb_api = trackerbot.api(trackerbot_url) all_providers = set(selected_provider or [ key for key, data in cfme_data.management_systems.items() if 'disabled' not in data.get('tags', []) ]) bad_providers = manager.Queue() # starmap the list of provider_keys into templates_on_provider # return is list of ProvTemplate tuples with ThreadPool(8) as pool: mgmt_templates = pool.starmap(templates_on_provider, ((provider_key, bad_providers) for provider_key in all_providers)) # filter out the misbehaving providers bad_provider_keys = [] while not bad_providers.empty(): bad_provider_keys.append(bad_providers.get()) logger.warning('Filtering out providers that failed template query: %s', bad_provider_keys) working_providers = { key for key in all_providers if key not in bad_provider_keys } # Flip mgmt_templates into dict keyed on template name, listing providers # [ # {prov1: [t1, t2]}, # {prov2: [t1, t3]}, # ] # # mgmt_providertemplates should look like: # { # t1: [prov1, prov2], # t2: [prov1], # t3: [prov2] # } mgmt_providertemplates = defaultdict(list) # filter out any empty results from pulling mgmt_templates for prov_templates in [mt for mt in mgmt_templates if mt is not None]: # expecting one key (provider), one value (list of templates) for prov_key, templates in prov_templates.items(): for template in templates: mgmt_providertemplates[template].append(prov_key) logger.debug('DEBUG: template_providers: %r', mgmt_providertemplates) logger.debug('DEBUG: working_providers: %r', working_providers) usable = {'usable': mark_usable} if mark_usable is not None else {} # init these outside conditions/looping to be safe in reporting ignored_providertemplates = defaultdict(list) tb_pts_to_add = list() tb_pts_to_delete = list() tb_templates_to_delete = list() # ADD PROVIDERTEMPLATES # add all parseable providertemplates from what is actually on providers for template_name, provider_keys in mgmt_providertemplates.items(): # drop empty names, or sprout groups # go over templates pulled from provider mgmt interfaces, if template_name.strip() == '': logger.info('Ignoring empty name template on providers %s', provider_keys) template_info = TemplateName.parse_template(template_name) template_group = template_info.group_name # Don't want sprout templates, or templates that aren't parsable cfme/MIQ if template_group in GROUPS_TO_IGNORE: ignored_providertemplates[template_group].append(template_name) continue tb_pts_to_add = [ ( template_group, provider_key, template_name, None, # custom_data usable) for provider_key in provider_keys ] logger.info( 'Threading add providertemplate records to trackerbot for %s', template_name) with ThreadPool(8) as pool: # thread for each template, passing the list of providers with the template add_results = pool.starmap(trackerbot.add_provider_template, tb_pts_to_add) if not all( [True if result in [None, True] else False for result in add_results]): # ignore results that are None, warn for any false results from adding logger.warning('Trackerbot providertemplate add failed, see logs') for group, names in ignored_providertemplates.items(): logger.info('Skipped group [%s] templates %r', group, names) # REMOVE PROVIDERTEMPLATES # Remove provider relationships where they no longer exist, skipping unresponsive providers, # and providers not known to this environment logger.info( 'Querying providertemplate records from Trackerbot for ones to delete') pts = trackerbot.depaginate( tb_api, tb_api.providertemplate.get(provider_in=working_providers))['objects'] for pt in pts: key = pt['provider']['key'] pt_name, pt_group = pt['template']['name'], pt['template']['group'][ 'name'] if pt_group in GROUPS_TO_IGNORE or key not in mgmt_providertemplates[ pt_name]: logger.info( "Marking trackerbot providertemplate for delete: %s::%s", key, pt_name) tb_pts_to_delete.append(ProvTemplate(key, pt_name)) with ThreadPool(8) as pool: # thread for each delete_provider_template call pool.starmap(trackerbot.delete_provider_template, ((tb_api, pt.provider_key, pt.template_name) for pt in tb_pts_to_delete)) # REMOVE TEMPLATES # Remove templates that aren't on any providers anymore for template in trackerbot.depaginate(tb_api, tb_api.template.get())['objects']: template_name = template['name'] if not template['providers'] and template_name.strip(): logger.info("Deleting trackerbot template %s (no providers)", template_name) tb_templates_to_delete.append(template_name) tb_api.template(template_name).delete() # WRITE REPORT with open(kwargs.get('outfile'), 'a') as report: add_header = '##### ProviderTemplate records added: #####\n' del_header = '##### ProviderTemplate records deleted: #####\n' report.write(add_header) add_message = tabulate(sorted([(ptadd[0], ptadd[1], ptadd[2]) for ptadd in tb_pts_to_add], key=lambda ptadd: ptadd[0]), headers=['Group', 'Provider', 'Template'], tablefmt='orgtbl') report.write(f'{add_message}\n\n') report.write(del_header) del_message = tabulate(sorted( [(ptdel.provider_key, ptdel.template_name) for ptdel in tb_pts_to_delete], key=lambda ptdel: ptdel[0]), headers=['Provider', 'Template'], tablefmt='orgtbl') report.write(del_message) logger.info('%s %s', add_header, add_message) logger.info('%s %s', del_header, del_message) return 0
def poke_trackerbot(self): """This beat-scheduled task periodically polls the trackerbot if there are any new templates. """ template_usability = [] # Extract data from trackerbot tbapi = trackerbot() objects = depaginate( tbapi, tbapi.providertemplate().get(limit=TRACKERBOT_PAGINATE))["objects"] per_group = {} for obj in objects: if obj["template"]["group"]["name"] == 'unknown': continue if obj["template"]["group"]["name"] not in per_group: per_group[obj["template"]["group"]["name"]] = [] per_group[obj["template"]["group"]["name"]].append(obj) # Sort them using the build date for group in list(per_group.keys()): per_group[group] = sorted(per_group[group], reverse=True, key=lambda o: o["template"]["datestamp"]) objects = [] # And interleave the the groups while any(per_group.values()): for key in list(per_group.keys()): if per_group[key]: objects.append(per_group[key].pop(0)) for template in objects: if template["provider"]["key"] not in list( conf.cfme_data.management_systems.keys()): # If we don't use that provider in yamls, set the template as not usable # 1) It will prevent adding this template if not added # 2) It'll mark the template as unusable if it already exists template["usable"] = False template_usability.append( (template["provider"]["key"], template["template"]["name"], template["usable"])) if not template["usable"]: continue group, create = Group.objects.get_or_create( id=template["template"]["group"]["name"]) # Check if the template is already obsolete if group.template_obsolete_days is not None: build_date = parsetime.from_iso_date( template["template"]["datestamp"]) if build_date <= (parsetime.today() - timedelta(days=group.template_obsolete_days)): # It is already obsolete, so ignore it continue if conf.cfme_data.management_systems.get( template["provider"]["key"], {}).get( "use_for_sprout", False ): # only create provider in db if it is marked to use for sprout provider, create = Provider.objects.get_or_create( id=template["provider"]["key"]) else: continue if not provider.is_working: continue if "sprout" not in provider.provider_data: continue if not provider.provider_type: provider.provider_type = provider.provider_data.get('type') provider.save(update_fields=['provider_type']) template_name = template["template"]["name"] ga_released = template['template']['ga_released'] custom_data = template['template'].get('custom_data', "{}") processed_custom_data = yaml.safe_load(custom_data) template_info = TemplateName.parse_template(template_name) if not template_info.datestamp: # Not a CFME/MIQ template, ignore it. continue # Original one original_template = None try: original_template = Template.objects.get( provider=provider, template_group=group, original_name=template_name, name=template_name, preconfigured=False) if original_template.ga_released != ga_released: original_template.ga_released = ga_released original_template.save(update_fields=['ga_released']) if provider.provider_type == 'openshift': if original_template.custom_data != custom_data: original_template.custom_data = processed_custom_data original_template.template_type = Template.OPENSHIFT_POD original_template.container = 'cloudforms-0' original_template.save(update_fields=[ 'custom_data', 'container', 'template_type' ]) except ObjectDoesNotExist: if template_name in provider.templates: if template_info.datestamp is None: self.logger.warning( "Ignoring template {} because it does not have a date!" .format(template_name)) continue template_version = template_info.version if template_version is None: # Make up a faux version # First 3 fields of version get parsed as a zstream # therefore ... makes it a "nil" stream template_version = "...{}".format( template_info.datestamp.strftime("%Y%m%d")) # openshift has two templates bound to one build version # sometimes sprout tries using second template -extdb that's wrong # so, such template has to be marked as not usable inside sprout usable = not template_name.endswith('-extdb') with transaction.atomic(): tpl = Template(provider=provider, template_group=group, original_name=template_name, name=template_name, preconfigured=False, date=template_info.datestamp, ready=True, exists=True, usable=usable, version=template_version) tpl.save() if provider.provider_type == 'openshift': tpl.custom_data = processed_custom_data tpl.container = 'cloudforms-0' tpl.template_type = Template.OPENSHIFT_POD tpl.save(update_fields=[ 'container', 'template_type', 'custom_data' ]) original_template = tpl self.logger.info("Created a new template #{}".format( tpl.id)) # If the provider is set to not preconfigure templates, do not bother even doing it. if provider.num_simultaneous_configuring > 0: # Preconfigured one try: # openshift providers don't have preconfigured templates. # so regular template should be used if provider.provider_type != 'openshift': preconfigured_template = Template.objects.get( provider=provider, template_group=group, original_name=template_name, preconfigured=True) else: preconfigured_template = Template.objects.get( provider=provider, template_group=group, name=template_name, preconfigured=True) preconfigured_template.custom_data = processed_custom_data preconfigured_template.container = 'cloudforms-0' preconfigured_template.template_type = Template.OPENSHIFT_POD preconfigured_template.save(update_fields=[ 'container', 'template_type', 'custom_data' ]) if preconfigured_template.ga_released != ga_released: preconfigured_template.ga_released = ga_released preconfigured_template.save(update_fields=['ga_released']) except ObjectDoesNotExist: if template_name in provider.templates and provider.provider_type != 'openshift': original_id = original_template.id if original_template is not None else None create_appliance_template.delay( provider.id, group.id, template_name, source_template_id=original_id) # If any of the templates becomes unusable, let sprout know about it # Similarly if some of them becomes usable ... for provider_id, template_name, usability in template_usability: if conf.cfme_data.management_systems.get(provider_id, {}).get( "use_for_sprout", False ): # only create provider in db if it is marked to use for sprout provider, create = Provider.objects.get_or_create(id=provider_id) else: continue if not provider.working or provider.disabled: continue with transaction.atomic(): for template in Template.objects.filter( provider=provider, original_name=template_name): template.usable = usability template.save(update_fields=['usable']) # Kill all shepherd appliances if they were accidentally spun up if not usability: for appliance in Appliance.objects.filter( template=template, marked_for_deletion=False, appliance_pool=None): self.logger.info('Killing an appliance {}/{} ' 'because its template was marked ' 'as unusable'.format( appliance.id, appliance.name)) Appliance.kill(appliance)
def main(trackerbot_url, mark_usable=None, selected_provider=None, **kwargs): tb_api = trackerbot.api(trackerbot_url) all_providers = set( selected_provider or [key for key, data in cfme_data.management_systems.items() if 'disabled' not in data.get('tags', [])]) bad_providers = manager.Queue() # starmap the list of provider_keys into templates_on_provider # return is list of ProvTemplate tuples with ThreadPool(8) as pool: mgmt_templates = pool.starmap( templates_on_provider, ((provider_key, bad_providers) for provider_key in all_providers) ) # filter out the misbehaving providers bad_provider_keys = [] while not bad_providers.empty(): bad_provider_keys.append(bad_providers.get()) logger.warning('Filtering out providers that failed template query: %s', bad_provider_keys) working_providers = set([key for key in all_providers if key not in bad_provider_keys]) # Flip mgmt_templates into dict keyed on template name, listing providers # [ # {prov1: [t1, t2]}, # {prov2: [t1, t3]}, # ] # # mgmt_providertemplates should look like: # { # t1: [prov1, prov2], # t2: [prov1], # t3: [prov2] # } mgmt_providertemplates = defaultdict(list) # filter out any empty results from pulling mgmt_templates for prov_templates in [mt for mt in mgmt_templates if mt is not None]: # expecting one key (provider), one value (list of templates) for prov_key, templates in prov_templates.items(): for template in templates: mgmt_providertemplates[template].append(prov_key) logger.debug('DEBUG: template_providers: %r', mgmt_providertemplates) logger.debug('DEBUG: working_providers: %r', working_providers) usable = {'usable': mark_usable} if mark_usable is not None else {} # init these outside conditions/looping to be safe in reporting ignored_providertemplates = defaultdict(list) tb_pts_to_add = list() tb_pts_to_delete = list() tb_templates_to_delete = list() # ADD PROVIDERTEMPLATES # add all parseable providertemplates from what is actually on providers for template_name, provider_keys in mgmt_providertemplates.items(): # drop empty names, or sprout groups # go over templates pulled from provider mgmt interfaces, if template_name.strip() == '': logger.info('Ignoring empty name template on providers %s', provider_keys) template_info = TemplateName.parse_template(template_name) template_group = template_info.group_name # Don't want sprout templates, or templates that aren't parsable cfme/MIQ if template_group in GROUPS_TO_IGNORE: ignored_providertemplates[template_group].append(template_name) continue tb_pts_to_add = [ (template_group, provider_key, template_name, None, # custom_data usable) for provider_key in provider_keys ] logger.info('Threading add providertemplate records to trackerbot for %s', template_name) with ThreadPool(8) as pool: # thread for each template, passing the list of providers with the template add_results = pool.starmap( trackerbot.add_provider_template, tb_pts_to_add ) if not all([True if result in [None, True] else False for result in add_results]): # ignore results that are None, warn for any false results from adding logger.warning('Trackerbot providertemplate add failed, see logs') for group, names in ignored_providertemplates.items(): logger.info('Skipped group [%s] templates %r', group, names) # REMOVE PROVIDERTEMPLATES # Remove provider relationships where they no longer exist, skipping unresponsive providers, # and providers not known to this environment logger.info('Querying providertemplate records from Trackerbot for ones to delete') pts = trackerbot.depaginate( tb_api, tb_api.providertemplate.get(provider_in=working_providers) )['objects'] for pt in pts: key = pt['provider']['key'] pt_name, pt_group = pt['template']['name'], pt['template']['group']['name'] if pt_group in GROUPS_TO_IGNORE or key not in mgmt_providertemplates[pt_name]: logger.info("Marking trackerbot providertemplate for delete: %s::%s", key, pt_name) tb_pts_to_delete.append(ProvTemplate(key, pt_name)) with ThreadPool(8) as pool: # thread for each delete_provider_template call pool.starmap( trackerbot.delete_provider_template, ((tb_api, pt.provider_key, pt.template_name) for pt in tb_pts_to_delete)) # REMOVE TEMPLATES # Remove templates that aren't on any providers anymore for template in trackerbot.depaginate(tb_api, tb_api.template.get())['objects']: template_name = template['name'] if not template['providers'] and template_name.strip(): logger.info("Deleting trackerbot template %s (no providers)", template_name) tb_templates_to_delete.append(template_name) tb_api.template(template_name).delete() # WRITE REPORT with open(kwargs.get('outfile'), 'a') as report: add_header = '##### ProviderTemplate records added: #####\n' del_header = '##### ProviderTemplate records deleted: #####\n' report.write(add_header) add_message = tabulate( sorted([(ptadd[0], ptadd[1], ptadd[2]) for ptadd in tb_pts_to_add], key=lambda ptadd: ptadd[0]), headers=['Group', 'Provider', 'Template'], tablefmt='orgtbl' ) report.write('{}\n\n'.format(add_message)) report.write(del_header) del_message = tabulate( sorted([(ptdel.provider_key, ptdel.template_name) for ptdel in tb_pts_to_delete], key=lambda ptdel: ptdel[0]), headers=['Provider', 'Template'], tablefmt='orgtbl' ) report.write(del_message) logger.info('%s %s', add_header, add_message) logger.info('%s %s', del_header, del_message) return 0