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

Allow noderange pagination of all nodes

When a noderange starts with '<' or '>', use the set of all nodes as basis for pagination.
Additionally, provide better feedback to client on noderange parsing issues.  Also
implement natural sort in various places after doing it for the pagination.
This commit is contained in:
Jarrod Johnson 2015-03-25 09:57:25 -04:00
parent ca4c1673a5
commit 1c6430bf3f
4 changed files with 52 additions and 15 deletions

View File

@ -262,7 +262,12 @@ def enumerate_nodegroup_collection(collectionpath, configmanager):
def enumerate_node_collection(collectionpath, configmanager):
if collectionpath == ['nodes']: # it is just '/node/', need to list nodes
return iterate_collections(configmanager.list_nodes())
allnodes = list(configmanager.list_nodes())
try:
allnodes.sort(key=noderange.humanify_nodename)
except TypeError:
allnodes.sort()
return iterate_collections(allnodes)
nodeorrange = collectionpath[1]
if collectionpath[0] == 'nodes' and not configmanager.is_node(nodeorrange):
raise exc.NotFoundException("Invalid element requested")
@ -368,8 +373,8 @@ def handle_node_request(configmanager, inputdata, operation,
if isnoderange:
try:
nodes = noderange.NodeRange(nodeorrange, configmanager).nodes
except Exception:
raise exc.NotFoundException("Invalid Noderange")
except Exception as e:
raise exc.NotFoundException("Invalid Noderange: " + str(e))
else:
nodes = (nodeorrange,)
except IndexError: # doesn't actually have a long enough path
@ -381,9 +386,19 @@ def handle_node_request(configmanager, inputdata, operation,
if operation == "create":
inputdata = msg.InputAttributes(pathcomponents, inputdata)
create_node(inputdata.attribs, configmanager)
return iterate_collections(configmanager.list_nodes())
allnodes = list(configmanager.list_nodes())
try:
allnodes.sort(key=noderange.humanify_nodename)
except TypeError:
allnodes.sort()
return iterate_collections(allnodes)
if isnoderange and len(pathcomponents) == 3 and pathcomponents[2] == 'nodes':
# this means that it's a list of relevant nodes
nodes = list(nodes)
try:
nodes.sort(key=noderange.humanify_nodename)
except TypeError:
nodes.sort()
return iterate_collections(nodes)
if len(pathcomponents) == 2:
iscollection = True

View File

@ -393,9 +393,9 @@ def resourcehandler_backend(env, start_response):
pagecontent += datum
start_response('200 OK', headers)
yield pagecontent
except exc.NotFoundException:
except exc.NotFoundException as ne:
start_response('404 Not found', headers)
yield "404 - Request path not recognized"
yield "404 - Request path not recognized - " + str(ne)
except exc.InvalidArgumentException as e:
start_response('400 Bad Request - ' + str(e), headers)
yield '400 - Bad Request - ' + str(e)

View File

@ -22,19 +22,30 @@
import itertools
import pyparsing as pp
import re
# construct custom grammar with pyparsing
_nodeword = pp.Word(pp.alphanums + '~^$/=-:.*+!')
_nodebracket = pp.QuotedString(quoteChar='[', endQuoteChar=']',
unquoteResults=False)
_nodeatom = pp.Group(pp.OneOrMore(_nodeword | _nodebracket))
_paginationstart = pp.Word('<', pp.nums)
_paginationend = pp.Word('>', pp.nums)
_paginationstart = pp.Group(pp.Word('<', pp.nums))
_paginationend = pp.Group(pp.Word('>', pp.nums))
_grammar = _nodeatom | ',-' | ',' | '@' | _paginationstart | _paginationend
_parser = pp.nestedExpr(content=_grammar)
_numextractor = pp.OneOrMore(pp.Word(pp.alphas + '-') | pp.Word(pp.nums))
numregex = re.compile('([0-9]+)')
def humanify_nodename(nodename):
"""Analyzes nodename in a human way to enable natural sort
:param nodename: The node name to analyze
:returns: A structure that can be consumed by 'sorted'
"""
return [int(text) if text.isdigit() else text.lower()
for text in re.split(numregex, nodename)]
# TODO: pagination operators <pp.nums and >pp.nums for begin and end respective
class NodeRange(object):
@ -48,15 +59,27 @@ class NodeRange(object):
self.beginpage = None
self.endpage = None
self.cfm = config
elements = _parser.parseString("(" + noderange + ")").asList()[0]
self._noderange = self._evaluate(elements)
try:
elements = _parser.parseString("(" + noderange + ")").asList()[0]
except pp.ParseException as pe:
raise Exception("Invalid syntax")
if noderange[0] in ('<', '>'):
# pagination across all nodes
self._evaluate(elements)
self._noderange = set(self.cfm.list_nodes())
else:
self._noderange = self._evaluate(elements)
@property
def nodes(self):
if self.beginpage is None and self.endpage is None:
return self._noderange
sortedlist = list(self._noderange)
sortedlist.sort()
try:
sortedlist.sort(key=humanify_nodename)
except TypeError:
# The natural sort attempt failed, fallback to ascii sort
sortedlist.sort()
if self.beginpage is not None:
sortedlist = sortedlist[self.beginpage:]
if self.endpage is not None:
@ -135,8 +158,6 @@ class NodeRange(object):
def expand_entity(self, entname):
if self.cfm is None or self.cfm.is_node(entname):
return set([entname])
if self.cfm.is_node(entname):
return set([entname])
if self.cfm.is_nodegroup(entname):
grpcfg = self.cfm.get_nodegroup_attributes(entname)
nodes = grpcfg['nodes']
@ -144,6 +165,7 @@ class NodeRange(object):
nodes |= NodeRange(
grpcfg['noderange']['value'], self.cfm).nodes
return nodes
raise Exception('Unknown node ' + entname)
def _expandstring(self, element, filternodes=None):
prefix = ''

View File

@ -214,9 +214,9 @@ def process_request(connection, request, cfm, authdata, authname, skipauth):
configmanager.ConfigManager.shutdown()
else:
hdlr = pluginapi.handle_path(path, operation, cfm, params)
except exc.NotFoundException:
except exc.NotFoundException as e:
tlvdata.send(connection, {"errorcode": 404,
"error": "Target not found"})
"error": "Target not found - " + str(e)})
tlvdata.send(connection, {"_requestdone": 1})
except exc.InvalidArgumentException as e:
tlvdata.send(connection, {"errorcode": 400,