Exemple #1
0
def delete(network_id, credentials, atomic_context=None):
    network = util.get_network(network_id,
                               credentials,
                               for_update=True,
                               non_deleted=True)
    if network.public and not credentials.is_admin:
        raise faults.Forbidden("Cannot delete the public network.")

    validate_network_action(network, "DESTROY")

    if network.nics.exists():
        raise faults.Conflict("Cannot delete network. There are ports still"
                              " configured on network network %s" % network.id)
    if network.ips.filter(deleted=False, floating_ip=True).exists():
        msg = "Cannot delete netowrk. Network has allocated floating IPs."
        raise faults.Conflict(msg)

    network.action = "DESTROY"
    # Mark network as drained to prevent automatic allocation of
    # public/floating IPs while the network is being deleted
    if network.public:
        network.drained = True
    network.save()

    # Delete network to all backends that exists
    for bnet in network.backend_networks.exclude(operstate="DELETED"):
        backend_mod.delete_network(network, bnet.backend)
    else:
        # If network does not exist in any backend, update the network state
        backend_mod.update_network_state(network,
                                         atomic_context=atomic_context)
    return network
Exemple #2
0
def create_floating_ip(userid,
                       network=None,
                       address=None,
                       project=None,
                       shared_to_project=False):
    if network is None:
        floating_ip = allocate_public_ip(userid, floating_ip=True)
    else:
        if not network.floating_ip_pool:
            msg = ("Cannot allocate floating IP. Network %s is"
                   " not a floating IP pool.")
            raise faults.Conflict(msg % network.id)
        if network.action == "DESTROY":
            msg = "Cannot allocate floating IP. Network %s is being deleted."
            raise faults.Conflict(msg % network.id)

        # Allocate the floating IP
        floating_ip = allocate_ip(network,
                                  userid,
                                  address=address,
                                  floating_ip=True)

    if project is None:
        project = userid
    floating_ip.project = project
    floating_ip.shared_to_project = shared_to_project
    floating_ip.save()
    # Issue commission (quotas)
    quotas.issue_and_accept_commission(floating_ip)
    transaction.commit()

    log.info("Created floating IP '%s' for user IP '%s'", floating_ip, userid)

    return floating_ip
Exemple #3
0
def reassign_volume(volume_id, project, shared_to_project, credentials,
                    atomic_context=None):
    volume = util.get_volume(credentials,
                             volume_id, for_update=True, non_deleted=True)

    if not credentials.is_admin and credentials.userid != volume.userid:
        raise faults.Forbidden("Action 'reassign' is allowed only to the owner"
                               " of the volume.")

    if volume.index == 0:
        raise faults.Conflict("Cannot reassign: %s is a system volume" %
                              volume.id)

    server = volume.machine
    if server is not None:
        commands.validate_server_action(server, "REASSIGN")

    if volume.project == project:
        if volume.shared_to_project != shared_to_project:
            log.info("%s volume %s to project %s",
                "Sharing" if shared_to_project else "Unsharing",
                volume, project)
            volume.shared_to_project = shared_to_project
            volume.save()
    else:
        action_fields = {"to_project": project, "from_project": volume.project}
        log.info("Reassigning volume %s from project %s to %s, shared: %s",
                volume, volume.project, project, shared_to_project)
        volume.project = project
        volume.shared_to_project = shared_to_project
        volume.save()
        quotas.issue_and_accept_commission(volume, action="REASSIGN",
                                           action_fields=action_fields,
                                           atomic_context=atomic_context)
    return volume
Exemple #4
0
def delete_floating_ip(floating_ip):
    if floating_ip.nic:
        # This is safe, you also need for_update to attach floating IP to
        # instance.
        server = floating_ip.nic.machine
        if server is None:
            msg = ("Floating IP '%s' is used by port '%s'" %
                   (floating_ip.id, floating_ip.nic_id))
        else:
            msg = ("Floating IP '%s' is used by server '%s'" %
                   (floating_ip.id, floating_ip.nic.machine_id))
        raise faults.Conflict(msg)

    # Lock network to prevent deadlock
    Network.objects.select_for_update().get(id=floating_ip.network_id)

    # Return the address of the floating IP back to pool
    floating_ip.release_address()
    # And mark the floating IP as deleted
    floating_ip.deleted = True
    floating_ip.save()
    # Release quota for floating IP
    quotas.issue_and_accept_commission(floating_ip, action="DESTROY")
    transaction.commit()
    # Delete the floating IP from DB
    log.info("Deleted floating IP '%s' of user '%s", floating_ip,
             floating_ip.userid)
    floating_ip.delete()
Exemple #5
0
def _port_from_setting(user_id, network_id, category):
    # TODO: Fix this..you need only IPv4 and only IPv6 network
    if network_id == "SNF:ANY_PUBLIC_IPV4":
        return create_public_ipv4_port(user_id, category=category)
    elif network_id == "SNF:ANY_PUBLIC_IPV6":
        return create_public_ipv6_port(user_id, category=category)
    elif network_id == "SNF:ANY_PUBLIC":
        try:
            return create_public_ipv4_port(user_id, category=category)
        except faults.Conflict as e1:
            try:
                return create_public_ipv6_port(user_id, category=category)
            except faults.Conflict as e2:
                log.error(
                    "Failed to connect server to a public IPv4 or IPv6"
                    " network. IPv4: %s, IPv6: %s", e1, e2)
                msg = ("Cannot connect server to a public IPv4 or IPv6"
                       " network.")
                raise faults.Conflict(msg)
    else:  # Case of network ID
        if category in ["user", "default"]:
            return _port_for_request(user_id, {"uuid": network_id})
        elif category == "admin":
            network = util.get_network(network_id, user_id, non_deleted=True)
            return _create_port(user_id, network)
        else:
            raise ValueError("Unknown category: %s" % category)
Exemple #6
0
def reassign_volume(volume, project, shared_to_project):
    if volume.index == 0:
        raise faults.Conflict("Cannot reassign: %s is a system volume" %
                              volume.id)

    server = volume.machine
    if server is not None:
        commands.validate_server_action(server, "REASSIGN")

    if volume.project == project:
        if volume.shared_to_project != shared_to_project:
            log.info("%s volume %s to project %s",
                "Sharing" if shared_to_project else "Unsharing",
                volume, project)
            volume.shared_to_project = shared_to_project
            volume.save()
    else:
        action_fields = {"to_project": project, "from_project": volume.project}
        log.info("Reassigning volume %s from project %s to %s, shared: %s",
                volume, volume.project, project, shared_to_project)
        volume.project = project
        volume.shared_to_project = shared_to_project
        volume.save()
        quotas.issue_and_accept_commission(volume, action="REASSIGN",
                                           action_fields=action_fields)
    return volume
Exemple #7
0
def get_free_floating_ip(userid, network=None):
    """Get one of the free available floating IPs of the user.

    Get one of the users floating IPs that is not connected to any port
    or server. If network is specified, the floating IP must be from
    that network.

    """
    floating_ips = IPAddress.objects\
                            .filter(userid=userid, deleted=False, nic=None,
                                    floating_ip=True)
    if network is not None:
        floating_ips = floating_ips.filter(network=network)

    for floating_ip in floating_ips:
        floating_ip = IPAddress.objects.select_for_update()\
                                       .get(id=floating_ip.id)
        if floating_ip.nic is None:
            return floating_ip

    msg = "Cannot find an unused floating IP to connect server to"
    if network is not None:
        msg += " network '%s'." % network.id
    else:
        msg += " a public network."
    msg += " Please create a floating IP."
    raise faults.Conflict(msg)
Exemple #8
0
def associate_port_with_machine(port, machine):
    """Associate a Port with a VirtualMachine.

    Associate the port with the VirtualMachine and add an entry to the
    IPAddressHistory if the port has a public IPv4 address from a public
    network.

    """
    if port.machine is not None:
        raise faults.Conflict("Port %s is already in use." % port.id)
    if port.network.public:
        ipv4_address = port.ipv4_address
        if ipv4_address is not None:
            ip_log = IPAddressHistory.objects.create(
                server_id=machine.id,
                user_id=machine.userid,
                network_id=port.network_id,
                address=ipv4_address,
                action=IPAddressHistory.ASSOCIATE,
                action_reason="associate port %s" % port.id)
            log.info("Created IP log entry %s", ip_log)
    port.machine = machine
    port.state = "BUILD"
    port.device_owner = "vm"
    port.save()
    return port
Exemple #9
0
def create_new_keypair(request):
    """Generates or imports a keypair.

    Normal response code: 201

    Error response codes: badRequest(400), unauthorized(401), forbidden(403),
                          conflict(409)
    """

    userid = request.credentials.userid
    if PublicKeyPair.user_limit_exceeded(userid):
        return HttpResponseServerError("SSH keys limit exceeded")

    req = utils.get_json_body(request)
    try:
        keypair = req['keypair']
        assert (isinstance(req, dict))
        name = keypair['name']
    except (KeyError, AssertionError):
        raise faults.BadRequest('Malformed request.')

    if re.match(key_name_regex, name) is None:
        raise faults.BadRequest('Invalid name format')

    try:
        # If the key with the same name exists in the database
        # a conflict error will be raised

        util.get_keypair(name, userid)
        # If we get past this point then the key is already present
        # in the database
        raise faults.Conflict('A keypair with that name already exists')
    except faults.ItemNotFound:
        new_keypair = PublicKeyPair(name=name, user=userid)

    gen_keypair = None
    try:
        new_keypair.content = keypair['public_key']
    except KeyError:
        # If the public_key field is omitted, generate a new
        # keypair and return both the private and the public key
        if not SUPPORT_GENERATE_KEYS:
            raise faults.Forbidden(
                "Application does not support ssh keys generation")

        gen_keypair = generate_keypair()
        new_keypair.content = gen_keypair['public']

    new_keypair.save()

    data = keypair_to_dict(new_keypair)
    if gen_keypair is not None:
        data['keypair']['private_key'] = gen_keypair['private']

    return HttpResponse(json.dumps(data), status=201)
Exemple #10
0
def create_public_ipv6_port(user_id, category=None):
    """Create a port in a public IPv6 only network."""
    networks = Network.objects.filter(public=True, deleted=False,
                                      drained=False, subnets__ipversion=6)\
                              .exclude(subnets__ipversion=4)
    if networks:
        return _create_port(user_id, networks[0])
    else:
        msg = "No available IPv6 only network!"
        log.error(msg)
        raise faults.Conflict(msg)
Exemple #11
0
def create_floating_ip(credentials,
                       network_id=None,
                       address=None,
                       project=None,
                       shared_to_project=False,
                       atomic_context=None):
    userid = credentials.userid
    if network_id is None:
        floating_ip = allocate_public_ip(userid, floating_ip=True)
    else:
        network = util.get_network(network_id,
                                   credentials,
                                   for_update=True,
                                   non_deleted=True)
        if not network.floating_ip_pool:
            msg = ("Cannot allocate floating IP. Network %s is"
                   " not a floating IP pool.")
            raise faults.Conflict(msg % network.id)
        if network.action == "DESTROY":
            msg = "Cannot allocate floating IP. Network %s is being deleted."
            raise faults.Conflict(msg % network.id)

        # Allocate the floating IP
        floating_ip = allocate_ip(network,
                                  userid,
                                  address=address,
                                  floating_ip=True)

    if project is None:
        project = userid
    floating_ip.project = project
    floating_ip.shared_to_project = shared_to_project
    floating_ip.save()
    # Issue commission (quotas)
    quotas.issue_and_accept_commission(floating_ip,
                                       atomic_context=atomic_context)

    log.info("Created floating IP '%s' for user IP '%s'", floating_ip, userid)

    return floating_ip
Exemple #12
0
def allocate_ip(network, userid, address=None, floating_ip=False):
    """Try to allocate an IP from networks IP pools."""
    if network.action == "DESTROY":
        raise faults.Conflict("Cannot allocate IP. Network %s is being"
                              " deleted" % network.id)
    elif network.drained:
        raise faults.Conflict("Can not allocate IP while network '%s' is in"
                              " 'SNF:DRAINED' status" % network.id)

    ip_pools = IPPoolTable.objects.select_for_update()\
        .filter(subnet__network=network).order_by('id')
    try:
        return allocate_ip_from_pools(ip_pools, userid, address=address,
                                      floating_ip=floating_ip)
    except pools.EmptyPool:
        raise faults.Conflict("No more IP addresses available on network %s"
                              % network.id)
    except pools.ValueNotAvailable:
        raise faults.Conflict("IP address %s is already used." % address)
    except pools.InvalidValue:
        raise faults.BadRequest("Address %s does not belong to network %s" %
                                (address, network.id))
Exemple #13
0
def delete(network):
    if network.nics.exists():
        raise faults.Conflict("Cannot delete network. There are ports still"
                              " configured on network network %s" % network.id)
    if network.ips.filter(deleted=False, floating_ip=True).exists():
        msg = "Cannot delete netowrk. Network has allocated floating IPs."
        raise faults.Conflict(msg)

    network.action = "DESTROY"
    # Mark network as drained to prevent automatic allocation of
    # public/floating IPs while the network is being deleted
    if network.public:
        network.drained = True
    network.save()

    # Delete network to all backends that exists
    for bnet in network.backend_networks.exclude(operstate="DELETED"):
        backend_mod.delete_network(network, bnet.backend)
    else:
        # If network does not exist in any backend, update the network state
        backend_mod.update_network_state(network)
    return network
Exemple #14
0
def user_update(request, user_id):
    admin_id = request.user_uniq
    req = api.utils.get_json_body(request)
    logger.info('user_update: %s user: %s request: %s', admin_id, user_id, req)

    user_data = req.get('user', {})

    try:
        user = AstakosUser.objects.select_for_update().get(uuid=user_id)
    except AstakosUser.DoesNotExist:
        raise faults.ItemNotFound("User not found")

    email = user_data.get('username', None)
    first_name = user_data.get('first_name', None)
    last_name = user_data.get('last_name', None)
    affiliation = user_data.get('affiliation', None)
    password = user_data.get('password', None)
    metadata = user_data.get('metadata', {})

    if 'password' in user_data:
        user.set_password(password)

    if 'username' in user_data:
        try:
            validate_email(email)
        except ValidationError:
            raise faults.BadRequest("Invalid username (email format required)")

        if AstakosUser.objects.verified_user_exists(email):
            raise faults.Conflict("User '%s' already exists" % email)

        user.email = email

    if 'first_name' in user_data:
        user.first_name = first_name

    if 'last_name' in user_data:
        user.last_name = last_name

    try:
        user.save()
        if 'metadata' in user_data:
            provider = user.auth_providers.get(auth_backend="astakos")
            provider.info = metadata
            if affiliation in user_data:
                provider.affiliation = affiliation
            provider.save()

    except Exception, e:
        raise faults.BadRequest(e.message)
Exemple #15
0
def reassign_volume(volume, project):
    if volume.index == 0:
        raise faults.Conflict("Cannot reassign: %s is a system volume" %
                              volume.id)
    if volume.machine_id is not None:
        server = util.get_server(volume.userid,
                                 volume.machine_id,
                                 for_update=True,
                                 non_deleted=True,
                                 exception=faults.BadRequest)
        commands.validate_server_action(server, "REASSIGN")
    action_fields = {"from_project": volume.project, "to_project": project}
    log.info("Reassigning volume %s from project %s to %s", volume.id,
             volume.project, project)
    volume.project = project
    volume.save()
    quotas.issue_and_accept_commission(volume,
                                       action="REASSIGN",
                                       action_fields=action_fields)
Exemple #16
0
def allocate_public_ip(userid, floating_ip=False, backend=None, networks=None):
    """Try to allocate a public or floating IP address.

    Try to allocate a a public IPv4 address from one of the available networks.
    If 'floating_ip' is set, only networks which are floating IP pools will be
    used and the IPAddress that will be created will be marked as a floating
    IP. If 'backend' is set, only the networks that exist in this backend will
    be used.

    """

    ip_pool_rows = IPPoolTable.objects.select_for_update()\
        .prefetch_related("subnet__network")\
        .filter(subnet__deleted=False)\
        .filter(subnet__network__deleted=False)\
        .filter(subnet__network__public=True)\
        .filter(subnet__network__drained=False)
    if networks is not None:
        ip_pool_rows = ip_pool_rows.filter(subnet__network__in=networks)
    if floating_ip:
        ip_pool_rows = ip_pool_rows\
            .filter(subnet__network__floating_ip_pool=True)
    if backend is not None:
        ip_pool_rows = ip_pool_rows\
            .filter(subnet__network__backend_networks__backend=backend)

    try:
        return allocate_ip_from_pools(ip_pool_rows,
                                      userid,
                                      floating_ip=floating_ip)
    except pools.EmptyPool:
        ip_type = "floating" if floating_ip else "public"
        log_msg = "Failed to allocate a %s IP. Reason:" % ip_type
        if ip_pool_rows:
            log_msg += " No network exists."
        else:
            log_msg += " All network are full."
        if backend is not None:
            log_msg += " Backend: %s" % backend
        log.error(log_msg)
        exception_msg = "Cannot allocate a %s IP address." % ip_type
        raise faults.Conflict(exception_msg)
Exemple #17
0
        def wrapper(request, *args, **kwargs):
            # The args variable may contain up to (account, container, object).
            if len(args) > 1 and len(args[1]) > 256:
                raise faults.BadRequest("Container name too large")
            if len(args) > 2 and len(args[2]) > 1024:
                raise faults.BadRequest('Object name too large.')

            success_status = False
            try:
                # Add a PithosBackend as attribute of the request object
                request.backend = get_backend()
                request.backend.pre_exec(lock_container_path)

                # Many API method expect thet X-Auth-Token in request,token
                request.token = request.x_auth_token
                update_request_headers(request)
                response = func(request, *args, **kwargs)
                update_response_headers(request, response)

                success_status = True
                return response
            except LimitExceeded as le:
                raise faults.BadRequest(le.args[0])
            except BrokenSnapshot as bs:
                raise faults.BadRequest(bs.args[0])
            except (IllegalOperationError, NotAllowedError) as e:
                raise faults.Forbidden(smart_str_(e))
            except (InconsistentContentSize, InvalidPolicy, LimitExceeded,
                    InvalidHash, ValueError) as e:
                raise faults.BadRequest(smart_str_(e))
            except (ItemNotExists, VersionNotExists) as e:
                raise faults.ItemNotFound(smart_str_(e))
            except ContainerNotEmpty as e:
                raise faults.Conflict(smart_str_(e))
            except QuotaError as e:
                raise faults.RequestEntityTooLarge('Quota error: %s' % e)
            finally:
                # Always close PithosBackend connection
                if getattr(request, "backend", None) is not None:
                    request.backend.post_exec(success_status)
                    request.backend.close()
Exemple #18
0
def _create_port(userid,
                 network,
                 machine=None,
                 use_ipaddress=None,
                 address=None,
                 name="",
                 security_groups=None,
                 device_owner=None):
    """Create a new port on the specified network.

    Create a new Port(NetworkInterface model) on the specified Network. If
    'machine' is specified, the machine will be connected to the network using
    this port. If 'use_ipaddress' argument is specified, the port will be
    assigned this IPAddress. Otherwise, an IPv4 address from the IPv4 subnet
    will be allocated.

    """
    if network.state != "ACTIVE":
        raise faults.Conflict("Cannot create port while network '%s' is in"
                              " '%s' status" % (network.id, network.state))
    elif network.action == "DESTROY":
        msg = "Cannot create port. Network %s is being deleted."
        raise faults.Conflict(msg % network.id)
    elif network.drained:
        raise faults.Conflict("Cannot create port while network %s is in"
                              " 'SNF:DRAINED' status" % network.id)

    utils.check_name_length(name, NetworkInterface.NETWORK_IFACE_NAME_LENGTH,
                            "Port name is too long")

    ipaddress = None
    if use_ipaddress is not None:
        # Use an existing IPAddress object.
        ipaddress = use_ipaddress
        if ipaddress and (ipaddress.network_id != network.id):
            msg = "IP Address %s does not belong to network %s"
            raise faults.Conflict(msg % (ipaddress.address, network.id))
    else:
        # If network has IPv4 subnets, try to allocate the address that the
        # the user specified or a random one.
        if network.subnets.filter(ipversion=4).exists():
            ipaddress = ips.allocate_ip(network,
                                        userid=userid,
                                        address=address)
        elif address is not None:
            raise faults.BadRequest("Address %s is not a valid IP for the"
                                    " defined network subnets" % address)

    if ipaddress is not None and ipaddress.nic is not None:
        raise faults.Conflict("IP address '%s' is already in use" %
                              ipaddress.address)

    port = NetworkInterface.objects.create(network=network,
                                           state="DOWN",
                                           userid=userid,
                                           device_owner=None,
                                           name=name)

    # add the security groups if any
    if security_groups:
        port.security_groups.add(*security_groups)

    if ipaddress is not None:
        # Associate IPAddress with the Port
        ipaddress.nic = port
        ipaddress.save()

    if machine is not None:
        # Connect port to the instance.
        machine = connect(machine, network, port)
        jobID = machine.task_job_id
        log.info("Created Port %s with IP %s. Ganeti Job: %s", port, ipaddress,
                 jobID)
    else:
        log.info("Created Port %s with IP %s not attached to any instance",
                 port, ipaddress)

    return port
Exemple #19
0
def users_create(request):
    user_id = request.user_uniq
    req = api.utils.get_json_body(request)
    logger.info('users_create: %s request: %s', user_id, req)

    user_data = req.get('user', {})
    email = user_data.get('username', None)

    first_name = user_data.get('first_name', None)
    last_name = user_data.get('last_name', None)
    affiliation = user_data.get('affiliation', None)
    password = user_data.get('password', None)
    metadata = user_data.get('metadata', {})

    password_gen = AstakosUser.objects.make_random_password
    if not password:
        password = password_gen()

    try:
        validate_email(email)
    except ValidationError:
        raise faults.BadRequest("Invalid username (email format required)")

    if AstakosUser.objects.verified_user_exists(email):
        raise faults.Conflict("User '%s' already exists" % email)

    if not first_name:
        raise faults.BadRequest("Invalid first_name")

    if not last_name:
        raise faults.BadRequest("Invalid last_name")

    has_signed_terms = True

    try:
        user = make_local_user(email,
                               first_name=first_name,
                               last_name=last_name,
                               password=password,
                               has_signed_terms=has_signed_terms)
        if metadata:
            # we expect a unique local auth provider for the user
            provider = user.auth_providers.get()
            provider.info = metadata
            provider.affiliation = affiliation
            provider.save()

        user = AstakosUser.objects.get(pk=user.pk)
        code = user.verification_code
        ver_res = user_logic.verify(user, code)
        if ver_res.is_error():
            raise Exception(ver_res.message)

        # in case of auto moderation user moderation is handled within the
        # verification process, no need to reapply moderation process
        if not user.moderated:
            mod_res = user_logic.accept(user, accept=True, notify_user=False)
            if mod_res.is_error():
                raise Exception(ver_res.message)

    except Exception, e:
        raise faults.BadRequest(e.message)