示例#1
0
文件: api.py 项目: henn/hil
def headnode_connect_network(headnode, hnic, network):
    """Connect a headnode's hnic to a network.

    Raises IllegalStateError if the headnode has already been started.

    Raises ProjectMismatchError if the project does not have access rights to
    the given network.

    Raises BadArgumentError if the network is a non-allocated network. This
    is currently unsupported due to an implementation limitation, but will be
    supported in a future release. See issue #333.
    """
    headnode = _must_find(model.Headnode, headnode)
    get_auth_backend().require_project_access(headnode.project)
    hnic = _must_find_n(headnode, model.Hnic, hnic)
    network = _must_find(model.Network, network)

    if not network.allocated:
        raise BadArgumentError("Headnodes may only be connected to networks "
                               "allocated by the project.")

    if not headnode.dirty:
        raise IllegalStateError

    project = headnode.project

    if (network.access is not None) and (network.access is not project):
        raise ProjectMismatchError("Project does not have access to given network.")

    hnic.network = network
    db.session.commit()
示例#2
0
文件: api.py 项目: henn/hil
def project_delete(project):
    """Delete project.

    If the project does not exist, a NotFoundError will be raised.
    """
    get_auth_backend().require_admin()
    project = _must_find(model.Project, project)
    if project.nodes:
        raise BlockedError("Project has nodes still")
    if project.networks_created:
        raise BlockedError("Project still has networks")
    if project.networks_access:
        ### FIXME: This is not the user's fault, and they cannot fix it.  The
        ### only reason we need to error here is that, with how network access
        ### is done, the following bad thing happens.  If there's a network
        ### that only the project can access, its "access" field will be the
        ### project.  When you then delete that project, "access" will be set
        ### to None instead.  Counter-intuitively, this then makes that
        ### network accessible to ALL PROJECTS!  Once we use real ACLs, this
        ### will not be an issue---instead, the network will be accessible by
        ### NO projects.
        raise BlockedError("Project can still access networks")
    if project.headnodes:
        raise BlockedError("Project still has a headnode")
    db.session.delete(project)
    db.session.commit()
示例#3
0
文件: api.py 项目: henn/hil
def show_headnode(nodename):
    """Show details of a headnode.

    Returns a JSON object representing a headnode.
    The obect will have at least the following fields:
        * "name", the name/label of the headnode (string).
        * "project", the project to which the headnode belongs.
        * "hnics", a JSON array of hnic labels that are attached to this
            headnode.
        * "vncport", the vnc port that the headnode VM is listening on; this
            value can be None if the VM is powered off or has not been
            created yet.

    Example:  '{"name": "headnode1",
                "project": "project1",
                "hnics": ["hnic1", "hnic2"],
                "vncport": 5900
               }'
    """
    headnode = _must_find(model.Headnode, nodename)
    get_auth_backend().require_project_access(headnode.project)
    return json.dumps({
        'name': headnode.label,
        'project': headnode.project.label,
        'hnics': [n.label for n in headnode.hnics],
        'vncport': headnode.get_vncport(),
        'uuid' : headnode.uuid,
        'base_img': headnode.base_img,
    }, sort_keys = True)
示例#4
0
文件: api.py 项目: henn/hil
def project_detach_node(project, node):
    """Remove a node from a project.

    If the node or project does not exist, a NotFoundError will be raised.

    If the node has network attachments or pending network actions, a
    BlockedError will be raised.
    """
    project = _must_find(model.Project, project)
    get_auth_backend().require_project_access(project)
    node = _must_find(model.Node, node)
    if node not in project.nodes:
        raise NotFoundError("Node not in project")
    num_attachments = model.NetworkAttachment.query \
        .filter(model.Nic.owner == node,
                model.NetworkAttachment.nic_id == model.Nic.id).count()
    if num_attachments != 0:
        raise BlockedError("Node attached to a network")
    for nic in node.nics:
        if nic.current_action is not None:
            raise BlockedError("Node has pending network actions")
    node.obm.stop_console()
    node.obm.delete_console()
    project.nodes.remove(node)
    db.session.commit()
示例#5
0
文件: api.py 项目: henn/hil
def headnode_create(headnode, project, base_img):
    """Create headnode.

    If a headnode with the same name already exists, a DuplicateError will be
    raised.

    If the project does not exist, a NotFoundError will be raised.

    If the base image does not exist (not specified in haas.cfg) a
    BadArgumentError will be raised.
    """

    valid_imgs = cfg.get('headnode', 'base_imgs')
    valid_imgs = [img.strip() for img in valid_imgs.split(',')]

    if base_img not in valid_imgs:
        raise BadArgumentError('Provided image is not a valid image.')

    _assert_absent(model.Headnode, headnode)
    project = _must_find(model.Project, project)
    get_auth_backend().require_project_access(project)

    headnode = model.Headnode(project, headnode, base_img)

    db.session.add(headnode)
    db.session.commit()
示例#6
0
文件: api.py 项目: henn/hil
def node_delete_nic(node, nic):
    """Delete nic with given name from it's node.

    If the node or nic does not exist, a NotFoundError will be raised.
    """
    get_auth_backend().require_admin()
    nic = _must_find_n(_must_find(model.Node, node), model.Nic, nic)
    db.session.delete(nic)
    db.session.commit()
示例#7
0
文件: api.py 项目: henn/hil
def project_create(project):
    """Create a project.

    If the project already exists, a DuplicateError will be raised.
    """
    get_auth_backend().require_admin()
    _assert_absent(model.Project, project)
    project = model.Project(project)
    db.session.add(project)
    db.session.commit()
示例#8
0
文件: api.py 项目: henn/hil
def switch_delete(switch):
    get_auth_backend().require_admin()
    switch = _must_find(model.Switch, switch)

    if switch.ports != []:
        raise BlockedError("Switch %r has ports; delete them first." %
                           switch.label)

    db.session.delete(switch)
    db.session.commit()
示例#9
0
文件: api.py 项目: henn/hil
def headnode_stop(headnode):
    """Stop the headnode.

    This powers off the headnode. This is a hard poweroff; the VM is not given
    the opportunity to shut down cleanly. This does *not* unfreeze the VM;
    headnode_start will be the only valid API call after the VM is powered off.
    """
    headnode = _must_find(model.Headnode, headnode)
    get_auth_backend().require_project_access(headnode.project)
    headnode.stop()
示例#10
0
文件: api.py 项目: henn/hil
def list_projects():
    """List all projects.

    Returns a JSON array of strings representing a list of projects.

    Example:  '["project1", "project2", "project3"]'
    """
    get_auth_backend().require_admin()
    projects = model.Project.query.all()
    projects = sorted([p.label for p in projects])
    return json.dumps(projects)
示例#11
0
文件: api.py 项目: henn/hil
def list_switches():
    """List all switches.

    Returns a JSON array of strings representing a list of switches.

    Example:  '["cisco3", "brocade1", "mock2"]'
    """
    get_auth_backend().require_admin()
    switches = model.Switch.query.all()
    snames = sorted([s.label for s in switches])
    return json.dumps(snames)
示例#12
0
文件: api.py 项目: henn/hil
def list_project_nodes(project):
    """List all nodes belonging the given project.

    Returns a JSON array of strings representing a list of nodes.

    Example:  '["node1", "node2", "node3"]'
    """
    project = _must_find(model.Project, project)
    get_auth_backend().require_project_access(project)
    nodes = project.nodes
    nodes = [n.label for n in nodes]
    return json.dumps(nodes)
示例#13
0
文件: api.py 项目: henn/hil
def list_project_networks(project):
    """List all private networks the project can access.

    Returns a JSON array of strings representing a list of networks.

    Example:  '["net1", "net2", "net3"]'
    """
    project = _must_find(model.Project, project)
    get_auth_backend().require_project_access(project)
    networks = project.networks_access
    networks = sorted([n.label for n in networks])
    return json.dumps(networks)
示例#14
0
文件: api.py 项目: henn/hil
def list_project_headnodes(project):
    """List all headnodes belonging the given project.

    Returns a JSON array of strings representing a list of headnodes.

    Example:  '["headnode1", "headnode2", "headnode3"]'
    """
    project = _must_find(model.Project, project)
    get_auth_backend().require_project_access(project)
    headnodes = project.headnodes
    headnodes = sorted([hn.label for hn in headnodes])
    return json.dumps(headnodes)
示例#15
0
文件: database.py 项目: henn/hil
def user_add_project(user, project):
    """Add a user to a project.

    If the project or user does not exist, a NotFoundError will be raised.
    """
    get_auth_backend().require_admin()
    user = api._must_find(User, user)
    project = api._must_find(model.Project, project)
    if project in user.projects:
        raise DuplicateError("User %s is already in project %s" % (user.label, project.label))
    user.projects.append(project)
    db.session.commit()
示例#16
0
文件: database.py 项目: henn/hil
def user_remove_project(user, project):
    """Remove a user from a project.

    If the project or user does not exist, a NotFoundError will be raised.
    """
    get_auth_backend().require_admin()
    user = api._must_find(User, user)
    project = api._must_find(model.Project, project)
    if project not in user.projects:
        raise NotFoundError("User %s is not in project %s" % (user.label, project.label))
    user.projects.remove(project)
    db.session.commit()
示例#17
0
文件: api.py 项目: henn/hil
def switch_register_port(switch, port):
    """Register a port on a switch.

    If the port already exists, a DuplicateError will be raised.
    """
    get_auth_backend().require_admin()
    switch = _must_find(model.Switch, switch)
    _assert_absent_n(switch, model.Port, port)
    port = model.Port(port, switch)

    db.session.add(port)
    db.session.commit()
示例#18
0
文件: api.py 项目: henn/hil
def headnode_delete(headnode):
    """Delete headnode.

    If the node does not exist, a NotFoundError will be raised.
    """
    headnode = _must_find(model.Headnode, headnode)
    get_auth_backend().require_project_access(headnode.project)
    if not headnode.dirty:
        headnode.delete()
    for hnic in headnode.hnics:
        db.session.delete(hnic)
    db.session.delete(headnode)
    db.session.commit()
示例#19
0
文件: api.py 项目: henn/hil
def node_register_nic(node, nic, macaddr):
    """Register existence of nic attached to given node.

    If the node does not exist, a NotFoundError will be raised.

    If there is already an nic with that name, a DuplicateError will be raised.
    """
    get_auth_backend().require_admin()
    node = _must_find(model.Node, node)
    _assert_absent_n(node, model.Nic, nic)
    nic = model.Nic(node, nic, macaddr)
    db.session.add(nic)
    db.session.commit()
示例#20
0
文件: database.py 项目: gsilvis/haas
def user_delete(user):
    """Delete user.

    If the user does not exist, a NotFoundError will be raised.
    """
    get_auth_backend().require_admin()

    # XXX: We need to do a bit of refactoring, so this is available outside of
    # haas.api:
    user = api._must_find(User, user)

    db.session.delete(user)
    db.session.commit()
示例#21
0
文件: database.py 项目: gsilvis/haas
def user_create(user, password, is_admin=False):
    """Create user with given password.

    If the user already exists, a DuplicateError will be raised.
    """
    get_auth_backend().require_admin()

    # XXX: We need to do a bit of refactoring, so this is available outside of
    # haas.api:
    api._assert_absent(User, user)

    user = User(user, password, is_admin=is_admin)
    db.session.add(user)
    db.session.commit()
示例#22
0
文件: api.py 项目: henn/hil
def node_delete(node):
    """Delete node.

    If the node does not exist, a NotFoundError will be raised.
    """
    get_auth_backend().require_admin()
    node = _must_find(model.Node, node)
    if node.nics != []:
        raise BlockedError("Node %r has nics; remove them before deleting %r."
                           % (node.label, node.label))
    node.obm.stop_console()
    node.obm.delete_console()
    db.session.delete(node)
    db.session.commit()
示例#23
0
文件: api.py 项目: henn/hil
def project_connect_node(project, node):
    """Add a node to a project.

    If the node or project does not exist, a NotFoundError will be raised.

    If node is already owned by a project, a BlockedError will be raised.
    """
    project = _must_find(model.Project, project)
    get_auth_backend().require_project_access(project)
    node = _must_find(model.Node, node)
    if node.project is not None:
        raise BlockedError("Node is already owned by a project.")
    project.nodes.append(node)
    db.session.commit()
示例#24
0
文件: api.py 项目: henn/hil
def headnode_start(headnode):
    """Start the headnode.

    This actually boots up the headnode virtual machine. The VM is created
    within libvirt if needed. Once the VM has been started once, it is
    "frozen," and all other headnode-related api calls will fail (by raising
    an IllegalStateError), with the exception of headnode_stop.
    """
    headnode = _must_find(model.Headnode, headnode)
    get_auth_backend().require_project_access(headnode.project)
    if headnode.dirty:
        headnode.create()
    headnode.start()
    db.session.commit()
示例#25
0
文件: api.py 项目: henn/hil
def headnode_detach_network(headnode, hnic):
    """Detach a heanode's nic from any network it's on.

    Raises IllegalStateError if the headnode has already been started.
    """
    headnode = _must_find(model.Headnode, headnode)
    get_auth_backend().require_project_access(headnode.project)
    hnic = _must_find_n(headnode, model.Hnic, hnic)

    if not headnode.dirty:
        raise IllegalStateError

    hnic.network = None
    db.session.commit()
示例#26
0
文件: api.py 项目: henn/hil
def switch_delete_port(switch, port):
    """Delete a port on a switch.

    If the port does not exist, a NotFoundError will be raised.
    """
    get_auth_backend().require_admin()
    switch = _must_find(model.Switch, switch)
    port = _must_find_n(switch, model.Port, port)
    if port.nic is not None:
        raise BlockedError("Port %r is attached to a nic; please detach "
                           "it first." % port.label)

    db.session.delete(port)
    db.session.commit()
示例#27
0
文件: api.py 项目: henn/hil
def switch_register(switch, type, **kwargs):
    get_auth_backend().require_admin()
    _assert_absent(model.Switch, switch)

    cls = concrete_class_for(model.Switch, type)
    if cls is None:
        raise BadArgumentError('%r is not a valid switch type.' % type)
    cls.validate(kwargs)
    obj = cls(**kwargs)
    obj.label = switch
    obj.type = type

    db.session.add(obj)
    db.session.commit()
示例#28
0
文件: api.py 项目: henn/hil
def show_network(network):
    """Show details of a network.

    Returns a JSON object representing a network. See `docs/rest_api.md`
    for a full description of the output.
    """
    allocator = get_network_allocator()
    auth_backend = get_auth_backend()

    network = _must_find(model.Network, network)

    if network.access is not None:
        auth_backend.require_project_access(network.access)

    result = {
        'name': network.label,
        'channels': allocator.legal_channels_for(network.network_id),
    }
    if network.creator is None:
        result['creator'] = 'admin'
    else:
        result['creator'] = network.creator.label

    if network.access is not None:
        result['access'] = network.access.label

    return json.dumps(result, sort_keys=True)
示例#29
0
文件: api.py 项目: henn/hil
def network_delete(network):
    """Delete network.

    If the network does not exist, a NotFoundError will be raised.

    If the network is connected to nodes or headnodes, or there are pending
    network actions involving it, a BlockedError will be raised.
    """
    auth_backend = get_auth_backend()

    network = _must_find(model.Network, network)

    if network.creator is None:
        auth_backend.require_admin()
    else:
        auth_backend.require_project_access(network.creator)

    if len(network.attachments) != 0:
        raise BlockedError("Network still connected to nodes")
    if network.hnics:
        raise BlockedError("Network still connected to headnodes")
    if len(network.scheduled_nics) != 0:
        raise BlockedError("There are pending actions on this network")
    if network.allocated:
        get_network_allocator().free_network_id(network.network_id)

    db.session.delete(network)
    db.session.commit()
示例#30
0
文件: api.py 项目: henn/hil
def node_detach_network(node, nic, network):
    """Detach network ``network`` from physical nic ``nic``.

    Raises ProjectMismatchError if the node is not in a project.

    Raises BlockedError if there is already a pending network action.

    Raises BadArgumentError if the network is not attached to the nic.
    """
    auth_backend = get_auth_backend()

    node = _must_find(model.Node, node)
    network = _must_find(model.Network, network)
    nic = _must_find_n(node, model.Nic, nic)

    if not node.project:
        raise ProjectMismatchError("Node not in project")
    auth_backend.require_project_access(node.project)

    if nic.current_action:
        raise BlockedError("A networking operation is already active on the nic.")
    attachment = model.NetworkAttachment.query \
        .filter_by(nic=nic, network=network).first()
    if attachment is None:
        raise BadArgumentError("The network is not attached to the nic.")
    db.session.add(model.NetworkingAction(nic=nic,
                                          channel=attachment.channel,
                                          new_network=None))
    db.session.commit()
    return '', 202
示例#31
0
def node_set_bootdev(node, bootdev):
    auth_backend = get_auth_backend()
    node = _must_find(model.Node, node)
    if node.project is None:
        auth_backend.require_admin()
    else:
        auth_backend.require_project_access(node.project)

    node.obm.require_legal_bootdev(bootdev)

    node.obm.set_bootdev(bootdev)
示例#32
0
def show_switch(switch):
    """Show details of a switch.

    Returns a JSON object regrading the switch. See `docs/rest_api.md`
    for a full description of the output.

    FIXME: Ideally this api call should return all detail information about
    this switch. right now it needs support from the switch backend.
    """
    get_auth_backend().require_admin()
    switch = _must_find(model.Switch, switch)

    return json.dumps(
        {
            'name': switch.label,
            'ports': [{
                'label': port.label
            } for port in switch.ports],
        },
        sort_keys=True)
示例#33
0
def node_register(node, **kwargs):
    """Create node.

    If the node already exists, a DuplicateError will be raised.
    The node is initially registered with no nics; see the method
    node_register_nic.
    """
    get_auth_backend().require_admin()
    _assert_absent(model.Node, node)
    obm_type = kwargs['obm']['type']
    cls = concrete_class_for(model.Obm, obm_type)
    if cls is None:
        raise BadArgumentError('%r is not a valid OBM type.' % obm_type)
    cls.validate(kwargs['obm'])
    node_obj = model.Node(label=node, obm=cls(**kwargs['obm']))
    if 'metadata' in kwargs:
        for label, value in kwargs['metadata'].items():
            metadata_obj = model.Metadata(label, json.dumps(value), node_obj)
            db.session.add(metadata_obj)
    db.session.add(node_obj)
    db.session.commit()
示例#34
0
def headnode_create_hnic(headnode, hnic):
    """Create hnic attached to given headnode.

    If the node does not exist, a NotFoundError will be raised.

    If there is already an hnic with that name, a DuplicateError will
    be raised.

    If the headnode's VM has already created (headnode is not "dirty"), raises
    an IllegalStateError
    """
    headnode = _must_find(model.Headnode, headnode)
    get_auth_backend().require_project_access(headnode.project)
    _assert_absent_n(headnode, model.Hnic, hnic)

    if not headnode.dirty:
        raise IllegalStateError

    hnic = model.Hnic(headnode, hnic)
    db.session.add(hnic)
    db.session.commit()
示例#35
0
文件: rest.py 项目: rahulbahal7/haas
    def wrapper(**kwargs):
        kwargs = _do_validation(schema, kwargs)

        init_auth()
        username = auth.get_auth_backend().get_user() or '(guest)'
        logger.info('%s - API call: %s(%s)' %
                    (username, f.__name__, _format_arglist(**kwargs)))

        ret = f(**kwargs)
        if ret is None:
            ret = ''
        return ret
示例#36
0
def port_detach_nic(switch, port):
    """Detach a port from the nic it's attached to

    If the port does not exist, a NotFoundError will be raised.

    If the port is not connected to anything, a NotFoundError will be raised.

    If the port is attached to a node which is not free, a BlockedError
    will be raised.
    """
    get_auth_backend().require_admin()
    switch = _must_find(model.Switch, switch)
    port = _must_find_n(switch, model.Port, port)

    if port.nic is None:
        raise NotFoundError(port.label + " not attached")
    if port.nic.owner.project is not None:
        raise BlockedError("The port is attached to a node which is not free")

    port.nic = None
    db.session.commit()
示例#37
0
def port_connect_nic(switch, port, node, nic):
    """Connect a port on a switch to a nic on a node.

    If any of the three arguments does not exist, a NotFoundError will be
    raised.

    If the port or the nic is already connected to something, a DuplicateError
    will be raised.
    """
    get_auth_backend().require_admin()
    switch = _must_find(model.Switch, switch)
    port = _must_find_n(switch, model.Port, port)

    node = _must_find(model.Node, node)
    nic = _must_find_n(node, model.Nic, nic)

    if nic.port is not None:
        raise DuplicateError(nic.label)

    if port.nic is not None:
        raise DuplicateError(port.label)

    nic.port = port
    db.session.commit()
示例#38
0
文件: server.py 项目: saacg/hil
def validate_state():
    """Do some sanity checking before kicking things off. In particular:

    * Make sure we have extensions loaded for:
      * a network allocator
      * an auth backend

    (More checks may be added in the future).

    If any of the checks fail, ``validate_state`` aborts the program.
    """
    if get_network_allocator() is None:
        sys.exit("ERROR: No network allocator registered; make sure your "
                 "haas.cfg loads an extension which provides the network "
                 "allocator.")
    if auth.get_auth_backend() is None:
        sys.exit("ERROR: No authentication/authorization backend registered; "
                 "make sure your haas.cfg loads an extension which provides "
                 "the auth backend.")
示例#39
0
def network_revoke_project_access(project, network):
    """Remove access to <network> from <project>.

    If the project or network does not exist, a NotFoundError will be raised.
    If the project is the owner of the network a BlockedError will be raised.
    """
    auth_backend = get_auth_backend()
    network = _must_find(model.Network, network)
    project = _must_find(model.Project, project)
    # must be admin, the owner of the network, or <project> to remove
    # <project>.

    if network.access:
        if not (auth_backend.have_admin() or
                (network.owner is not None
                 and auth_backend.have_project_access(network.owner)) or
                (project in network.access
                 and auth_backend.have_project_access(project))):
            raise AuthorizationError("You are not authorized to remove the "
                                     "specified project from this network.")

    if project not in network.access:
        raise NotFoundError("Network %r is not in project %r" %
                            (network.label, project.label))

    if project is network.owner:
        raise BlockedError("Project %r is owner of network %r and "
                           "its access cannot be removed" %
                           (project.label, network.label))

    # TODO: Make this and the next loop more SQLAlchemy-friendly
    for attachment in network.attachments:
        if attachment.nic.owner.project.label == project.label:
            raise BlockedError(
                "Project still has node(s) attached to the network")

    for hnic in network.hnics:
        if hnic.owner.project.label == project.label:
            raise BlockedError(
                "Project still has headnode(s) attached to the network")

    network.access.remove(project)
    db.session.commit()
示例#40
0
def network_grant_project_access(project, network):
    """Add access to <network> to <project>.

    If the project or network does not exist, a NotFoundError will be raised.
    If the project already has access to the network a DuplicateError will be
    raised.
    """
    network = _must_find(model.Network, network)
    project = _must_find(model.Project, project)
    auth_backend = get_auth_backend()

    # Must be admin or the owner of the network to add projects
    if network.owner is None:
        auth_backend.require_admin()
    else:
        auth_backend.require_project_access(network.owner)

    if project in network.access:
        raise DuplicateError('Network %s is already in project %s' %
                             (network.label, project.label))

    network.access.append(project)
    db.session.commit()
示例#41
0
def auth_backend():
    return get_auth_backend()
示例#42
0
def node_connect_network(node, nic, network, channel=None):
    """Connect a physical NIC to a network, on channel.

    If channel is ``None``, use the allocator default.

    Raises ProjectMismatchError if the node is not in a project, or if the
    project does not have access rights to the given network.

    Raises BlockedError if there is a pending network action, or if the network
    is already attached to the nic, or if the channel is in use.

    Raises BadArgumentError if the channel is invalid for the network.
    """
    def _have_attachment(nic, query):
        """Return whether there are any attachments matching ``query`` for ``nic``.

        ``query`` should an argument suitable to pass to db.query(...).filter
        """
        return model.NetworkAttachment.query.filter(
            model.NetworkAttachment.nic == nic,
            query,
        ).count() != 0

    auth_backend = get_auth_backend()

    node = _must_find(model.Node, node)
    nic = _must_find_n(node, model.Nic, nic)
    network = _must_find(model.Network, network)

    if not node.project:
        raise ProjectMismatchError("Node not in project")
    auth_backend.require_project_access(node.project)

    project = node.project

    allocator = get_network_allocator()

    if nic.port is None:
        raise NotFoundError("No port is connected to given nic.")

    if nic.current_action:
        raise BlockedError(
            "A networking operation is already active on the nic.")

    if (network.access) and (project not in network.access):
        raise ProjectMismatchError(
            "Project does not have access to given network.")

    if _have_attachment(nic, model.NetworkAttachment.network == network):
        raise BlockedError("The network is already attached to the nic.")

    if channel is None:
        channel = allocator.get_default_channel()

    if _have_attachment(nic, model.NetworkAttachment.channel == channel):
        raise BlockedError("The channel is already in use on the nic.")

    if not allocator.is_legal_channel_for(channel, network.network_id):
        raise BadArgumentError("Channel %r, is not legal for this network." %
                               channel)

    db.session.add(
        model.NetworkingAction(nic=nic, new_network=network, channel=channel))
    db.session.commit()
    return '', 202