Add helpers to create per-aggregate filters
This patchset introduce a new file of utility methods to deal with aggregate metadata. As well it updates related filters to use this new logic. * AggregateRamFilter * AggregateCoreFilter, * AggregateTypeAffinityFilter Closes-Bug: #1301340 Change-Id: I5e4aebe9e61cfbdc7b9e6b5de0788fc6670eb936
This commit is contained in:
parent
76d6dee94d
commit
878f767b9e
|
@ -17,10 +17,10 @@
|
|||
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import db
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common.gettextutils import _LW
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.scheduler import filters
|
||||
from nova.scheduler.filters import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -48,7 +48,7 @@ class BaseCoreFilter(filters.BaseHostFilter):
|
|||
|
||||
if not host_state.vcpus_total:
|
||||
# Fail safe
|
||||
LOG.warning(_("VCPUs not set; assuming CPU collection broken"))
|
||||
LOG.warning(_LW("VCPUs not set; assuming CPU collection broken"))
|
||||
return True
|
||||
|
||||
instance_vcpus = instance_type['vcpus']
|
||||
|
@ -78,27 +78,18 @@ class AggregateCoreFilter(BaseCoreFilter):
|
|||
"""
|
||||
|
||||
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
|
||||
context = filter_properties['context']
|
||||
# TODO(uni): DB query in filter is a performance hit, especially for
|
||||
# system with lots of hosts. Will need a general solution here to fix
|
||||
# all filters with aggregate DB call things.
|
||||
metadata = db.aggregate_metadata_get_by_host(
|
||||
context, host_state.host, key='cpu_allocation_ratio')
|
||||
aggregate_vals = metadata.get('cpu_allocation_ratio', set())
|
||||
num_values = len(aggregate_vals)
|
||||
|
||||
if num_values == 0:
|
||||
return CONF.cpu_allocation_ratio
|
||||
|
||||
if num_values > 1:
|
||||
LOG.warning(_("%(num_values)d ratio values found, "
|
||||
"of which the minimum value will be used."),
|
||||
{'num_values': num_values})
|
||||
|
||||
aggregate_vals = utils.aggregate_values_from_db(
|
||||
filter_properties['context'],
|
||||
host_state.host,
|
||||
'cpu_allocation_ratio')
|
||||
try:
|
||||
ratio = float(min(aggregate_vals))
|
||||
ratio = utils.validate_num_values(
|
||||
aggregate_vals, CONF.cpu_allocation_ratio, cast_to=float)
|
||||
except ValueError as e:
|
||||
LOG.warning(_("Could not decode cpu_allocation_ratio: '%s'"), e)
|
||||
LOG.warning(_LW("Could not decode cpu_allocation_ratio: '%s'"), e)
|
||||
ratio = CONF.cpu_allocation_ratio
|
||||
|
||||
return ratio
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import db
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common.gettextutils import _LW
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.scheduler import filters
|
||||
from nova.scheduler.filters import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -80,27 +80,19 @@ class AggregateRamFilter(BaseRamFilter):
|
|||
"""
|
||||
|
||||
def _get_ram_allocation_ratio(self, host_state, filter_properties):
|
||||
context = filter_properties['context']
|
||||
# TODO(uni): DB query in filter is a performance hit, especially for
|
||||
# system with lots of hosts. Will need a general solution here to fix
|
||||
# all filters with aggregate DB call things.
|
||||
metadata = db.aggregate_metadata_get_by_host(
|
||||
context, host_state.host, key='ram_allocation_ratio')
|
||||
aggregate_vals = metadata.get('ram_allocation_ratio', set())
|
||||
num_values = len(aggregate_vals)
|
||||
|
||||
if num_values == 0:
|
||||
return CONF.ram_allocation_ratio
|
||||
|
||||
if num_values > 1:
|
||||
LOG.warning(_("%(num_values)d ratio values found, "
|
||||
"of which the minimum value will be used."),
|
||||
{'num_values': num_values})
|
||||
aggregate_vals = utils.aggregate_values_from_db(
|
||||
filter_properties['context'],
|
||||
host_state.host,
|
||||
'ram_allocation_ratio')
|
||||
|
||||
try:
|
||||
ratio = float(min(aggregate_vals))
|
||||
ratio = utils.validate_num_values(
|
||||
aggregate_vals, CONF.ram_allocation_ratio, cast_to=float)
|
||||
except ValueError as e:
|
||||
LOG.warning(_("Could not decode ram_allocation_ratio: '%s'"), e)
|
||||
LOG.warning(_LW("Could not decode ram_allocation_ratio: '%s'"), e)
|
||||
ratio = CONF.ram_allocation_ratio
|
||||
|
||||
return ratio
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
from nova import db
|
||||
from nova.scheduler import filters
|
||||
from nova.scheduler.filters import utils
|
||||
|
||||
|
||||
class TypeAffinityFilter(filters.BaseHostFilter):
|
||||
|
@ -51,8 +52,13 @@ class AggregateTypeAffinityFilter(filters.BaseHostFilter):
|
|||
|
||||
def host_passes(self, host_state, filter_properties):
|
||||
instance_type = filter_properties.get('instance_type')
|
||||
context = filter_properties['context']
|
||||
metadata = db.aggregate_metadata_get_by_host(
|
||||
context, host_state.host, key='instance_type')
|
||||
return (len(metadata) == 0 or
|
||||
instance_type['name'] in metadata['instance_type'])
|
||||
|
||||
# TODO(uni): DB query in filter is a performance hit, especially for
|
||||
# system with lots of hosts. Will need a general solution here to fix
|
||||
# all filters with aggregate DB call things.
|
||||
aggregate_vals = utils.aggregate_values_from_db(
|
||||
filter_properties['context'], host_state.host, 'instance_type')
|
||||
|
||||
if not aggregate_vals:
|
||||
return True
|
||||
return instance_type['name'] in aggregate_vals
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# 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.
|
||||
|
||||
"""Bench of utility methods used by filters."""
|
||||
|
||||
|
||||
from nova.objects import aggregate
|
||||
from nova.openstack.common.gettextutils import _LI
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def aggregate_values_from_db(context, host, key_name):
|
||||
"""Returns a set of values based on a metadata key for a specific host."""
|
||||
# TODO(sahid): DB query in filter is a performance hit, especially for
|
||||
# system with lots of hosts. Will need a general solution here to fix
|
||||
# all filters with aggregate DB call things.
|
||||
aggrlist = aggregate.AggregateList.get_by_host(
|
||||
context.elevated(), host, key=key_name)
|
||||
aggregate_vals = set(aggr.metadata[key_name] for aggr in aggrlist)
|
||||
return aggregate_vals
|
||||
|
||||
|
||||
def validate_num_values(vals, default=None, cast_to=int, based_on=min):
|
||||
"""Returns a corretly casted value based on a set of values.
|
||||
|
||||
This method is useful to work with per-aggregate filters, It takes
|
||||
a set of values then return the 'based_on'{min/max} converted to
|
||||
'cast_to' of the set or the default value.
|
||||
|
||||
Note: The cast implies a possible ValueError
|
||||
"""
|
||||
num_values = len(vals)
|
||||
if num_values == 0:
|
||||
return default
|
||||
|
||||
if num_values > 1:
|
||||
LOG.info(_LI("%(num_values)d values found, "
|
||||
"of which the minimum value will be used."),
|
||||
{'num_values': num_values})
|
||||
|
||||
return cast_to(based_on(vals))
|
|
@ -0,0 +1,44 @@
|
|||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from nova.scheduler.filters import utils
|
||||
from nova import test
|
||||
|
||||
|
||||
class UtilsTestCase(test.NoDBTestCase):
|
||||
def test_validate_num_values(self):
|
||||
f = utils.validate_num_values
|
||||
|
||||
self.assertEqual("x", f(set(), default="x"))
|
||||
self.assertEqual(1, f(set(["1"]), cast_to=int))
|
||||
self.assertEqual(1.0, f(set(["1"]), cast_to=float))
|
||||
self.assertEqual(1, f(set([1, 2]), based_on=min))
|
||||
self.assertEqual(2, f(set([1, 2]), based_on=max))
|
||||
|
||||
@mock.patch("nova.objects.aggregate.AggregateList.get_by_host")
|
||||
def test_aggregate_values_from_db(self, get_by_host):
|
||||
aggrA = mock.MagicMock()
|
||||
aggrB = mock.MagicMock()
|
||||
context = mock.MagicMock()
|
||||
|
||||
get_by_host.return_value = [aggrA, aggrB]
|
||||
aggrA.metadata = {'k1': 1, 'k2': 2}
|
||||
aggrB.metadata = {'k1': 3, 'k2': 4}
|
||||
|
||||
values = utils.aggregate_values_from_db(context, 'h1', key_name='k1')
|
||||
|
||||
self.assertTrue(context.elevated.called)
|
||||
self.assertEqual(set([1, 3]), values)
|
Loading…
Reference in New Issue