nova/nova/compute/instance_types.py

276 lines
8.5 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.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Ken Pepple
#
# 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.
"""Built-in instance properties."""
import re
import uuid
from oslo.config import cfg
from nova import context
from nova import db
from nova import exception
from nova.openstack.common.db.sqlalchemy import session as db_session
from nova.openstack.common import log as logging
from nova import utils
instance_type_opts = [
cfg.StrOpt('default_instance_type',
default='m1.small',
help='default instance type to use, testing only'),
]
CONF = cfg.CONF
CONF.register_opts(instance_type_opts)
LOG = logging.getLogger(__name__)
INVALID_NAME_REGEX = re.compile("[^\w\.\- ]")
def _int_or_none(val):
if val is not None:
return int(val)
system_metadata_instance_type_props = {
'id': int,
'name': str,
'memory_mb': int,
'vcpus': int,
'root_gb': int,
'ephemeral_gb': int,
'flavorid': str,
'swap': int,
'rxtx_factor': float,
'vcpu_weight': _int_or_none,
}
def create(name, memory, vcpus, root_gb, ephemeral_gb=None, flavorid=None,
swap=None, rxtx_factor=None, is_public=True):
"""Creates instance types."""
if flavorid is None:
flavorid = uuid.uuid4()
if swap is None:
swap = 0
if rxtx_factor is None:
rxtx_factor = 1.0
if ephemeral_gb is None:
ephemeral_gb = 0
kwargs = {
'memory_mb': memory,
'vcpus': vcpus,
'root_gb': root_gb,
'ephemeral_gb': ephemeral_gb,
'swap': swap,
'rxtx_factor': rxtx_factor,
}
# ensure name do not exceed 255 characters
utils.check_string_length(name, 'name', min_length=1, max_length=255)
# ensure name does not contain any special characters
invalid_name = INVALID_NAME_REGEX.search(name)
if invalid_name:
msg = _("names can only contain [a-zA-Z0-9_.- ]")
raise exception.InvalidInput(reason=msg)
# ensure some attributes are integers and greater than or equal to 0
for option in ['memory_mb', 'vcpus', 'root_gb', 'ephemeral_gb', 'swap']:
try:
kwargs[option] = int(kwargs[option])
assert kwargs[option] >= 0
except (ValueError, AssertionError):
msg = _("'%s' argument must be a positive integer") % option
raise exception.InvalidInput(reason=msg)
# rxtx_factor should be a positive float
try:
kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
assert kwargs['rxtx_factor'] > 0
except (ValueError, AssertionError):
msg = _("'rxtx_factor' argument must be a positive float")
raise exception.InvalidInput(reason=msg)
# some value are required to be nonzero, not just positive
for option in ['memory_mb', 'vcpus']:
try:
assert kwargs[option] > 0
except AssertionError:
msg = _("'%s' argument must be greater than 0") % option
raise exception.InvalidInput(reason=msg)
kwargs['name'] = name
# NOTE(vish): Internally, flavorid is stored as a string but it comes
# in through json as an integer, so we convert it here.
kwargs['flavorid'] = unicode(flavorid)
# ensure is_public attribute is boolean
if not utils.is_valid_boolstr(is_public):
msg = _("is_public must be a boolean")
raise exception.InvalidInput(reason=msg)
kwargs['is_public'] = utils.bool_from_str(is_public)
try:
return db.instance_type_create(context.get_admin_context(), kwargs)
except db_session.DBError, e:
LOG.exception(_('DB error: %s') % e)
raise exception.InstanceTypeCreateFailed()
def destroy(name):
"""Marks instance types as deleted."""
try:
assert name is not None
db.instance_type_destroy(context.get_admin_context(), name)
except (AssertionError, exception.NotFound):
LOG.exception(_('Instance type %s not found for deletion') % name)
raise exception.InstanceTypeNotFoundByName(instance_type_name=name)
def get_all_types(ctxt=None, inactive=False, filters=None):
"""Get all non-deleted instance_types.
Pass true as argument if you want deleted instance types returned also.
"""
if ctxt is None:
ctxt = context.get_admin_context()
inst_types = db.instance_type_get_all(
ctxt, inactive=inactive, filters=filters)
inst_type_dict = {}
for inst_type in inst_types:
inst_type_dict[inst_type['name']] = inst_type
return inst_type_dict
get_all_flavors = get_all_types
def get_default_instance_type():
"""Get the default instance type."""
name = CONF.default_instance_type
return get_instance_type_by_name(name)
def get_instance_type(instance_type_id, ctxt=None, inactive=False):
"""Retrieves single instance type by id."""
if instance_type_id is None:
return get_default_instance_type()
if ctxt is None:
ctxt = context.get_admin_context()
if inactive:
ctxt = ctxt.elevated(read_deleted="yes")
return db.instance_type_get(ctxt, instance_type_id)
def get_instance_type_by_name(name, ctxt=None):
"""Retrieves single instance type by name."""
if name is None:
return get_default_instance_type()
if ctxt is None:
ctxt = context.get_admin_context()
return db.instance_type_get_by_name(ctxt, name)
# TODO(termie): flavor-specific code should probably be in the API that uses
# flavors.
def get_instance_type_by_flavor_id(flavorid, ctxt=None, read_deleted="yes"):
"""Retrieve instance type by flavorid.
:raises: FlavorNotFound
"""
if ctxt is None:
ctxt = context.get_admin_context(read_deleted=read_deleted)
return db.instance_type_get_by_flavor_id(ctxt, flavorid)
def get_instance_type_access_by_flavor_id(flavorid, ctxt=None):
"""Retrieve instance type access list by flavor id."""
if ctxt is None:
ctxt = context.get_admin_context()
return db.instance_type_access_get_by_flavor_id(ctxt, flavorid)
def add_instance_type_access(flavorid, projectid, ctxt=None):
"""Add instance type access for project."""
if ctxt is None:
ctxt = context.get_admin_context()
return db.instance_type_access_add(ctxt, flavorid, projectid)
def remove_instance_type_access(flavorid, projectid, ctxt=None):
"""Remove instance type access for project."""
if ctxt is None:
ctxt = context.get_admin_context()
return db.instance_type_access_remove(ctxt, flavorid, projectid)
def extract_instance_type(instance, prefix=''):
"""Create an InstanceType-like object from instance's system_metadata
information."""
instance_type = {}
sys_meta = utils.metadata_to_dict(instance['system_metadata'])
for key, type_fn in system_metadata_instance_type_props.items():
type_key = '%sinstance_type_%s' % (prefix, key)
instance_type[key] = type_fn(sys_meta[type_key])
return instance_type
def save_instance_type_info(metadata, instance_type, prefix=''):
"""Save properties from instance_type into instance's system_metadata,
in the format of:
[prefix]instance_type_[key]
This can be used to update system_metadata in place from a type, as well
as stash information about another instance_type for later use (such as
during resize)."""
for key in system_metadata_instance_type_props.keys():
to_key = '%sinstance_type_%s' % (prefix, key)
metadata[to_key] = instance_type[key]
return metadata
def delete_instance_type_info(metadata, *prefixes):
"""Delete instance_type information from instance's system_metadata
by prefix."""
for key in system_metadata_instance_type_props.keys():
for prefix in prefixes:
to_key = '%sinstance_type_%s' % (prefix, key)
del metadata[to_key]
return metadata