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
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
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
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()
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)
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
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)
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
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)
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)
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
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))
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
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)
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)
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)
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()
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
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)