Added attribute filter to scheduler
Change-Id: I80ce5621c75f05b794a4093696cf118a35a702ea
This commit is contained in:
parent
a80a704ef4
commit
ae8532248c
@ -11,15 +11,18 @@
|
||||
# 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 six
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils.strutils import bool_from_string
|
||||
|
||||
from designate.objects import PoolAttributeList
|
||||
from designate.scheduler.filters.base import Filter
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AttributeFilter(Filter):
|
||||
"""This allows users top choose the pool by supplying hints to this filter.
|
||||
"""This allows users to choose the pool by supplying hints to this filter.
|
||||
These are provided as attributes as part of the zone object provided at
|
||||
zone create time.
|
||||
|
||||
@ -29,7 +32,7 @@ class AttributeFilter(Filter):
|
||||
{
|
||||
"attributes": {
|
||||
"pool_level": "gold",
|
||||
"fast_ttl": True,
|
||||
"fast_ttl": "true",
|
||||
"pops": "global",
|
||||
},
|
||||
"email": "user@example.com",
|
||||
@ -39,11 +42,6 @@ class AttributeFilter(Filter):
|
||||
The zone attributes are matched against the potential pool candiates, and
|
||||
any pools that do not match **all** hints are removed.
|
||||
|
||||
.. warning::
|
||||
|
||||
This filter is disabled currently, and should not be used.
|
||||
It will be enabled at a later date.
|
||||
|
||||
.. warning::
|
||||
|
||||
This should be uses in conjunction with the
|
||||
@ -58,5 +56,57 @@ class AttributeFilter(Filter):
|
||||
"""
|
||||
|
||||
def filter(self, context, pools, zone):
|
||||
# FIXME (graham) actually filter on attributes
|
||||
|
||||
zone_attributes = zone.attributes.to_dict()
|
||||
|
||||
def evaluate_pool(pool):
|
||||
|
||||
pool_attributes = pool.attributes.to_dict()
|
||||
|
||||
# Check if the keys requested exist in this pool
|
||||
if not {key for key in six.iterkeys(pool_attributes)}.issuperset(
|
||||
zone_attributes):
|
||||
msg = "%(pool)s did not match list of requested attribute "\
|
||||
"keys - removing from list. Requested: %(r_key)s. Pool:"\
|
||||
"%(p_key)s"
|
||||
|
||||
LOG.debug(
|
||||
msg,
|
||||
{
|
||||
'pool': pool,
|
||||
'r_key': zone_attributes,
|
||||
'p_key': pool_attributes
|
||||
}
|
||||
)
|
||||
# Missing required keys - remove from the list
|
||||
return False
|
||||
|
||||
for key in six.iterkeys(zone_attributes):
|
||||
LOG.debug("Checking value of %s for %s", key, pool)
|
||||
|
||||
pool_attr = bool_from_string(pool_attributes[key],
|
||||
default=pool_attributes[key])
|
||||
zone_attr = bool_from_string(zone_attributes[key],
|
||||
default=zone_attributes[key])
|
||||
|
||||
if not pool_attr == zone_attr:
|
||||
LOG.debug(
|
||||
"%(pool)s did not match requested value of %(key)s. "
|
||||
"Requested: %(r_val)s. Pool: %(p_val)s",
|
||||
{
|
||||
'pool': pool,
|
||||
'key': key,
|
||||
'r_val': zone_attr,
|
||||
'p_val': pool_attr
|
||||
})
|
||||
# Value didn't match - remove from the list
|
||||
return False
|
||||
|
||||
# Pool matches list of attributes - keep
|
||||
return True
|
||||
|
||||
pool_list = [pool for pool in pools if evaluate_pool(pool)]
|
||||
|
||||
pools = PoolAttributeList(objects=pool_list)
|
||||
|
||||
return pools
|
||||
|
@ -67,7 +67,7 @@ class HeartBeatEmitter(plugin.DriverPlugin):
|
||||
if not self._running:
|
||||
return
|
||||
|
||||
LOG.debug("Emitting heartbeat...")
|
||||
LOG.trace("Emitting heartbeat...")
|
||||
|
||||
status, stats, capabilities = self._get_status()
|
||||
|
||||
|
@ -22,6 +22,7 @@ from oslotest import base as test
|
||||
from designate.scheduler.filters import default_pool_filter
|
||||
from designate.scheduler.filters import fallback_filter
|
||||
from designate.scheduler.filters import pool_id_attribute_filter
|
||||
from designate.scheduler.filters import attribute_filter
|
||||
from designate import objects
|
||||
from designate import context
|
||||
from designate import policy
|
||||
@ -202,3 +203,221 @@ class SchedulerPoolIDAttributeFilterTest(SchedulerFilterTest):
|
||||
'zone_create_forced_pool',
|
||||
self.context,
|
||||
pools[0])
|
||||
|
||||
|
||||
class SchedulerAttributeFilterTest(SchedulerFilterTest):
|
||||
|
||||
FILTER = attribute_filter.AttributeFilter
|
||||
|
||||
def setUp(self):
|
||||
super(SchedulerAttributeFilterTest, self).setUp()
|
||||
|
||||
self.zone = objects.Zone(
|
||||
name="example.com.",
|
||||
type="PRIMARY",
|
||||
email="hostmaster@example.com",
|
||||
attributes=objects.ZoneAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_one",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_two",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_three",
|
||||
"value": "foo"
|
||||
}
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
def test_default_operation(self):
|
||||
pools = objects.PoolList.from_list(
|
||||
[
|
||||
{
|
||||
"id": "6c346011-e581-429b-a7a2-6cdf0aba91c3",
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
pools[0].attributes = objects.PoolAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_one",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_two",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_three",
|
||||
"value": "foo"
|
||||
}
|
||||
])
|
||||
|
||||
pools = self.test_filter.filter(self.context, pools, self.zone)
|
||||
|
||||
self.assertEqual("6c346011-e581-429b-a7a2-6cdf0aba91c3", pools[0].id)
|
||||
|
||||
def test_multiple_pools_all_match(self):
|
||||
pools = objects.PoolList.from_list(
|
||||
[
|
||||
{"id": "6c346011-e581-429b-a7a2-6cdf0aba91c3"},
|
||||
{"id": "5fabcd37-262c-4cf3-8625-7f419434b6df"}
|
||||
]
|
||||
|
||||
)
|
||||
|
||||
attributes = objects.PoolAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_one",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_two",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_three",
|
||||
"value": "foo"
|
||||
}
|
||||
])
|
||||
|
||||
pools[0].attributes = attributes
|
||||
pools[1].attributes = attributes
|
||||
|
||||
pools = self.test_filter.filter(self.context, pools, self.zone)
|
||||
|
||||
self.assertEqual(2, len(pools))
|
||||
|
||||
def test_multiple_pools_one_match(self):
|
||||
pools = objects.PoolList.from_list(
|
||||
[
|
||||
{"id": "6c346011-e581-429b-a7a2-6cdf0aba91c3"},
|
||||
{"id": "5fabcd37-262c-4cf3-8625-7f419434b6df"}
|
||||
]
|
||||
|
||||
)
|
||||
|
||||
pool_0_attributes = objects.PoolAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_one",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_two",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_three",
|
||||
"value": "foo"
|
||||
}
|
||||
])
|
||||
|
||||
pool_1_attributes = objects.PoolAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_four",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_five",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_three",
|
||||
"value": "foo"
|
||||
}
|
||||
])
|
||||
|
||||
pools[0].attributes = pool_0_attributes
|
||||
pools[1].attributes = pool_1_attributes
|
||||
|
||||
pools = self.test_filter.filter(self.context, pools, self.zone)
|
||||
|
||||
self.assertEqual(1, len(pools))
|
||||
self.assertEqual("6c346011-e581-429b-a7a2-6cdf0aba91c3", pools[0].id)
|
||||
|
||||
def test_multiple_pools_no_match(self):
|
||||
pools = objects.PoolList.from_list(
|
||||
[
|
||||
{"id": "6c346011-e581-429b-a7a2-6cdf0aba91c3"},
|
||||
{"id": "5fabcd37-262c-4cf3-8625-7f419434b6df"}
|
||||
]
|
||||
|
||||
)
|
||||
|
||||
pool_0_attributes = objects.PoolAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_six",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_two",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_seven",
|
||||
"value": "foo"
|
||||
}
|
||||
])
|
||||
|
||||
pool_1_attributes = objects.PoolAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_four",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_five",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_three",
|
||||
"value": "foo"
|
||||
}
|
||||
])
|
||||
|
||||
pools[0].attributes = pool_0_attributes
|
||||
pools[1].attributes = pool_1_attributes
|
||||
|
||||
pools = self.test_filter.filter(self.context, pools, self.zone)
|
||||
|
||||
self.assertEqual(0, len(pools))
|
||||
|
||||
def test_no_match_non_bool(self):
|
||||
pools = objects.PoolList.from_list(
|
||||
[
|
||||
{"id": "6c346011-e581-429b-a7a2-6cdf0aba91c3"},
|
||||
]
|
||||
|
||||
)
|
||||
|
||||
pool_0_attributes = objects.PoolAttributeList.from_list(
|
||||
[
|
||||
{
|
||||
"key": "attribute_one",
|
||||
"value": "True"
|
||||
},
|
||||
{
|
||||
"key": "attribute_two",
|
||||
"value": "False"
|
||||
},
|
||||
{
|
||||
"key": "attribute_three",
|
||||
"value": "bar"
|
||||
}
|
||||
])
|
||||
|
||||
pools[0].attributes = pool_0_attributes
|
||||
|
||||
pools = self.test_filter.filter(self.context, pools, self.zone)
|
||||
|
||||
self.assertEqual(0, len(pools))
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- Addition of the "attribute" filter for scheduling zones across pools.
|
||||
This can be enabled in the ``[service:central]`` section of the config
|
||||
by adding ``attribute`` to the list of values in the ``filters`` option.
|
@ -104,6 +104,7 @@ designate.quota =
|
||||
|
||||
designate.scheduler.filters =
|
||||
fallback = designate.scheduler.filters.fallback_filter:FallbackFilter
|
||||
attribute = designate.scheduler.filters.attribute_filter:AttributeFilter
|
||||
random = designate.scheduler.filters.random_filter:RandomFilter
|
||||
pool_id_attribute = designate.scheduler.filters.pool_id_attribute_filter:PoolIDAttributeFilter
|
||||
default_pool = designate.scheduler.filters.default_pool_filter:DefaultPoolFilter
|
||||
|
Loading…
x
Reference in New Issue
Block a user