nova/nova/quota.py

253 lines
10 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Quotas for instances, volumes, and floating ips."""
from nova import db
from nova.openstack.common import cfg
from nova import flags
quota_opts = [
cfg.IntOpt('quota_instances',
default=10,
help='number of instances allowed per project'),
cfg.IntOpt('quota_cores',
default=20,
help='number of instance cores allowed per project'),
cfg.IntOpt('quota_ram',
default=50 * 1024,
help='megabytes of instance ram allowed per project'),
cfg.IntOpt('quota_volumes',
default=10,
help='number of volumes allowed per project'),
cfg.IntOpt('quota_gigabytes',
default=1000,
help='number of volume gigabytes allowed per project'),
cfg.IntOpt('quota_floating_ips',
default=10,
help='number of floating ips allowed per project'),
cfg.IntOpt('quota_metadata_items',
default=128,
help='number of metadata items allowed per instance'),
cfg.IntOpt('quota_injected_files',
default=5,
help='number of injected files allowed'),
cfg.IntOpt('quota_injected_file_content_bytes',
default=10 * 1024,
help='number of bytes allowed per injected file'),
cfg.IntOpt('quota_injected_file_path_bytes',
default=255,
help='number of bytes allowed per injected file path'),
cfg.IntOpt('quota_security_groups',
default=10,
help='number of security groups per project'),
cfg.IntOpt('quota_security_group_rules',
default=20,
help='number of security rules per security group'),
cfg.IntOpt('quota_key_pairs',
default=100,
help='number of key pairs per user'),
]
FLAGS = flags.FLAGS
FLAGS.register_opts(quota_opts)
quota_resources = ['metadata_items', 'injected_file_content_bytes',
'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances',
'injected_files', 'cores', 'security_groups', 'security_group_rules',
'key_pairs']
def _get_default_quotas():
defaults = {
'instances': FLAGS.quota_instances,
'cores': FLAGS.quota_cores,
'ram': FLAGS.quota_ram,
'volumes': FLAGS.quota_volumes,
'gigabytes': FLAGS.quota_gigabytes,
'floating_ips': FLAGS.quota_floating_ips,
'metadata_items': FLAGS.quota_metadata_items,
'injected_files': FLAGS.quota_injected_files,
'injected_file_content_bytes':
FLAGS.quota_injected_file_content_bytes,
'security_groups': FLAGS.quota_security_groups,
'security_group_rules': FLAGS.quota_security_group_rules,
'key_pairs': FLAGS.quota_key_pairs,
}
# -1 in the quota flags means unlimited
return defaults
def get_class_quotas(context, quota_class, defaults=None):
"""Update defaults with the quota class values."""
if not defaults:
defaults = _get_default_quotas()
quota = db.quota_class_get_all_by_name(context, quota_class)
for key in defaults.keys():
if key in quota:
defaults[key] = quota[key]
return defaults
def get_project_quotas(context, project_id):
defaults = _get_default_quotas()
if context.quota_class:
get_class_quotas(context, context.quota_class, defaults)
quota = db.quota_get_all_by_project(context, project_id)
for key in defaults.keys():
if key in quota:
defaults[key] = quota[key]
return defaults
def _get_request_allotment(requested, used, quota):
if quota == -1:
return requested
return quota - used
def allowed_instances(context, requested_instances, instance_type):
"""Check quota and return min(requested_instances, allowed_instances)."""
project_id = context.project_id
context = context.elevated()
requested_cores = requested_instances * instance_type['vcpus']
requested_ram = requested_instances * instance_type['memory_mb']
usage = db.instance_data_get_for_project(context, project_id)
used_instances, used_cores, used_ram = usage
quota = get_project_quotas(context, project_id)
allowed_instances = _get_request_allotment(requested_instances,
used_instances,
quota['instances'])
allowed_cores = _get_request_allotment(requested_cores, used_cores,
quota['cores'])
allowed_ram = _get_request_allotment(requested_ram, used_ram, quota['ram'])
if instance_type['vcpus']:
allowed_instances = min(allowed_instances,
allowed_cores // instance_type['vcpus'])
if instance_type['memory_mb']:
allowed_instances = min(allowed_instances,
allowed_ram // instance_type['memory_mb'])
return min(requested_instances, allowed_instances)
def allowed_volumes(context, requested_volumes, size):
"""Check quota and return min(requested_volumes, allowed_volumes)."""
project_id = context.project_id
context = context.elevated()
size = int(size)
requested_gigabytes = requested_volumes * size
used_volumes, used_gigabytes = db.volume_data_get_for_project(context,
project_id)
quota = get_project_quotas(context, project_id)
allowed_volumes = _get_request_allotment(requested_volumes, used_volumes,
quota['volumes'])
allowed_gigabytes = _get_request_allotment(requested_gigabytes,
used_gigabytes,
quota['gigabytes'])
if size != 0:
allowed_volumes = min(allowed_volumes,
int(allowed_gigabytes // size))
return min(requested_volumes, allowed_volumes)
def allowed_floating_ips(context, requested_floating_ips):
"""Check quota and return min(requested, allowed) floating ips."""
project_id = context.project_id
context = context.elevated()
used_floating_ips = db.floating_ip_count_by_project(context, project_id)
quota = get_project_quotas(context, project_id)
allowed_floating_ips = _get_request_allotment(requested_floating_ips,
used_floating_ips,
quota['floating_ips'])
return min(requested_floating_ips, allowed_floating_ips)
def allowed_security_groups(context, requested_security_groups):
"""Check quota and return min(requested, allowed) security groups."""
project_id = context.project_id
context = context.elevated()
used_sec_groups = db.security_group_count_by_project(context, project_id)
quota = get_project_quotas(context, project_id)
allowed_sec_groups = _get_request_allotment(requested_security_groups,
used_sec_groups,
quota['security_groups'])
return min(requested_security_groups, allowed_sec_groups)
def allowed_security_group_rules(context, security_group_id,
requested_rules):
"""Check quota and return min(requested, allowed) sec group rules."""
project_id = context.project_id
context = context.elevated()
used_rules = db.security_group_rule_count_by_group(context,
security_group_id)
quota = get_project_quotas(context, project_id)
allowed_rules = _get_request_allotment(requested_rules,
used_rules,
quota['security_group_rules'])
return min(requested_rules, allowed_rules)
def allowed_key_pairs(context, requested_key_pairs):
"""Check quota and return min(requested, allowed) key pairs."""
user_id = context.user_id
project_id = context.project_id
context = context.elevated()
used_key_pairs = db.key_pair_count_by_user(context, user_id)
quota = get_project_quotas(context, project_id)
allowed_key_pairs = _get_request_allotment(requested_key_pairs,
used_key_pairs,
quota['key_pairs'])
return min(requested_key_pairs, allowed_key_pairs)
def _calculate_simple_quota(context, resource, requested):
"""Check quota for resource; return min(requested, allowed)."""
quota = get_project_quotas(context, context.project_id)
allowed = _get_request_allotment(requested, 0, quota[resource])
return min(requested, allowed)
def allowed_metadata_items(context, requested_metadata_items):
"""Return the number of metadata items allowed."""
return _calculate_simple_quota(context, 'metadata_items',
requested_metadata_items)
def allowed_injected_files(context, requested_injected_files):
"""Return the number of injected files allowed."""
return _calculate_simple_quota(context, 'injected_files',
requested_injected_files)
def allowed_injected_file_content_bytes(context, requested_bytes):
"""Return the number of bytes allowed per injected file content."""
resource = 'injected_file_content_bytes'
return _calculate_simple_quota(context, resource, requested_bytes)
def allowed_injected_file_path_bytes(context):
"""Return the number of bytes allowed in an injected file path."""
return FLAGS.quota_injected_file_path_bytes