nova/nova/compute/flavors.py
Stephen Finucane 100b9dc62c db: Unify 'nova.db.api', 'nova.db.sqlalchemy.api'
Merge these, removing an unnecessary layer of abstraction, and place
them in the new 'nova.db.main' directory. The resulting change is huge,
but it's mainly the result of 's/sqlalchemy import api/main import api/'
and 's/nova.db.api/nova.db.main.api/' with some necessary cleanup. We
also need to rework how we do the blocking of API calls since we no
longer have a 'DBAPI' object that we can monkey patch as we were doing
before. This is now done via a global variable that is set by the 'main'
function of 'nova.cmd.compute'.

The main impact of this change is that it's no longer possible to set
'[database] use_db_reconnect' and have all APIs automatically wrapped in
a DB retry. Seeing as this behavior is experimental, isn't applied to
any of the API DB methods (which don't use oslo.db's 'DBAPI' helper),
and is used explicitly in what would appear to be the critical cases
(via the explicit 'oslo_db.api.wrap_db_retry' decorator), this doesn't
seem like a huge loss.

Change-Id: Iad2e4da4546b80a016e477577d23accb2606a6e4
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
2021-08-09 15:34:40 +01:00

214 lines
6.9 KiB
Python

# 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
from oslo_utils import strutils
from oslo_utils import uuidutils
import nova.conf
from nova import context
from nova.db import constants as db_const
from nova import exception
from nova.i18n import _
from nova import objects
from nova import utils
CONF = nova.conf.CONF
# Validate extra specs key names.
VALID_EXTRASPEC_NAME_REGEX = re.compile(r"[\w\.\- :]+$", re.UNICODE)
def _int_or_none(val):
if val is not None:
return int(val)
system_metadata_flavor_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,
}
system_metadata_flavor_extra_props = [
'hw:numa_cpus.', 'hw:numa_mem.',
]
def create(name, memory, vcpus, root_gb, ephemeral_gb=0, flavorid=None,
swap=0, rxtx_factor=1.0, is_public=True, description=None):
"""Creates flavors."""
if not flavorid:
flavorid = uuidutils.generate_uuid()
kwargs = {
'memory_mb': memory,
'vcpus': vcpus,
'root_gb': root_gb,
'ephemeral_gb': ephemeral_gb,
'swap': swap,
'rxtx_factor': rxtx_factor,
'description': description
}
if isinstance(name, str):
name = name.strip()
# NOTE(vish): Internally, flavorid is stored as a string but it comes
# in through json as an integer, so we convert it here.
flavorid = str(flavorid)
# NOTE(wangbo): validate attributes of the creating flavor.
# ram and vcpus should be positive ( > 0) integers.
# disk, ephemeral and swap should be non-negative ( >= 0) integers.
flavor_attributes = {
'memory_mb': ('ram', 1),
'vcpus': ('vcpus', 1),
'root_gb': ('disk', 0),
'ephemeral_gb': ('ephemeral', 0),
'swap': ('swap', 0)
}
for key, value in flavor_attributes.items():
kwargs[key] = utils.validate_integer(
kwargs[key], value[0], value[1], db_const.MAX_INT,
)
# rxtx_factor should be a positive float
try:
kwargs['rxtx_factor'] = float(kwargs['rxtx_factor'])
if (
kwargs['rxtx_factor'] <= 0 or
kwargs['rxtx_factor'] > db_const.SQL_SP_FLOAT_MAX
):
raise ValueError()
except ValueError:
msg = _("'rxtx_factor' argument must be a float between 0 and %g")
raise exception.InvalidInput(reason=msg % db_const.SQL_SP_FLOAT_MAX)
kwargs['name'] = name
kwargs['flavorid'] = flavorid
# ensure is_public attribute is boolean
try:
kwargs['is_public'] = strutils.bool_from_string(
is_public, strict=True)
except ValueError:
raise exception.InvalidInput(reason=_("is_public must be a boolean"))
flavor = objects.Flavor(context=context.get_admin_context(), **kwargs)
flavor.create()
return flavor
# TODO(termie): flavor-specific code should probably be in the API that uses
# flavors.
def get_flavor_by_flavor_id(flavorid, ctxt=None, read_deleted="yes"):
"""Retrieve flavor by flavorid.
:raises: FlavorNotFound
"""
if ctxt is None:
ctxt = context.get_admin_context(read_deleted=read_deleted)
return objects.Flavor.get_by_flavor_id(ctxt, flavorid, read_deleted)
# NOTE(danms): This method is deprecated, do not use it!
# Use instance.{old_,new_,}flavor instead, as instances no longer
# have flavor information in system_metadata.
def extract_flavor(instance, prefix=''):
"""Create a Flavor object from instance's system_metadata
information.
"""
flavor = objects.Flavor()
sys_meta = utils.instance_sys_meta(instance)
if not sys_meta:
return None
for key in system_metadata_flavor_props.keys():
type_key = '%sinstance_type_%s' % (prefix, key)
setattr(flavor, key, sys_meta[type_key])
# NOTE(danms): We do NOT save all of extra_specs, but only the
# NUMA-related ones that we need to avoid an uglier alternative. This
# should be replaced by a general split-out of flavor information from
# system_metadata very soon.
extra_specs = [(k, v) for k, v in sys_meta.items()
if k.startswith('%sinstance_type_extra_' % prefix)]
if extra_specs:
flavor.extra_specs = {}
for key, value in extra_specs:
extra_key = key[len('%sinstance_type_extra_' % prefix):]
flavor.extra_specs[extra_key] = value
return flavor
# NOTE(danms): This method is deprecated, do not use it!
# Use instance.{old_,new_,}flavor instead, as instances no longer
# have flavor information in system_metadata.
# NOTE(stephenfin): 'prefix' is unused and could be removed
def save_flavor_info(metadata, flavor, prefix=''):
"""Save properties from flavor 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 flavor for later use (such as
during resize).
"""
for key in system_metadata_flavor_props.keys():
to_key = '%sinstance_type_%s' % (prefix, key)
metadata[to_key] = flavor[key]
# NOTE(danms): We do NOT save all of extra_specs here, but only the
# NUMA-related ones that we need to avoid an uglier alternative. This
# should be replaced by a general split-out of flavor information from
# system_metadata very soon.
extra_specs = flavor.get('extra_specs', {})
for extra_prefix in system_metadata_flavor_extra_props:
for key in extra_specs:
if key.startswith(extra_prefix):
to_key = '%sinstance_type_extra_%s' % (prefix, key)
metadata[to_key] = extra_specs[key]
return metadata
def validate_extra_spec_keys(key_names_list):
for key_name in key_names_list:
if not VALID_EXTRASPEC_NAME_REGEX.match(key_name):
expl = _('Key Names can only contain alphanumeric characters, '
'periods, dashes, underscores, colons and spaces.')
raise exception.InvalidInput(message=expl)