2
0
mirror of https://github.com/xcat2/confluent.git synced 2025-01-17 21:23:18 +00:00

First part of hooking noderange into configuration

Make expansion of groups and rejection of unrecognized elements work.
Additionally, implement dynamic groups.  A group may have a 'noderange'
attribute.  In this case it is considered to have 'dynamic' members.  This
only has meaning in noderange expansion, not in deriving configuration data.
This does not yet add the search by attribute value capabilities and still
doesn't do the pagination or + operators.
This commit is contained in:
Jarrod Johnson 2015-03-12 15:38:50 -04:00
parent 7a346ed0d3
commit 0515acd054
2 changed files with 31 additions and 23 deletions

View File

@ -796,7 +796,7 @@ class ConfigManager(object):
if not autocreate and group not in self._cfgstore['nodegroups']:
raise ValueError("{0} group does not exist".format(group))
for attr in attribmap[group].iterkeys():
if (attr != 'nodes' and
if (attr not in ('nodes', 'noderange') and
(attr not in allattributes.node or
('type' in allattributes.node[attr] and
not isinstance(attribmap[group][attr],
@ -819,6 +819,8 @@ class ConfigManager(object):
for attr in attribmap[group].iterkeys():
if attr == 'nodes':
newdict = set(attribmap[group][attr])
elif attr == 'noderange':
newdict = attribmap[group][attr]
elif (isinstance(attribmap[group][attr], str) or
isinstance(attribmap[group][attr], unicode)):
newdict = {'value': attribmap[group][attr]}
@ -832,7 +834,7 @@ class ConfigManager(object):
self._sync_nodes_to_group(group=group,
nodes=attribmap[group]['nodes'],
changeset=changeset)
else: # update inheritence
elif attr != 'noderange': # update inheritence
for node in cfgobj['nodes']:
nodecfg = self._cfgstore['nodes'][node]
self._do_inheritance(nodecfg, attr, node, changeset,

View File

@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2014 IBM Corporation
# Copyright 2015 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -21,7 +22,6 @@
import itertools
import pyparsing as pp
import re
# construct custom grammar with pyparsing
_nodeword = pp.Word(pp.alphanums + '/=-:.*+')
@ -33,16 +33,17 @@ _parser = pp.nestedExpr(content=_grammar)
_numextractor = pp.OneOrMore(pp.Word(pp.alphas + '-') | pp.Word(pp.nums))
#TODO: pagination operators <pp.nums and >pp.nums for begin and end respective
# TODO: pagination operators <pp.nums and >pp.nums for begin and end respective
class NodeRange(object):
"""Iterate over a noderange
:param noderange: string representing a noderange to evaluate
:param verify: whether or not to perform lookups in the config
:param config: Config manager object to use to vet elements
"""
def __init__(self, noderange, verify=True):
self.verify = verify
def __init__(self, noderange, config=None):
self.cfm = config
elements = _parser.parseString("(" + noderange + ")").asList()[0]
self._noderange = self._evaluate(elements)
@ -71,21 +72,21 @@ class NodeRange(object):
return current_range
def failorreturn(self, atom):
if self.verify:
if self.cfm is not None:
raise Exception(atom + " not valid")
return set([atom])
def expandrange(self, range, delimiter):
pieces = range.split(delimiter)
def expandrange(self, seqrange, delimiter):
pieces = seqrange.split(delimiter)
if len(pieces) % 2 != 0:
return self.failorreturn(range)
return self.failorreturn(seqrange)
halflen = len(pieces) / 2
left = delimiter.join(pieces[:halflen])
right = delimiter.join(pieces[halflen:])
leftbits = _numextractor.parseString(left).asList()
rightbits = _numextractor.parseString(right).asList()
if len(leftbits) != len(rightbits):
return self.failorreturn(range)
return self.failorreturn(seqrange)
finalfmt = ''
iterators = []
for idx in xrange(len(leftbits)):
@ -93,7 +94,7 @@ class NodeRange(object):
finalfmt += leftbits[idx]
elif leftbits[idx][0] in pp.alphas:
# if string portion unequal, not going to work
return self.failorreturn(range)
return self.failorreturn(seqrange)
else:
curseq = []
finalfmt += '{%d}' % len(iterators)
@ -109,7 +110,7 @@ class NodeRange(object):
minnum = leftnum
maxnum = rightnum + 1
else: # differently padded, but same number...
return self.failorreturn(range)
return self.failorreturn(seqrange)
numformat = '{0:0%d}' % width
for num in xrange(minnum, maxnum):
curseq.append(numformat.format(num))
@ -123,19 +124,23 @@ class NodeRange(object):
for idx in xrange(len(element)):
if element[idx][0] == '[':
nodes = set([])
for numeric in NodeRange(element[idx][1:-1], False).nodes:
for numeric in NodeRange(element[idx][1:-1]).nodes:
nodes |= self._expandstring(
[prefix + numeric] + element[idx + 1:])
[prefix + numeric] + element[idx + 1:])
return nodes
else:
prefix += element[idx]
element = prefix
nodes = set([])
if self.verify:
#this is where we would check for exactly this
raise Exception("TODO: link with actual config")
#this is where we would check for a literal groupname
#ok, now time to understand the various things
if self.cfm is not None:
# this is where we would check for exactly this
if self.cfm.is_node(element):
return set([element])
if self.cfm.is_nodegroup(element):
grpcfg = self.cfm.get_nodegroup_attributes(element)
nodes = grpcfg['nodes']
if 'noderange' in grpcfg and grpcfg['noderange']:
nodes |= NodeRange(grpcfg['noderange'], self.cfm).nodes
return nodes
if '-' in element and ':' not in element:
return self.expandrange(element, '-')
elif ':' in element: # : range for less ambiguity
@ -146,5 +151,6 @@ class NodeRange(object):
raise Exception('TODO: regex noderange')
elif '+' in element:
raise Exception('TODO: plus range')
if not self.verify:
if self.cfm is None:
return set([element])
raise Exception(element + ' not a recognized node, group, or alias')