Beispiel #1
0
def handle_path(path, operation, configmanager, inputdata=None, autostrip=True):
    """Given a full path request, return an object.

    The plugins should generally return some sort of iterator.
    An exception is made for console/session, which should return
    a class with connect(), read(), write(bytes), and close()
    """
    pathcomponents = path.split('/')
    del pathcomponents[0]  # discard the value from leading /
    if pathcomponents[-1] == '':
        del pathcomponents[-1]
    if not pathcomponents:  # root collection list
        return enumerate_collections(rootcollections)
    elif pathcomponents[0] == 'noderange':
        return handle_node_request(configmanager, inputdata, operation,
                                   pathcomponents, autostrip)
    elif pathcomponents[0] == 'nodegroups':
        return handle_nodegroup_request(configmanager, inputdata,
                                        pathcomponents,
                                        operation)
    elif pathcomponents[0] == 'nodes':
        # single node request of some sort
        return handle_node_request(configmanager, inputdata,
                                   operation, pathcomponents, autostrip)
    elif pathcomponents[0] == 'users':
        # TODO: when non-administrator accounts exist,
        # they must only be allowed to see their own user
        try:
            user = pathcomponents[1]
        except IndexError:  # it's just users/
            if operation == 'create':
                inputdata = msg.get_input_message(
                    pathcomponents, operation, inputdata)
                create_user(inputdata.attribs, configmanager)
            return iterate_collections(configmanager.list_users(),
                                       forcecollection=False)
        if user not in configmanager.list_users():
            raise exc.NotFoundException("Invalid user %s" % user)
        if operation == 'retrieve':
            return show_user(user, configmanager)
        elif operation == 'delete':
            return delete_user(user, configmanager)
        elif operation == 'update':
            inputdata = msg.get_input_message(
                pathcomponents, operation, inputdata)
            update_user(user, inputdata.attribs, configmanager)
            return show_user(user, configmanager)
    elif pathcomponents[0] == 'events':
        try:
            element = pathcomponents[1]
        except IndexError:
            if operation != 'retrieve':
                raise exc.InvalidArgumentException('Target is read-only')
            return (msg.ChildCollection('decode'),)
        if element != 'decode':
            raise exc.NotFoundException()
        if operation == 'update':
            return alerts.decode_alert(inputdata, configmanager)
    else:
        raise exc.NotFoundException()
Beispiel #2
0
def handle_dispatch(connection, cert, dispatch, peername):
    cert = crypto.dump_certificate(crypto.FILETYPE_ASN1, cert)
    if not util.cert_matches(
            cfm.get_collective_member(peername)['fingerprint'], cert):
        connection.close()
        return
    if dispatch[0:2] != b'\x01\x03':  # magic value to indicate msgpack
        # We only support msgpack now
        # The magic should preclude any pickle, as the first byte can never be
        # under 0x20 or so.
        connection.close()
        return
    dispatch = msgpack.unpackb(dispatch[2:], raw=False)
    configmanager = cfm.ConfigManager(dispatch['tenant'])
    nodes = dispatch['nodes']
    inputdata = dispatch['inputdata']
    operation = dispatch['operation']
    pathcomponents = dispatch['path']
    routespec = nested_lookup(noderesources, pathcomponents)
    inputdata = msg.get_input_message(pathcomponents, operation, inputdata,
                                      nodes, dispatch['isnoderange'],
                                      configmanager)
    plugroute = routespec.routeinfo
    plugpath = None
    nodesbyhandler = {}
    passvalues = []
    nodeattr = configmanager.get_node_attributes(nodes,
                                                 plugroute['pluginattrs'])
    for node in nodes:
        for attrname in plugroute['pluginattrs']:
            if attrname in nodeattr[node]:
                plugpath = nodeattr[node][attrname]['value']
            elif 'default' in plugroute:
                plugpath = plugroute['default']
        if plugpath:
            try:
                hfunc = getattr(pluginmap[plugpath], operation)
            except KeyError:
                nodesbyhandler[BadPlugin(node, plugpath).error] = [node]
                continue
            if hfunc in nodesbyhandler:
                nodesbyhandler[hfunc].append(node)
            else:
                nodesbyhandler[hfunc] = [node]
    try:
        for hfunc in nodesbyhandler:
            passvalues.append(
                hfunc(nodes=nodesbyhandler[hfunc],
                      element=pathcomponents,
                      configmanager=configmanager,
                      inputdata=inputdata))
        for res in itertools.chain(*passvalues):
            _forward_rsp(connection, res)
    except Exception as res:
        _forward_rsp(connection, res)
    connection.sendall('\x00\x00\x00\x00\x00\x00\x00\x00')
Beispiel #3
0
def handle_nodegroup_request(configmanager, inputdata,
                             pathcomponents, operation):
    iscollection = False
    routespec = None
    if len(pathcomponents) < 2:
        if operation == "create":
            inputdata = msg.InputAttributes(pathcomponents, inputdata)
            create_group(inputdata.attribs, configmanager)
        allgroups = list(configmanager.get_groups())
        try:
            allgroups.sort(key=noderange.humanify_nodename)
        except TypeError:
            allgroups.sort()
        return iterate_collections(allgroups)
    elif len(pathcomponents) == 2:
        iscollection = True
    else:
        try:
            routespec = nested_lookup(nodegroupresources, pathcomponents[2:])
            if isinstance(routespec, dict):
                iscollection = True
            elif isinstance(routespec, PluginCollection):
                iscollection = False  # it is a collection, but plugin defined
        except KeyError:
            raise exc.NotFoundException("Invalid element requested")
    if iscollection:
        if operation == "delete":
            return delete_nodegroup_collection(pathcomponents,
                                               configmanager)
        elif operation == "retrieve":
            return enumerate_nodegroup_collection(pathcomponents,
                                                  configmanager)
        else:
            raise Exception("TODO")
    plugroute = routespec.routeinfo
    inputdata = msg.get_input_message(
        pathcomponents[2:], operation, inputdata)
    if 'handler' in plugroute:  # fixed handler definition
        hfunc = getattr(pluginmap[plugroute['handler']], operation)
        return hfunc(
            nodes=None, element=pathcomponents,
            configmanager=configmanager,
            inputdata=inputdata)
    raise Exception("unknown case encountered")
Beispiel #4
0
def handle_nodegroup_request(configmanager, inputdata, pathcomponents,
                             operation):
    iscollection = False
    routespec = None
    if len(pathcomponents) < 2:
        if operation == "create":
            inputdata = msg.InputAttributes(pathcomponents, inputdata)
            return create_group(inputdata.attribs, configmanager)
        allgroups = list(configmanager.get_groups())
        try:
            allgroups.sort(key=noderange.humanify_nodename)
        except TypeError:
            allgroups.sort()
        return iterate_collections(allgroups)
    elif len(pathcomponents) == 2:
        iscollection = True
    else:
        try:
            routespec = nested_lookup(nodegroupresources, pathcomponents[2:])
            if isinstance(routespec, dict):
                iscollection = True
            elif isinstance(routespec, PluginCollection):
                iscollection = False  # it is a collection, but plugin defined
        except KeyError:
            raise exc.NotFoundException("Invalid element requested")
    if iscollection:
        if operation == "delete":
            return delete_nodegroup_collection(pathcomponents, configmanager)
        elif operation == "retrieve":
            return enumerate_nodegroup_collection(pathcomponents,
                                                  configmanager)
        else:
            raise Exception("TODO")
    plugroute = routespec.routeinfo
    inputdata = msg.get_input_message(pathcomponents[2:], operation, inputdata)
    if 'handler' in plugroute:  # fixed handler definition
        hfunc = getattr(pluginmap[plugroute['handler']], operation)
        return hfunc(nodes=None,
                     element=pathcomponents,
                     configmanager=configmanager,
                     inputdata=inputdata)
    raise Exception("unknown case encountered")
Beispiel #5
0
def handle_node_request(configmanager, inputdata, operation,
                        pathcomponents, autostrip=True):
    iscollection = False
    routespec = None
    if pathcomponents[0] == 'noderange':
        if len(pathcomponents) > 3 and pathcomponents[2] == 'nodes':
            # transform into a normal looking node request
            # this does mean we don't see if it is a valid
            # child, but that's not a goal for the noderange
            # facility anyway
            isnoderange = False
            pathcomponents = pathcomponents[2:]
        else:
            isnoderange = True
    else:
        isnoderange = False
    try:
        nodeorrange = pathcomponents[1]
        if not isnoderange and not configmanager.is_node(nodeorrange):
            raise exc.NotFoundException("Invalid Node")
        if isnoderange:
            try:
                nodes = noderange.NodeRange(nodeorrange, configmanager).nodes
            except Exception as e:
                raise exc.NotFoundException("Invalid Noderange: " + str(e))
        else:
            nodes = (nodeorrange,)
    except IndexError:  # doesn't actually have a long enough path
        # this is enumerating a list of nodes or just empty noderange
        if isnoderange and operation == "retrieve":
            return iterate_collections([])
        elif isnoderange or operation == "delete":
            raise exc.InvalidArgumentException()
        if operation == "create":
            inputdata = msg.InputAttributes(pathcomponents, inputdata)
            create_node(inputdata.attribs, configmanager)
        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
    else:
        try:
            routespec = nested_lookup(noderesources, pathcomponents[2:])
        except KeyError:
            raise exc.NotFoundException("Invalid element requested")
        if isinstance(routespec, dict):
            iscollection = True
        elif isinstance(routespec, PluginCollection):
            iscollection = False  # it is a collection, but plugin defined
        elif routespec is None:
            raise exc.InvalidArgumentException('Custom interface required for resource')
    if iscollection:
        if operation == "delete":
            return delete_node_collection(pathcomponents, configmanager)
        elif operation == "retrieve":
            return enumerate_node_collection(pathcomponents, configmanager)
        else:
            raise Exception("TODO here")
    del pathcomponents[0:2]
    passvalues = []
    plugroute = routespec.routeinfo
    inputdata = msg.get_input_message(
        pathcomponents, operation, inputdata, nodes, isnoderange)
    if 'handler' in plugroute:  # fixed handler definition, easy enough
        if isinstance(plugroute['handler'], str):
            hfunc = getattr(pluginmap[plugroute['handler']], operation)
        else:
            hfunc = getattr(plugroute['handler'], operation)
        passvalue = hfunc(
            nodes=nodes, element=pathcomponents,
            configmanager=configmanager,
            inputdata=inputdata)
        if isnoderange:
            return passvalue
        elif isinstance(passvalue, console.Console):
            return passvalue
        else:
            return stripnode(passvalue, nodes[0])
    elif 'pluginattrs' in plugroute:
        nodeattr = configmanager.get_node_attributes(
            nodes, plugroute['pluginattrs'])
        plugpath = None
        if 'default' in plugroute:
            plugpath = plugroute['default']
        nodesbyhandler = {}
        for node in nodes:
            for attrname in plugroute['pluginattrs']:
                if attrname in nodeattr[node]:
                    plugpath = nodeattr[node][attrname]['value']
            if plugpath is not None:
                hfunc = getattr(pluginmap[plugpath], operation)
                if hfunc in nodesbyhandler:
                    nodesbyhandler[hfunc].append(node)
                else:
                    nodesbyhandler[hfunc] = [node]
        for hfunc in nodesbyhandler:
            passvalues.append(hfunc(
                nodes=nodesbyhandler[hfunc], element=pathcomponents,
                configmanager=configmanager,
                inputdata=inputdata))
        if isnoderange or not autostrip:
            return itertools.chain(*passvalues)
        else:
            if len(passvalues) > 0:
                if isinstance(passvalues[0], console.Console):
                    return passvalues[0]
                else:
                    return stripnode(passvalues[0], nodes[0])
            else:
                raise exc.NotImplementedException()
Beispiel #6
0
def handle_node_request(configmanager,
                        inputdata,
                        operation,
                        pathcomponents,
                        autostrip=True):
    if log.logfull:
        raise exc.TargetResourceUnavailable(
            'Filesystem full, free up space and restart confluent service')
    iscollection = False
    routespec = None
    if pathcomponents[0] == 'noderange':
        if len(pathcomponents) > 3 and pathcomponents[2] == 'nodes':
            # transform into a normal looking node request
            # this does mean we don't see if it is a valid
            # child, but that's not a goal for the noderange
            # facility anyway
            isnoderange = False
            pathcomponents = pathcomponents[2:]
        elif len(pathcomponents) == 3 and pathcomponents[2] == 'abbreviate':
            return abbreviate_noderange(configmanager, inputdata, operation)
        else:
            isnoderange = True
    else:
        isnoderange = False
    try:
        nodeorrange = pathcomponents[1]
        if not isnoderange and not configmanager.is_node(nodeorrange):
            raise exc.NotFoundException("Invalid Node")
        if isnoderange and not (len(pathcomponents) == 3
                                and pathcomponents[2] == 'abbreviate'):
            try:
                nodes = noderange.NodeRange(nodeorrange, configmanager).nodes
            except Exception as e:
                raise exc.NotFoundException("Invalid Noderange: " + str(e))
        else:
            nodes = (nodeorrange, )
    except IndexError:  # doesn't actually have a long enough path
        # this is enumerating a list of nodes or just empty noderange
        if isnoderange and operation == "retrieve":
            return iterate_collections([])
        elif isnoderange and operation == "create":
            inputdata = msg.InputAttributes(pathcomponents, inputdata)
            return create_noderange(inputdata.attribs, configmanager)
        elif isnoderange or operation == "delete":
            raise exc.InvalidArgumentException()
        if operation == "create":
            inputdata = msg.InputAttributes(pathcomponents, inputdata)
            return create_node(inputdata.attribs, configmanager)
        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
    else:
        try:
            routespec = nested_lookup(noderesources, pathcomponents[2:])
        except KeyError:
            raise exc.NotFoundException("Invalid element requested")
        if isinstance(routespec, dict):
            iscollection = True
        elif isinstance(routespec, PluginCollection):
            iscollection = False  # it is a collection, but plugin defined
        elif routespec is None:
            raise exc.InvalidArgumentException(
                'Custom interface required for resource')
    if iscollection:
        if operation == "delete":
            return delete_node_collection(pathcomponents, configmanager,
                                          isnoderange)
        elif operation == "retrieve":
            return enumerate_node_collection(pathcomponents, configmanager)
        else:
            raise Exception("TODO here")
    del pathcomponents[0:2]
    passvalues = queue.Queue()
    plugroute = routespec.routeinfo
    msginputdata = msg.get_input_message(pathcomponents, operation, inputdata,
                                         nodes, isnoderange, configmanager)
    if 'handler' in plugroute:  # fixed handler definition, easy enough
        if isinstance(plugroute['handler'], str):
            hfunc = getattr(pluginmap[plugroute['handler']], operation)
        else:
            hfunc = getattr(plugroute['handler'], operation)
        passvalue = hfunc(nodes=nodes,
                          element=pathcomponents,
                          configmanager=configmanager,
                          inputdata=msginputdata)
        if isnoderange:
            return passvalue
        elif isinstance(passvalue, console.Console):
            return [passvalue]
        else:
            return stripnode(passvalue, nodes[0])
    elif 'pluginattrs' in plugroute:
        nodeattr = configmanager.get_node_attributes(
            nodes, plugroute['pluginattrs'] + ['collective.manager'])
        plugpath = None
        nodesbymanager = {}
        nodesbyhandler = {}
        badcollnodes = []
        for node in nodes:
            for attrname in plugroute['pluginattrs']:
                if attrname in nodeattr[node]:
                    plugpath = nodeattr[node][attrname]['value']
                elif 'default' in plugroute:
                    plugpath = plugroute['default']
            if plugpath in dispatch_plugins:
                cfm.check_quorum()
                manager = nodeattr[node].get('collective.manager',
                                             {}).get('value', None)
                if manager:
                    if collective.get_myname() != manager:
                        if manager not in nodesbymanager:
                            nodesbymanager[manager] = set([node])
                        else:
                            nodesbymanager[manager].add(node)
                        continue
                elif list(cfm.list_collective()):
                    badcollnodes.append(node)
                    continue
            if plugpath:
                try:
                    hfunc = getattr(pluginmap[plugpath], operation)
                except KeyError:
                    nodesbyhandler[BadPlugin(node, plugpath).error] = [node]
                    continue
                if hfunc in nodesbyhandler:
                    nodesbyhandler[hfunc].append(node)
                else:
                    nodesbyhandler[hfunc] = [node]
        for bn in badcollnodes:
            nodesbyhandler[BadCollective(bn).error] = [bn]
        workers = greenpool.GreenPool()
        numworkers = 0
        for hfunc in nodesbyhandler:
            numworkers += 1
            workers.spawn(
                addtoqueue, passvalues, hfunc, {
                    'nodes': nodesbyhandler[hfunc],
                    'element': pathcomponents,
                    'configmanager': configmanager,
                    'inputdata': msginputdata
                })
        for manager in nodesbymanager:
            numworkers += 1
            workers.spawn(
                addtoqueue, passvalues, dispatch_request, {
                    'nodes': nodesbymanager[manager],
                    'manager': manager,
                    'element': pathcomponents,
                    'configmanager': configmanager,
                    'inputdata': inputdata,
                    'operation': operation,
                    'isnoderange': isnoderange
                })
        if isnoderange or not autostrip:
            return iterate_queue(numworkers, passvalues)
        else:
            if numworkers > 0:
                return iterate_queue(numworkers, passvalues, nodes[0])
            else:
                raise exc.NotImplementedException()
Beispiel #7
0
def handle_path(path,
                operation,
                configmanager,
                inputdata=None,
                autostrip=True):
    """Given a full path request, return an object.

    The plugins should generally return some sort of iterator.
    An exception is made for console/session, which should return
    a class with connect(), read(), write(bytes), and close()
    """
    pathcomponents = path.split('/')
    del pathcomponents[0]  # discard the value from leading /
    if pathcomponents[-1] == '':
        del pathcomponents[-1]
    if not pathcomponents:  # root collection list
        return enumerate_collections(rootcollections)
    elif pathcomponents[0] == 'noderange':
        return handle_node_request(configmanager, inputdata, operation,
                                   pathcomponents, autostrip)
    elif pathcomponents[0] == 'deployment':
        return handle_deployment(configmanager, inputdata, pathcomponents,
                                 operation)
    elif pathcomponents[0] == 'nodegroups':
        return handle_nodegroup_request(configmanager, inputdata,
                                        pathcomponents, operation)
    elif pathcomponents[0] == 'nodes':
        # single node request of some sort
        return handle_node_request(configmanager, inputdata, operation,
                                   pathcomponents, autostrip)
    elif pathcomponents[0] == 'discovery':
        return disco.handle_api_request(configmanager, inputdata, operation,
                                        pathcomponents)
    elif pathcomponents[0] == 'networking':
        return macmap.handle_api_request(configmanager, inputdata, operation,
                                         pathcomponents)
    elif pathcomponents[0] == 'version':
        return (msg.Attributes(kv={'version': confluent.__version__}), )
    elif pathcomponents[0] == 'usergroups':
        # TODO: when non-administrator accounts exist,
        # they must only be allowed to see their own user
        try:
            usergroup = pathcomponents[1]
        except IndexError:  # it's just users/
            if operation == 'create':
                inputdata = msg.get_input_message(pathcomponents,
                                                  operation,
                                                  inputdata,
                                                  configmanager=configmanager)
                create_usergroup(inputdata.attribs, configmanager)
            return iterate_collections(configmanager.list_usergroups(),
                                       forcecollection=False)
        if usergroup not in configmanager.list_usergroups():
            raise exc.NotFoundException("Invalid usergroup %s" % usergroup)
        if operation == 'retrieve':
            return show_usergroup(usergroup, configmanager)
        elif operation == 'delete':
            return delete_usergroup(usergroup, configmanager)
        elif operation == 'update':
            inputdata = msg.get_input_message(pathcomponents,
                                              operation,
                                              inputdata,
                                              configmanager=configmanager)
            update_usergroup(usergroup, inputdata.attribs, configmanager)
            return show_usergroup(usergroup, configmanager)
    elif pathcomponents[0] == 'users':
        # TODO: when non-administrator accounts exist,
        # they must only be allowed to see their own user
        try:
            user = pathcomponents[1]
        except IndexError:  # it's just users/
            if operation == 'create':
                inputdata = msg.get_input_message(pathcomponents,
                                                  operation,
                                                  inputdata,
                                                  configmanager=configmanager)
                create_user(inputdata.attribs, configmanager)
            return iterate_collections(configmanager.list_users(),
                                       forcecollection=False)
        if user not in configmanager.list_users():
            raise exc.NotFoundException("Invalid user %s" % user)
        if operation == 'retrieve':
            return show_user(user, configmanager)
        elif operation == 'delete':
            return delete_user(user, configmanager)
        elif operation == 'update':
            inputdata = msg.get_input_message(pathcomponents,
                                              operation,
                                              inputdata,
                                              configmanager=configmanager)
            update_user(user, inputdata.attribs, configmanager)
            return show_user(user, configmanager)
    elif pathcomponents[0] == 'events':
        try:
            element = pathcomponents[1]
        except IndexError:
            if operation != 'retrieve':
                raise exc.InvalidArgumentException('Target is read-only')
            return (msg.ChildCollection('decode'), )
        if element != 'decode':
            raise exc.NotFoundException()
        if operation == 'update':
            return alerts.decode_alert(inputdata, configmanager)
    elif pathcomponents[0] == 'discovery':
        return handle_discovery(pathcomponents[1:], operation, configmanager,
                                inputdata)
    else:
        raise exc.NotFoundException()
Beispiel #8
0
def handle_node_request(configmanager,
                        inputdata,
                        operation,
                        pathcomponents,
                        autostrip=True):
    iscollection = False
    routespec = None
    if pathcomponents[0] == 'noderange':
        if len(pathcomponents) > 3 and pathcomponents[2] == 'nodes':
            # transform into a normal looking node request
            # this does mean we don't see if it is a valid
            # child, but that's not a goal for the noderange
            # facility anyway
            isnoderange = False
            pathcomponents = pathcomponents[2:]
        elif len(pathcomponents) == 3 and pathcomponents[2] == 'abbreviate':
            return abbreviate_noderange(configmanager, inputdata, operation)
        else:
            isnoderange = True
    else:
        isnoderange = False
    try:
        nodeorrange = pathcomponents[1]
        if not isnoderange and not configmanager.is_node(nodeorrange):
            raise exc.NotFoundException("Invalid Node")
        if isnoderange and not (len(pathcomponents) == 3
                                and pathcomponents[2] == 'abbreviate'):
            try:
                nodes = noderange.NodeRange(nodeorrange, configmanager).nodes
            except Exception as e:
                raise exc.NotFoundException("Invalid Noderange: " + str(e))
        else:
            nodes = (nodeorrange, )
    except IndexError:  # doesn't actually have a long enough path
        # this is enumerating a list of nodes or just empty noderange
        if isnoderange and operation == "retrieve":
            return iterate_collections([])
        elif isnoderange and operation == "create":
            inputdata = msg.InputAttributes(pathcomponents, inputdata)
            return create_noderange(inputdata.attribs, configmanager)
        elif isnoderange or operation == "delete":
            raise exc.InvalidArgumentException()
        if operation == "create":
            inputdata = msg.InputAttributes(pathcomponents, inputdata)
            return create_node(inputdata.attribs, configmanager)
        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
    else:
        try:
            routespec = nested_lookup(noderesources, pathcomponents[2:])
        except KeyError:
            raise exc.NotFoundException("Invalid element requested")
        if isinstance(routespec, dict):
            iscollection = True
        elif isinstance(routespec, PluginCollection):
            iscollection = False  # it is a collection, but plugin defined
        elif routespec is None:
            raise exc.InvalidArgumentException(
                'Custom interface required for resource')
    if iscollection:
        if operation == "delete":
            return delete_node_collection(pathcomponents, configmanager,
                                          isnoderange)
        elif operation == "retrieve":
            return enumerate_node_collection(pathcomponents, configmanager)
        else:
            raise Exception("TODO here")
    del pathcomponents[0:2]
    passvalues = []
    plugroute = routespec.routeinfo
    inputdata = msg.get_input_message(pathcomponents, operation, inputdata,
                                      nodes, isnoderange, configmanager)
    if 'handler' in plugroute:  # fixed handler definition, easy enough
        if isinstance(plugroute['handler'], str):
            hfunc = getattr(pluginmap[plugroute['handler']], operation)
        else:
            hfunc = getattr(plugroute['handler'], operation)
        passvalue = hfunc(nodes=nodes,
                          element=pathcomponents,
                          configmanager=configmanager,
                          inputdata=inputdata)
        if isnoderange:
            return passvalue
        elif isinstance(passvalue, console.Console):
            return passvalue
        else:
            return stripnode(passvalue, nodes[0])
    elif 'pluginattrs' in plugroute:
        nodeattr = configmanager.get_node_attributes(nodes,
                                                     plugroute['pluginattrs'])
        plugpath = None
        if 'default' in plugroute:
            plugpath = plugroute['default']
        nodesbyhandler = {}
        for node in nodes:
            for attrname in plugroute['pluginattrs']:
                if attrname in nodeattr[node]:
                    plugpath = nodeattr[node][attrname]['value']
            if plugpath is not None:
                try:
                    hfunc = getattr(pluginmap[plugpath], operation)
                except KeyError:
                    nodesbyhandler[BadPlugin(node, plugpath).error] = [node]
                    continue
                if hfunc in nodesbyhandler:
                    nodesbyhandler[hfunc].append(node)
                else:
                    nodesbyhandler[hfunc] = [node]
        for hfunc in nodesbyhandler:
            passvalues.append(
                hfunc(nodes=nodesbyhandler[hfunc],
                      element=pathcomponents,
                      configmanager=configmanager,
                      inputdata=inputdata))
        if isnoderange or not autostrip:
            return itertools.chain(*passvalues)
        else:
            if len(passvalues) > 0:
                if isinstance(passvalues[0], console.Console):
                    return passvalues[0]
                else:
                    return stripnode(passvalues[0], nodes[0])
            else:
                raise exc.NotImplementedException()