Added attribute filter to scheduler
Change-Id: I80ce5621c75f05b794a4093696cf118a35a702ea
This commit is contained in:
@@ -11,15 +11,18 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import six
|
||||||
from oslo_log import log as logging
|
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
|
from designate.scheduler.filters.base import Filter
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AttributeFilter(Filter):
|
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
|
These are provided as attributes as part of the zone object provided at
|
||||||
zone create time.
|
zone create time.
|
||||||
|
|
||||||
@@ -29,7 +32,7 @@ class AttributeFilter(Filter):
|
|||||||
{
|
{
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"pool_level": "gold",
|
"pool_level": "gold",
|
||||||
"fast_ttl": True,
|
"fast_ttl": "true",
|
||||||
"pops": "global",
|
"pops": "global",
|
||||||
},
|
},
|
||||||
"email": "user@example.com",
|
"email": "user@example.com",
|
||||||
@@ -39,11 +42,6 @@ class AttributeFilter(Filter):
|
|||||||
The zone attributes are matched against the potential pool candiates, and
|
The zone attributes are matched against the potential pool candiates, and
|
||||||
any pools that do not match **all** hints are removed.
|
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::
|
.. warning::
|
||||||
|
|
||||||
This should be uses in conjunction with the
|
This should be uses in conjunction with the
|
||||||
@@ -58,5 +56,57 @@ class AttributeFilter(Filter):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def filter(self, context, pools, zone):
|
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
|
return pools
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class HeartBeatEmitter(plugin.DriverPlugin):
|
|||||||
if not self._running:
|
if not self._running:
|
||||||
return
|
return
|
||||||
|
|
||||||
LOG.debug("Emitting heartbeat...")
|
LOG.trace("Emitting heartbeat...")
|
||||||
|
|
||||||
status, stats, capabilities = self._get_status()
|
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 default_pool_filter
|
||||||
from designate.scheduler.filters import fallback_filter
|
from designate.scheduler.filters import fallback_filter
|
||||||
from designate.scheduler.filters import pool_id_attribute_filter
|
from designate.scheduler.filters import pool_id_attribute_filter
|
||||||
|
from designate.scheduler.filters import attribute_filter
|
||||||
from designate import objects
|
from designate import objects
|
||||||
from designate import context
|
from designate import context
|
||||||
from designate import policy
|
from designate import policy
|
||||||
@@ -202,3 +203,221 @@ class SchedulerPoolIDAttributeFilterTest(SchedulerFilterTest):
|
|||||||
'zone_create_forced_pool',
|
'zone_create_forced_pool',
|
||||||
self.context,
|
self.context,
|
||||||
pools[0])
|
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 =
|
designate.scheduler.filters =
|
||||||
fallback = designate.scheduler.filters.fallback_filter:FallbackFilter
|
fallback = designate.scheduler.filters.fallback_filter:FallbackFilter
|
||||||
|
attribute = designate.scheduler.filters.attribute_filter:AttributeFilter
|
||||||
random = designate.scheduler.filters.random_filter:RandomFilter
|
random = designate.scheduler.filters.random_filter:RandomFilter
|
||||||
pool_id_attribute = designate.scheduler.filters.pool_id_attribute_filter:PoolIDAttributeFilter
|
pool_id_attribute = designate.scheduler.filters.pool_id_attribute_filter:PoolIDAttributeFilter
|
||||||
default_pool = designate.scheduler.filters.default_pool_filter:DefaultPoolFilter
|
default_pool = designate.scheduler.filters.default_pool_filter:DefaultPoolFilter
|
||||||
|
|||||||
Reference in New Issue
Block a user