mirror of
https://github.com/xcat2/confluent.git
synced 2025-01-17 21:23:18 +00:00
Add regex names and attribute match to noderange
Enhance noderange to implement: attribute.name==value (also attribute.name=value) -- strict equality match attribute.name=~value -- regular expression match attribute.name!=value -- strict negative match attribute.name!~value -- negative regular expression match ~nameexpression -- search by node name using regular expression This also goes a step further by intelligently limiting searches when found to the right of @ or ,-.
This commit is contained in:
parent
8b5d5b1f33
commit
a85ffa8f8b
@ -444,6 +444,76 @@ class ConfigManager(object):
|
||||
self._cfgstore['nodes'] = {}
|
||||
self._bg_sync_to_file()
|
||||
|
||||
def filter_node_attributes(self, expression, nodes=None):
|
||||
"""Filtered nodelist according to expression
|
||||
|
||||
expression may be:
|
||||
attribute.name=value
|
||||
attribute.name==value
|
||||
attribute.name=~value
|
||||
attribute.name!=value
|
||||
attribute.name!~value
|
||||
|
||||
== and != do strict equality. The ~ operators do a regular expression.
|
||||
! negates the sense of the match
|
||||
|
||||
:param expression: The expression containing the criteria to match
|
||||
:param nodes: Optional iterable set of nodes to limit the check
|
||||
"""
|
||||
exmatch = None
|
||||
yieldmatches = True
|
||||
if nodes is None:
|
||||
nodes = self._cfgstore['nodes']
|
||||
if '==' in expression:
|
||||
attribute, match = expression.split('==')
|
||||
elif '!=' in expression:
|
||||
attribute, match = expression.split('!=')
|
||||
yieldmatches = False
|
||||
elif '=~' in expression:
|
||||
attribute, match = expression.split('=~')
|
||||
exmatch = re.compile(match)
|
||||
elif '!~' in expression:
|
||||
attribute, match = expression.split('!~')
|
||||
exmatch = re.compile(match)
|
||||
yieldmatches = False
|
||||
elif '=' in expression:
|
||||
attribute, match = expression.split('=')
|
||||
else:
|
||||
raise Exception('Invalid Expression')
|
||||
for node in nodes:
|
||||
try:
|
||||
currval = self._cfgstore['nodes'][node][attribute]['value']
|
||||
except KeyError:
|
||||
# Let's treat 'not set' as being an empty string for this path
|
||||
currval = ''
|
||||
if exmatch:
|
||||
if yieldmatches:
|
||||
if exmatch.search(currval):
|
||||
yield node
|
||||
else:
|
||||
if not exmatch.search(currval):
|
||||
yield node
|
||||
else:
|
||||
if yieldmatches:
|
||||
if match == currval:
|
||||
yield node
|
||||
else:
|
||||
if match != currval:
|
||||
yield node
|
||||
|
||||
def filter_nodenames(self, expression, nodes=None):
|
||||
"""Filter nodenames by regular expression
|
||||
|
||||
:param expression: Regular expression for matching nodenames
|
||||
:param nodes: Optional iterable of candidates
|
||||
"""
|
||||
if nodes is None:
|
||||
nodes = self._cfgstore['nodes']
|
||||
expression = re.compile(expression)
|
||||
for node in nodes:
|
||||
if expression.search(node):
|
||||
yield node
|
||||
|
||||
def watch_attributes(self, nodes, attributes, callback):
|
||||
"""
|
||||
Watch a list of attributes for changes on a list of nodes
|
||||
@ -680,7 +750,7 @@ class ConfigManager(object):
|
||||
cfgnodeobj = self._cfgstore['nodes'][node]
|
||||
nodeobj = {}
|
||||
if len(attributes) == 0:
|
||||
relattribs = cfgnodeobj.iterkeys()
|
||||
relattribs = cfgnodeobj
|
||||
for attribute in relattribs:
|
||||
if attribute.startswith('_'):
|
||||
# skip private things
|
||||
|
@ -269,7 +269,6 @@ def enumerate_node_collection(collectionpath, configmanager):
|
||||
collection = nested_lookup(noderesources, collectionpath[2:])
|
||||
if len(collectionpath) == 2 and collectionpath[0] == 'noderange':
|
||||
collection['nodes'] = {}
|
||||
print repr(collection)
|
||||
if not isinstance(collection, dict):
|
||||
raise exc.NotFoundException("Invalid element requested")
|
||||
return iterate_resources(collection)
|
||||
|
@ -24,7 +24,7 @@ import itertools
|
||||
import pyparsing as pp
|
||||
|
||||
# construct custom grammar with pyparsing
|
||||
_nodeword = pp.Word(pp.alphanums + '/=-:.*+')
|
||||
_nodeword = pp.Word(pp.alphanums + '~^$/=-:.*+!')
|
||||
_nodebracket = pp.QuotedString(quoteChar='[', endQuoteChar=']',
|
||||
unquoteResults=False)
|
||||
_nodeatom = pp.Group(pp.OneOrMore(_nodeword | _nodebracket))
|
||||
@ -51,11 +51,11 @@ class NodeRange(object):
|
||||
def nodes(self):
|
||||
return self._noderange
|
||||
|
||||
def _evaluate(self, parsetree):
|
||||
def _evaluate(self, parsetree, filternodes=None):
|
||||
current_op = 0 # enum, 0 union, 1 subtract, 2 intersect
|
||||
current_range = set([])
|
||||
if not isinstance(parsetree[0], list): # down to a plain text thing
|
||||
return self._expandstring(parsetree)
|
||||
return self._expandstring(parsetree, filternodes)
|
||||
for elem in parsetree:
|
||||
if elem == ',-':
|
||||
current_op = 1
|
||||
@ -66,9 +66,9 @@ class NodeRange(object):
|
||||
elif current_op == 0:
|
||||
current_range |= self._evaluate(elem)
|
||||
elif current_op == 1:
|
||||
current_range -= self._evaluate(elem)
|
||||
current_range -= self._evaluate(elem, current_range)
|
||||
elif current_op == 2:
|
||||
current_range &= self._evaluate(elem)
|
||||
current_range &= self._evaluate(elem, current_range)
|
||||
return current_range
|
||||
|
||||
def failorreturn(self, atom):
|
||||
@ -134,7 +134,7 @@ class NodeRange(object):
|
||||
grpcfg['noderange']['value'], self.cfm).nodes
|
||||
return nodes
|
||||
|
||||
def _expandstring(self, element):
|
||||
def _expandstring(self, element, filternodes=None):
|
||||
prefix = ''
|
||||
for idx in xrange(len(element)):
|
||||
if element[idx][0] == '[':
|
||||
@ -161,10 +161,15 @@ class NodeRange(object):
|
||||
return self.expandrange(element, '-')
|
||||
elif ':' in element: # : range for less ambiguity
|
||||
return self.expandrange(element, ':')
|
||||
elif '=' in element:
|
||||
raise Exception('TODO: criteria noderange')
|
||||
elif '=' in element or '!~' in element:
|
||||
if self.cfm is None:
|
||||
raise Exception('Verification configmanager required')
|
||||
return set(self.cfm.filter_node_attributes(element, filternodes))
|
||||
elif element[0] in ('/', '~'):
|
||||
raise Exception('TODO: regex noderange')
|
||||
nameexpression = element[1:]
|
||||
if self.cfm is None:
|
||||
raise Exception('Verification configmanager required')
|
||||
return set(self.cfm.filter_nodenames(nameexpression, filternodes))
|
||||
elif '+' in element:
|
||||
raise Exception('TODO: plus range')
|
||||
if self.cfm is None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user