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.owner is None: auth_backend.require_admin() else: auth_backend.require_project_access(network.owner) if len(network.attachments) != 0: raise errors.BlockedError("Network still connected to nodes") if network.hnics: raise errors.BlockedError("Network still connected to headnodes") if len(network.scheduled_nics) != 0: raise errors.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()
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 = get_or_404(model.Project, project) get_auth_backend().require_project_access(project) node = get_or_404(model.Node, node) if node not in project.nodes: raise errors.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 errors.BlockedError("Node attached to a network") for nic in node.nics: if nic.current_action is not None and \ nic.current_action.status == 'PENDING': raise errors.BlockedError("Node has pending network actions") node.obm.stop_console() node.obm.delete_console() project.nodes.remove(node) _maintain(project, node, node.label) db.session.commit()
def project_delete(project): """Delete project. If the project does not exist, a NotFoundError will be raised. """ get_auth_backend().require_admin() project = get_or_404(model.Project, project) if project.nodes: raise errors.BlockedError("Project has nodes still") if project.networks_created: raise errors.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 errors.BlockedError("Project can still access networks") if project.headnodes: raise errors.BlockedError("Project still has a headnode") db.session.delete(project) db.session.commit()
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 errors.ProjectMismatchError("Node not in project") auth_backend.require_project_access(node.project) if nic.current_action: raise errors.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 errors.BadArgumentError( "The network is not attached to the nic.") db.session.add( model.NetworkingAction(type='modify_port', nic=nic, channel=attachment.channel, new_network=None)) db.session.commit() return '', 202
def node_set_bootdev(node, bootdev): """Set the node's boot device.""" node = get_or_404(model.Node, node) get_auth_backend().require_project_access(node.project) if not node.obm_is_enabled(): raise errors.BlockedError("OBM is not enabled") return _obmd_redirect(node, '/boot_device')
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 = get_or_404(model.Network, network) project = get_or_404(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 errors.AuthorizationError( "You are not authorized to remove the " "specified project from this network.") if project not in network.access: raise errors.NotFoundError( "Network %r is not in project %r" % (network.label, project.label)) if project is network.owner: raise errors.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 errors.BlockedError( "Project still has node(s) attached to the network") for hnic in network.hnics: if hnic.owner.project.label == project.label: raise errors.BlockedError( "Project still has headnode(s) attached to the network") network.access.remove(project) db.session.commit()
def node_delete(node): """Delete node. If the node does not exist, a NotFoundError will be raised. """ get_auth_backend().require_admin() node = get_or_404(model.Node, node) if node.project: raise errors.BlockedError("Node %r is part of project %r; remove from " "project before deleting" % (node.label, node.project.label)) if node.nics != []: raise errors.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()
def switch_delete(switch): get_auth_backend().require_admin() switch = _must_find(model.Switch, switch) if switch.ports != []: raise errors.BlockedError("Switch %r has ports; delete them first." % switch.label) db.session.delete(switch) db.session.commit()
def check_pending_action(nic): """Raises an error if the nic has a pending action Otherwise deletes the completed action""" if nic.current_action: if nic.current_action.status == 'PENDING': raise errors.BlockedError( "A networking operation is already active on the nic.") else: db.session.delete(nic.current_action) return
def _node_power_on_off(node, op): """helper for node_power_{on,off} This implements the actual logic for both calls. `op` should be one of 'on' or 'off' """ node = get_or_404(model.Node, node) get_auth_backend().require_project_access(node.project) if not node.obm_is_enabled(): raise errors.BlockedError("OBM is not enabled") return _obmd_redirect(node, '/power_' + op)
def node_power_cycle(node, force=False): """Reboot the node. Force indicates whether the node should be forced off, or allowed to respond to the shutdown signal. """ node = get_or_404(model.Node, node) get_auth_backend().require_project_access(node.project) if not node.obm_is_enabled(): raise errors.BlockedError("OBM is not enabled") return _obmd_redirect(node, '/power_cycle')
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 = get_or_404(model.Project, project) get_auth_backend().require_project_access(project) node = get_or_404(model.Node, node) if node.project is not None: raise errors.BlockedError("Node is already owned by a project.") project.nodes.append(node) db.session.commit()
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 = get_or_404(model.Switch, switch) port = get_child_or_404(switch, model.Port, port) if port.nic is not None: raise errors.BlockedError( "Port %r is attached to a nic; please detach " "it first." % port.label) db.session.delete(port) db.session.commit()
def port_revert(switch, port): 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 errors.NotFoundError(port.label + " not attached") if port.nic.current_action: raise errors.BlockedError("Port already has a pending action.") db.session.add( model.NetworkingAction(type='revert_port', nic=port.nic, channel='', new_network=None)) db.session.commit()
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 = get_or_404(model.Switch, switch) port = get_child_or_404(switch, model.Port, port) if port.nic is None: raise errors.NotFoundError(port.label + " not attached") if port.nic.owner.project is not None: raise errors.BlockedError("The port is attached to a node which is " "not free") port.nic = None db.session.commit()
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 errors.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 errors.NotFoundError("No port is connected to given nic.") if nic.current_action: raise errors.BlockedError( "A networking operation is already active on the nic.") if (network.access) and (project not in network.access): raise errors.ProjectMismatchError( "Project does not have access to given network.") if _have_attachment(nic, model.NetworkAttachment.network == network): raise errors.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 errors.BlockedError("The channel is already in use on the nic.") if not allocator.is_legal_channel_for(channel, network.network_id): raise errors.BadArgumentError( "Channel %r, is not legal for this network." % channel) db.session.add( model.NetworkingAction(type='modify_port', nic=nic, new_network=network, channel=channel)) db.session.commit() return '', 202
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, ).first() is not None auth_backend = get_auth_backend() node = get_or_404(model.Node, node) nic = get_child_or_404(node, model.Nic, nic) network = get_or_404(model.Network, network) if not node.project: raise errors.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 errors.NotFoundError("No port is connected to given nic.") check_pending_action(nic) if (network.access) and (project not in network.access): raise errors.ProjectMismatchError( "Project does not have access to given network.") if _have_attachment(nic, model.NetworkAttachment.network == network): raise errors.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 errors.BlockedError("The channel is already in use on the nic.") if not allocator.is_legal_channel_for(channel, network.network_id): raise errors.BadArgumentError( "Channel %r, is not legal for this network." % channel) switch = nic.port.owner switch.ensure_legal_operation(nic, 'connect', channel) unique_id = str(uuid.uuid4()) db.session.add( model.NetworkingAction(type='modify_port', nic=nic, new_network=network, channel=channel, uuid=unique_id, status='PENDING')) db.session.commit() return json.dumps({'status_id': unique_id}), 202