198 lines
7.0 KiB
Python
Executable File
198 lines
7.0 KiB
Python
Executable File
#!/usr/bin/python
|
|
"""
|
|
Author: amos.steven.davis@hp.com
|
|
Description: Fix nova quota in the nova database when the actual usage
|
|
and what nova thinks is the quota do not match.
|
|
"""
|
|
from nova import db
|
|
from nova import config
|
|
from nova import context
|
|
from nova import exception
|
|
from collections import OrderedDict
|
|
import argparse
|
|
import prettytable
|
|
|
|
def make_table(name, *args):
|
|
q = prettytable.PrettyTable(name)
|
|
q.align = "c"
|
|
q.add_row(args[0])
|
|
return q
|
|
|
|
|
|
def get_actual_usage(cntxt, tenant):
|
|
filter_object = {'deleted': '',
|
|
'project_id': tenant}
|
|
instances = db.instance_get_all_by_filters(cntxt, filter_object)
|
|
|
|
# calculate actual usage
|
|
actual_instance_count = len(instances)
|
|
actual_core_count = 0
|
|
actual_ram_count = 0
|
|
|
|
for instance in instances:
|
|
actual_core_count += instance['vcpus']
|
|
actual_ram_count += instance['memory_mb']
|
|
|
|
actual_secgroup_count = len(db.security_group_get_by_project(cntxt, tenant))
|
|
if actual_secgroup_count == 0:
|
|
actual_secgroup_count = 1 # Every tenant uses quota for default security group
|
|
|
|
return OrderedDict((
|
|
("actual_instance_count", actual_instance_count),
|
|
("actual_core_count", actual_core_count),
|
|
("actual_ram_count", actual_ram_count),
|
|
("actual_secgroup_count", actual_secgroup_count)
|
|
))
|
|
|
|
|
|
def get_incorrect_usage(cntxt, tenant):
|
|
existing_usage = db.quota_usage_get_all_by_project(cntxt, tenant)
|
|
# {u'ram': {'reserved': 0L, 'in_use': 0L},
|
|
# u'floating_ips': {'reserved': 0L, 'in_use': 1L},
|
|
# u'instances': {'reserved': 0L, 'in_use': 0L},
|
|
# u'cores': {'reserved': 0L, 'in_use': 0L},
|
|
# 'project_id': tenant,
|
|
# u'security_groups': {'reserved': 0L, 'in_use': 1L}}
|
|
#
|
|
# Get (instance_count, total_cores, total_ram) for project.
|
|
# If instances does not exist, then this
|
|
|
|
try:
|
|
security_groups = existing_usage["security_groups"]["in_use"]
|
|
except KeyError:
|
|
security_groups = 1
|
|
|
|
try:
|
|
instances = existing_usage["instances"]["in_use"]
|
|
except KeyError:
|
|
instances = 0
|
|
|
|
try:
|
|
cores = existing_usage["cores"]["in_use"]
|
|
except KeyError:
|
|
cores = 0
|
|
|
|
try:
|
|
ram = existing_usage["ram"]["in_use"]
|
|
except KeyError:
|
|
ram = 0
|
|
|
|
return OrderedDict((
|
|
("db_instance_count", instances),
|
|
("db_core_count", cores),
|
|
("db_ram_count", ram),
|
|
("db_secgroup_count", security_groups)
|
|
))
|
|
|
|
|
|
def fix_usage(cntxt, tenant):
|
|
|
|
# Get per-user data for this tenant since usage is now per-user
|
|
filter_object = {'project_id': tenant}
|
|
instance_info = db.instance_get_all_by_filters(cntxt, filter_object)
|
|
|
|
usage_by_resource = {}
|
|
#resource_types = ['instances', 'cores', 'ram', 'security_groups']
|
|
states_to_ignore = ['error', 'deleted', 'building']
|
|
|
|
for instance in instance_info:
|
|
user = instance['user_id']
|
|
# We need to build a list of users who have launched vm's even if the user
|
|
# no longer exists. We can't use keystone here.
|
|
if not usage_by_resource.has_key(user):
|
|
usage_by_resource[user] = {} # Record that this user has once used resources
|
|
if not instance['vm_state'] in states_to_ignore:
|
|
user_resource = usage_by_resource[user]
|
|
user_resource['instances'] = user_resource.get('instances', 0) + 1
|
|
user_resource['cores'] = user_resource.get('cores', 0) + instance['vcpus']
|
|
user_resource['ram'] = user_resource.get('ram', 0) + instance['memory_mb']
|
|
|
|
secgroup_list = db.security_group_get_by_project(cntxt, tenant)
|
|
for group in secgroup_list:
|
|
user = group.user_id
|
|
if not usage_by_resource.has_key(user):
|
|
usage_by_resource[user] = {} # Record that this user has once used resources
|
|
user_resource = usage_by_resource[user]
|
|
user_resource['security_groups'] = user_resource.get('security_groups', 0) + 1
|
|
|
|
# Correct the quota usage in the database
|
|
for user in usage_by_resource:
|
|
for resource in resource_types:
|
|
usage = usage_by_resource[user].get(resource, 0)
|
|
try:
|
|
db.quota_usage_update(cntxt, tenant, user, resource, in_use=usage)
|
|
except exception.QuotaUsageNotFound as e:
|
|
print e
|
|
print 'db.quota_usage_update(cntxt, %s, %s, %s, in_use=%s)' % \
|
|
(tenant, user, resource, usage)
|
|
|
|
def print_usage(cntxt, tenant):
|
|
actual_table_name = ["Actual Instances",
|
|
"Actual Cores",
|
|
"Actual RAM",
|
|
"Actual Security_Groups"]
|
|
|
|
# these are spaced so that the Quota & DB tables match in size
|
|
incorrect_table_name = [" DB Instances ",
|
|
" DB Cores ",
|
|
" DB RAM ",
|
|
" DB Security_Groups "]
|
|
|
|
print "############### Actual Usage (including non-active instances) ###############"
|
|
print make_table(actual_table_name, get_actual_usage(cntxt, tenant).values())
|
|
print "############### Database Usage ###############"
|
|
print make_table(incorrect_table_name, get_incorrect_usage(cntxt, tenant).values())
|
|
|
|
resource_types = ['instances', 'cores', 'ram', 'security_groups']
|
|
config.parse_args(['filename', '--config-file', '/etc/nova/nova.conf'])
|
|
|
|
# Get other arguments
|
|
parser = argparse.ArgumentParser(
|
|
description='Fix quota differences between reality and the database')
|
|
parser.add_argument('--tenant', help='Specify tenant', required=True)
|
|
parser.add_argument('-n', '--dryrun', help='Dry Run - don\'t update the database',
|
|
action="store_true")
|
|
parser.add_argument('-q', '--quiet', help='Quiet mode. Only show incorrect quotas',
|
|
action="store_true")
|
|
args = parser.parse_args()
|
|
tenant = args.tenant
|
|
|
|
# Get admin context
|
|
cxt = context.get_admin_context()
|
|
|
|
# if the actual usage & the quota tracking differ,
|
|
# update quota to match reality
|
|
try:
|
|
actual = get_actual_usage(cxt, tenant).values()
|
|
incorrect = get_incorrect_usage(cxt, tenant).values()
|
|
except:
|
|
exit(2)
|
|
|
|
if actual == incorrect:
|
|
if not args.quiet:
|
|
print_usage(cxt, tenant)
|
|
print "%s quota is OK" % tenant
|
|
exit(0)
|
|
else:
|
|
print "%s usage and database differ" % tenant
|
|
print_usage(cxt, tenant)
|
|
if args.dryrun:
|
|
print "Dry Run Mode Enabled - not correcting the quota database."
|
|
exit(1)
|
|
else:
|
|
print "Updating quota usage to reflect actual usage..\n"
|
|
fix_usage(cxt, tenant)
|
|
print_usage(cxt, tenant)
|
|
|
|
# This section can replace the final if/else statement to allow prompting for
|
|
# each tenant before changes happen
|
|
# if get_incorrect_usage(cxt,tenant).values() == get_actual_usage(cxt,tenant).values():
|
|
# print "%s quota is OK" % tenant
|
|
# else:
|
|
# if raw_input("Enter 'YES' to make the Database Usage match the Actual Usage. " \
|
|
# "This will modify the Nova database: ") != "YES":
|
|
# print "Exiting."
|
|
# exit(0)
|
|
# else:
|
|
# fix_usage(cxt,tenant,actual_table_name,incorrect_table_name)
|