def allocation_managers(csv=False, filename=None, sslwarnings=False): """Get the allocation manager emails for all projects. """ ssl_warnings(enabled=sslwarnings) keystone = hm_keystone.client_session(version=3) all_users = map(lambda x: x.to_dict(), keystone.users.list()) email_dict = {x['id']: x['email'] for x in all_users if 'email' in x and x['email'] is not None} projects = keystone.projects.list() managers = collections.defaultdict(list) for user_role in keystone.role_assignments.list(role=14): if 'project' in user_role.scope: managers[user_role.scope['project']['id']].append( user_role.user['id']) headings = ["Tenant ID", "Manager email(s)"] records = [] for proj in projects: if len(managers[proj.id]) == 0: continue emails = set() for tm in managers[proj.id]: if tm in email_dict: emails.add(email_dict[tm]) records.append([proj.id, ",".join(emails)]) if csv: csv_output(headings, records, filename=filename) else: pretty_output(headings, records, filename=filename)
def allocation_homes(csv=False, filename=None, sslwarnings=False): """Get the allocation_homes for all projects. If this metadata field is not set in keystone (see keystone hivemind commands), the value reported is the email domains for all tenant managers belonging to this project. """ ssl_warnings(enabled=sslwarnings) keystone = hm_keystone.client_session(version=3) all_users = map(lambda x: x.to_dict(), keystone.users.list()) email_dict = {x['id']: x['email'].split("@")[-1] for x in all_users if 'email' in x and x['email'] is not None} projects = keystone.projects.list() managers = collections.defaultdict(list) for user_role in keystone.role_assignments.list(role=14): if 'project' in user_role.scope: managers[user_role.scope['project']['id']].append( user_role.user['id']) headings = ["Tenant ID", "Allocation Home(s)"] records = [] for proj in projects: if "allocation_home" in proj.to_dict(): records.append([proj.id, proj.allocation_home]) else: if len(managers[proj.id]) == 0: continue institutions = set() for tm in managers[proj.id]: if tm in email_dict: institutions.add(email_dict[tm]) records.append([proj.id, ",".join(institutions)]) if csv: csv_output(headings, records, filename=filename) else: pretty_output(headings, records, filename=filename)
def get_project_usage_csv(start_date=None, end_date=None, filename=None, sslwarnings=False): """Get accumulated instance usage statistics for all projects. Date strings should be ISO 8601 to minute precision without timezone information. """ ssl_warnings(enabled=sslwarnings) assert start_date and end_date start = datetime.datetime.strptime(start_date, "%Y-%m-%dT%H:%M") end = datetime.datetime.strptime(end_date, "%Y-%m-%dT%H:%M") keystone = hm_keystone.client_session(version=3) nova = hm_nova.client() tenants = {x.id: x for x in keystone.projects.list()} headings = ["Tenant ID", "Tenant Name", "Instance count", "Instance hours", "vCPU hours", "Memory Hours (MB)", "Disk hours (GB)"] usage = map(lambda u: [ u.tenant_id, tenants[u.tenant_id].name if u.tenant_id in tenants else None, len(u.server_usages), u.total_hours, u.total_vcpus_usage, u.total_memory_mb_usage, u.total_local_gb_usage], nova.usage.list(start, end, detailed=True)) csv_output(headings, usage, filename=filename)
def get_instance_usage_csv(start_date=None, end_date=None, filename=None, sslwarnings=False): """Get individual instance usage for all projects, including tenant and availability zones. Date strings should be ISO 8601 to minute precision without timezone information. """ ssl_warnings(enabled=sslwarnings) assert start_date and end_date start = datetime.datetime.strptime(start_date, "%Y-%m-%dT%H:%M") end = datetime.datetime.strptime(end_date, "%Y-%m-%dT%H:%M") keystone = hm_keystone.client_session(version=3) nova = hm_nova.client() tenants = {x.id: x for x in keystone.projects.list()} usage = [] for u in nova.usage.list(start, end, detailed=True): tenant_id = u.tenant_id tenant_name = tenants[tenant_id].name if tenant_id in tenants else None # The Nova API doesn't allow "show" on deleted instances, but # we can get the info using "list --deleted". The problem is # figuring out how to avoid retrieving irrelevant instances, # and at the same time how to avoid too many requests. # # Attempt #1 - use the tenant_id and the instance's name to # focus queries. # Attempt #2 - as #1, but after N lookups by name for a tenant, # just fetch all of the deleted instances. cache = {} try: for iu in u.server_usages: name = iu['name'] instance_id = iu['instance_id'] instance = None if iu['state'] == 'terminated' or iu['state'] == 'deleted': instance = _get_deleted_instance(cache, nova, u.tenant_id, name, instance_id) else: try: instance = nova.servers.get(instance_id).to_dict() except: print 'Cannot find instance {0} in {1}' \ .format(instance_id, u.tenant_id) if instance is None: instance = {'OS-EXT-AZ:availability_zone': 'unknown'} usage.append([tenant_id, tenant_name, instance_id, name, iu['state'], iu['flavor'], iu['hours'], iu['vcpus'], iu['memory_mb'], iu['local_gb'], instance['OS-EXT-AZ:availability_zone']]) except: traceback.print_exc(file=sys.stdout) headings = ["Tenant ID", "Tenant Name", "Instance id", "Instance name", "Instance state", "Flavour", "Instance hours", "vCPUs", "Memory (MB)", "Disk (GB)", "AZ"] csv_output(headings, usage, filename=filename)
def compare_quotas(name_or_id=None): """Compare the allocation and quota information for a tenant """ if name_or_id == None: print 'A tenant name or id is required' return keystone_api = hm_keystone.client_session(version=3) try: tenant = hm_keystone.get_tenant(keystone_api, name_or_id) except: print 'Tenant {0} not found in keystone'.format(name_or_id) return nova_api = hm_nova.client() quotas = nova_api.quotas.get(tenant.id) print 'nova quotas: instances {0}, cores {1}, ram {2}'.format( quotas.instances, quotas.cores, quotas.ram / 1024) usage = _get_usage(nova_api, _get_flavor_map(nova_api), tenant.id) print 'nova usage: instances {0}, cores {1}, ram {2}'.format( usage['instances'], usage['vcpus'], usage['ram'] / 1024) allocations_api = NectarApiSession() allocations = allocations_api.get_allocations(); tenant_allocations = filter(lambda x: x['tenant_uuid'] == tenant.id and \ (x['status'] == 'A' or x['status'] == 'X'), allocations) if len(tenant_allocations) == 0: print 'No approved allocation records for tenant {0} / {1}'.format( tenant.id, tenant.name) return tenant_allocations.sort(key=lambda alloc: alloc['modified_time']) current_allocation = tenant_allocations[-1] format = '{0} mismatch: allocated {1}, nova {2}, used {3}' if current_allocation['instance_quota'] != quotas.instances: print format.format('Instance quota', current_allocation['instance_quota'], quotas.instances, usage['instances']) if current_allocation['core_quota'] != quotas.cores: print format.format('VCPU quota', current_allocation['core_quota'], quotas.cores, usage['vcpus']) if current_allocation['ram_quota'] * 1024 != quotas.ram: print format.format('RAM quota', current_allocation['ram_quota'] * 1024, quotas.ram, usage['ram'])