Move nova extra_specs_ops to oslo.utils
Ironic needs to do some capability matching that has feature parity with Nova's capability matching. Rather than trying to re-create it ourselves, move Nova's matching code into oslo.utils so that we make sure it's always the same. Also has small modifications to handle pep8 complaints in oslo.utils, renaming the file and test names to be more generalized, and to use oslotest instead of nova.test. Change-Id: I3b70afdf1479b6649feac509b794d04fc5836194
This commit is contained in:
parent
3ac0253bdb
commit
e97f08bb07
69
oslo_utils/specs_matcher.py
Normal file
69
oslo_utils/specs_matcher.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Copyright (c) 2011 OpenStack Foundation
|
||||||
|
# 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 operator
|
||||||
|
|
||||||
|
# 1. The following operations are supported:
|
||||||
|
# =, s==, s!=, s>=, s>, s<=, s<, <in>, <all-in>, <or>, ==, !=, >=, <=
|
||||||
|
# 2. Note that <or> is handled in a different way below.
|
||||||
|
# 3. If the first word in the extra_specs is not one of the operators,
|
||||||
|
# it is ignored.
|
||||||
|
op_methods = {
|
||||||
|
'=': lambda x, y: float(x) >= float(y),
|
||||||
|
'<in>': lambda x, y: y in x,
|
||||||
|
'<all-in>': lambda x, y: all(val in x for val in y),
|
||||||
|
'==': lambda x, y: float(x) == float(y),
|
||||||
|
'!=': lambda x, y: float(x) != float(y),
|
||||||
|
'>=': lambda x, y: float(x) >= float(y),
|
||||||
|
'<=': lambda x, y: float(x) <= float(y),
|
||||||
|
's==': operator.eq,
|
||||||
|
's!=': operator.ne,
|
||||||
|
's<': operator.lt,
|
||||||
|
's<=': operator.le,
|
||||||
|
's>': operator.gt,
|
||||||
|
's>=': operator.ge
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def match(value, req):
|
||||||
|
words = req.split()
|
||||||
|
|
||||||
|
op = method = None
|
||||||
|
if words:
|
||||||
|
op = words.pop(0)
|
||||||
|
method = op_methods.get(op)
|
||||||
|
|
||||||
|
if op != '<or>' and not method:
|
||||||
|
return value == req
|
||||||
|
|
||||||
|
if value is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if op == '<or>': # Ex: <or> v1 <or> v2 <or> v3
|
||||||
|
while True:
|
||||||
|
if words.pop(0) == value:
|
||||||
|
return True
|
||||||
|
if not words:
|
||||||
|
break
|
||||||
|
words.pop(0) # remove a keyword <or>
|
||||||
|
if not words:
|
||||||
|
break
|
||||||
|
return False
|
||||||
|
|
||||||
|
if words:
|
||||||
|
if op == '<all-in>': # requires a list not a string
|
||||||
|
return method(value, words)
|
||||||
|
return method(value, words[0])
|
||||||
|
return False
|
236
oslo_utils/tests/test_specs_matcher.py
Normal file
236
oslo_utils/tests/test_specs_matcher.py
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslotest import base as test_base
|
||||||
|
|
||||||
|
from oslo_utils import specs_matcher
|
||||||
|
|
||||||
|
|
||||||
|
class SpecsMatcherTestCase(test_base.BaseTestCase):
|
||||||
|
def _do_specs_matcher_test(self, value, req, matches):
|
||||||
|
assertion = self.assertTrue if matches else self.assertFalse
|
||||||
|
assertion(specs_matcher.match(value, req))
|
||||||
|
|
||||||
|
def test_specs_matches_simple(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='1',
|
||||||
|
req='1',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_simple(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='',
|
||||||
|
req='1',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_simple2(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='3',
|
||||||
|
req='1',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_simple3(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='222',
|
||||||
|
req='2',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_bogus_ops(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='4',
|
||||||
|
req='> 2',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_eq(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='123',
|
||||||
|
req='= 123',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_eq2(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='124',
|
||||||
|
req='= 123',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_eq(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='34',
|
||||||
|
req='= 234',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_eq3(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='34',
|
||||||
|
req='=',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_seq(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='123',
|
||||||
|
req='s== 123',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_seq(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='1234',
|
||||||
|
req='s== 123',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_sneq(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='1234',
|
||||||
|
req='s!= 123',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_sneq(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='123',
|
||||||
|
req='s!= 123',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_sge(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='1000',
|
||||||
|
req='s>= 234',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_sle(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='1234',
|
||||||
|
req='s<= 1000',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_sl(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='2',
|
||||||
|
req='s< 12',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_sg(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12',
|
||||||
|
req='s> 2',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_in(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12311321',
|
||||||
|
req='<in> 11',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_in2(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12311321',
|
||||||
|
req='<in> 12311321',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_in3(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12311321',
|
||||||
|
req='<in> 12311321 <in>',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_in(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12310321',
|
||||||
|
req='<in> 11',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_in2(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12310321',
|
||||||
|
req='<in> 11 <in>',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_or(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12',
|
||||||
|
req='<or> 11 <or> 12',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_or2(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='12',
|
||||||
|
req='<or> 11 <or> 12 <or>',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_or(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='13',
|
||||||
|
req='<or> 11 <or> 12',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_or2(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='13',
|
||||||
|
req='<or> 11 <or> 12 <or>',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_le(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='2',
|
||||||
|
req='<= 10',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_le(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='3',
|
||||||
|
req='<= 2',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_with_op_ge(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='3',
|
||||||
|
req='>= 1',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_ge(self):
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value='2',
|
||||||
|
req='>= 3',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_matches_all_with_op_allin(self):
|
||||||
|
values = ['aes', 'mmx', 'aux']
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value=str(values),
|
||||||
|
req='<all-in> aes mmx',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_matches_one_with_op_allin(self):
|
||||||
|
values = ['aes', 'mmx', 'aux']
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value=str(values),
|
||||||
|
req='<all-in> mmx',
|
||||||
|
matches=True)
|
||||||
|
|
||||||
|
def test_specs_fails_with_op_allin(self):
|
||||||
|
values = ['aes', 'mmx', 'aux']
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value=str(values),
|
||||||
|
req='<all-in> txt',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_all_with_op_allin(self):
|
||||||
|
values = ['aes', 'mmx', 'aux']
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value=str(values),
|
||||||
|
req='<all-in> txt 3dnow',
|
||||||
|
matches=False)
|
||||||
|
|
||||||
|
def test_specs_fails_match_one_with_op_allin(self):
|
||||||
|
values = ['aes', 'mmx', 'aux']
|
||||||
|
self._do_specs_matcher_test(
|
||||||
|
value=str(values),
|
||||||
|
req='<all-in> txt aes',
|
||||||
|
matches=False)
|
Loading…
Reference in New Issue
Block a user