def reassign(vm, project, shared_to_project): commands.validate_server_action(vm, "REASSIGN") if vm.project == project: if vm.shared_to_project != shared_to_project: log.info("%s VM %s to project %s", "Sharing" if shared_to_project else "Unsharing", vm, project) vm.shared_to_project = shared_to_project vm.volumes.filter(index=0, deleted=False)\ .update(shared_to_project=shared_to_project) vm.save() else: action_fields = {"to_project": project, "from_project": vm.project} log.info("Reassigning VM %s from project %s to %s, shared: %s", vm, vm.project, project, shared_to_project) if not (vm.backend.public or vm.backend.projects.filter(project=project).exists()): raise faults.BadRequest("Cannot reassign VM. Target project " "doesn't have access to the VM's backend.") vm.project = project vm.shared_to_project = shared_to_project vm.save() vm.volumes.filter(index=0, deleted=False).update( project=project, shared_to_project=shared_to_project) quotas.issue_and_accept_commission(vm, action="REASSIGN", action_fields=action_fields) return vm
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 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 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 reassign_floating_ip( floating_ip_id, project, shared_to_project, credentials, atomic_context=None): floating_ip = util.get_floating_ip_by_id(credentials, floating_ip_id, for_update=True) if not credentials.is_admin and credentials.userid != floating_ip.userid: raise faults.Forbidden("Action 'reassign' is allowed only to the owner" " of the floating IP.") validate_ip_action(floating_ip, "REASSIGN", silent=False) if floating_ip.project == project: if floating_ip.shared_to_project != shared_to_project: log.info("%s floating_ip %s to project %s", "Sharing" if shared_to_project else "Unsharing", floating_ip, project) floating_ip.shared_to_project = shared_to_project floating_ip.save() else: action_fields = {"to_project": project, "from_project": floating_ip.project} log.info("Reassigning floating_ip %s from project %s to %s, shared: %s", floating_ip, floating_ip.project, project, shared_to_project) floating_ip.project = project floating_ip.shared_to_project = shared_to_project floating_ip.save() quotas.issue_and_accept_commission(floating_ip, action="REASSIGN", action_fields=action_fields, atomic_context=atomic_context) return floating_ip
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 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 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 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 create_floating_ip(userid, network=None, address=None, project=None): 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.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 update_network_state(network): """Update the state of a Network based on BackendNetwork states. Update the state of a Network based on the operstate of the networks in the backends that network exists. The state of the network is: * ACTIVE: If it is 'ACTIVE' in at least one backend. * DELETED: If it is is 'DELETED' in all backends that have been created. This function also releases the resources (MAC prefix or Bridge) and the quotas for the network. """ if network.deleted: # Network has already been deleted. Just assert that state is also # DELETED if not network.state == "DELETED": network.state = "DELETED" network.save() return backend_states = [s.operstate for s in network.backend_networks.all()] if not backend_states and network.action != "DESTROY": if network.state != "ACTIVE": network.state = "ACTIVE" network.save() return # Network is deleted when all BackendNetworks go to "DELETED" operstate deleted = reduce(lambda x, y: x == y and "DELETED", backend_states, "DELETED") # Release the resources on the deletion of the Network if deleted: log.info("Network %r deleted. Releasing link %r mac_prefix %r", network.id, network.mac_prefix, network.link) network.deleted = True network.state = "DELETED" if network.mac_prefix: if network.FLAVORS[network.flavor]["mac_prefix"] == "pool": release_resource(res_type="mac_prefix", value=network.mac_prefix) if network.link: if network.FLAVORS[network.flavor]["link"] == "pool": release_resource(res_type="bridge", value=network.link) # Issue commission if network.userid: quotas.issue_and_accept_commission(network, delete=True) # the above has already saved the object and committed; # a second save would override others' changes, since the # object is now unlocked return elif not network.public: log.warning("Network %s does not have an owner!", network.id) network.save()
def reassign(network, project): action_fields = {"to_project": project, "from_project": network.project} log.info("Reassigning network %s from project %s to %s", network, network.project, project) network.project = project network.save() quotas.issue_and_accept_commission(network, action="REASSIGN", action_fields=action_fields) return network
def reassign_floating_ip(floating_ip, project): action_fields = {"to_project": project, "from_project": floating_ip.project} log.info("Reassigning floating IP %s from project %s to %s", floating_ip, floating_ip.project, project) floating_ip.project = project floating_ip.save() quotas.issue_and_accept_commission(floating_ip, action="REASSIGN", action_fields=action_fields)
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None): """Process a job progress notification from the backend Process an incoming message from the backend (currently Ganeti). Job notifications with a terminating status (sucess, error, or canceled), also update the operating state of the VM. """ # See #1492, #1031, #1111 why this line has been removed #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or if status not in [x[0] for x in BACKEND_STATUSES]: raise VirtualMachine.InvalidBackendMsgError(opcode, status) vm.backendjobid = jobid vm.backendjobstatus = status vm.backendopcode = opcode vm.backendlogmsg = logmsg # Update backendtime only for jobs that have been successfully completed, # since only these jobs update the state of the VM. Else a "race condition" # may occur when a successful job (e.g. OP_INSTANCE_REMOVE) completes # before an error job and messages arrive in reversed order. if status == 'success': vm.backendtime = etime # Notifications of success change the operating state state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode, None) if status == 'success' and state_for_success is not None: vm.operstate = state_for_success # Update the NICs of the VM if status == "success" and nics is not None: _process_net_status(vm, etime, nics) # Special case: if OP_INSTANCE_CREATE fails --> ERROR if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'): vm.operstate = 'ERROR' vm.backendtime = etime elif opcode == 'OP_INSTANCE_REMOVE': # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR, # when no instance exists at the Ganeti backend. if status == "success" or (status == "error" and not vm_exists_in_backend(vm)): _process_net_status(vm, etime, nics=[]) vm.operstate = state_for_success vm.backendtime = etime if not vm.deleted: vm.deleted = True # Issue and accept commission to Quotaholder quotas.issue_and_accept_commission(vm, delete=True) # the above has already saved the object and committed; # a second save would override others' changes, since the # object is now unlocked return vm.save()
def reassign(vm, project): commands.validate_server_action(vm, "REASSIGN") action_fields = {"to_project": project, "from_project": vm.project} log.info("Reassigning VM %s from project %s to %s", vm, vm.project, project) vm.project = project vm.save() vm.volumes.filter(index=0, deleted=False).update(project=project) quotas.issue_and_accept_commission(vm, action="REASSIGN", action_fields=action_fields)
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None): """Process a job progress notification from the backend Process an incoming message from the backend (currently Ganeti). Job notifications with a terminating status (sucess, error, or canceled), also update the operating state of the VM. """ # See #1492, #1031, #1111 why this line has been removed #if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or if status not in [x[0] for x in BACKEND_STATUSES]: raise VirtualMachine.InvalidBackendMsgError(opcode, status) vm.backendjobid = jobid vm.backendjobstatus = status vm.backendopcode = opcode vm.backendlogmsg = logmsg # Notifications of success change the operating state state_for_success = VirtualMachine.OPER_STATE_FROM_OPCODE.get(opcode, None) if status == 'success' and state_for_success is not None: vm.operstate = state_for_success # Update the NICs of the VM if status == "success" and nics is not None: _process_net_status(vm, etime, nics) # Special case: if OP_INSTANCE_CREATE fails --> ERROR if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'): vm.operstate = 'ERROR' vm.backendtime = etime elif opcode == 'OP_INSTANCE_REMOVE': # Set the deleted flag explicitly, cater for admin-initiated removals # Special case: OP_INSTANCE_REMOVE fails for machines in ERROR, # when no instance exists at the Ganeti backend. # See ticket #799 for all the details. # if (status == 'success' or (status == 'error' and (vm.operstate == 'ERROR' or vm.action == 'DESTROY'))): _process_net_status(vm, etime, nics=[]) vm.deleted = True vm.operstate = state_for_success vm.backendtime = etime # Issue and accept commission to Quotaholder quotas.issue_and_accept_commission(vm, delete=True) # Update backendtime only for jobs that have been successfully completed, # since only these jobs update the state of the VM. Else a "race condition" # may occur when a successful job (e.g. OP_INSTANCE_REMOVE) completes # before an error job and messages arrive in reversed order. if status == 'success': vm.backendtime = etime vm.save()
def reassign(vm, project): commands.validate_server_action(vm, "REASSIGN") action_fields = {"to_project": project, "from_project": vm.project} log.info("Reassigning VM %s from project %s to %s", vm, vm.project, project) vm.project = project vm.save() vm.volumes.filter(index=0, deleted=False).update(project=project) quotas.issue_and_accept_commission(vm, action="REASSIGN", action_fields=action_fields)
def import_server(instance_name, backend_id, flavor_id, image_id, user_id, new_public_nic, stream=sys.stdout): flavor = common.get_flavor(flavor_id) backend = common.get_backend(backend_id) backend_client = backend.get_client() try: instance = backend_client.GetInstance(instance_name) except GanetiApiError as e: if e.code == 404: raise CommandError("Instance %s does not exist in backend %s" % (instance_name, backend)) else: raise CommandError("Unexpected error" + str(e)) if not new_public_nic: check_instance_nics(instance) shutdown_instance(instance, backend_client, stream=stream) # Create the VM in DB stream.write("Creating VM entry in DB\n") vm = VirtualMachine.objects.create(name=instance_name, backend=backend, userid=user_id, imageid=image_id, flavor=flavor) quotas.issue_and_accept_commission(vm) if new_public_nic: remove_instance_nics(instance, backend_client, stream=stream) # Rename instance rename_instance(instance_name, vm.backend_vm_id, backend_client, stream) if new_public_nic: ports = servers.create_instance_ports(user_id) stream.write("Adding new NICs to server") [servers.associate_port_with_machine(port, vm) for port in ports] [connect_to_network(vm, port) for port in ports] # Startup instance startup_instance(vm.backend_vm_id, backend_client, stream=stream) backend.put_client(backend_client) return
def reassign_floating_ip(floating_ip, project): action_fields = { "to_project": project, "from_project": floating_ip.project } log.info("Reassigning floating IP %s from project %s to %s", floating_ip, floating_ip.project, project) floating_ip.project = project floating_ip.save() quotas.issue_and_accept_commission(floating_ip, action="REASSIGN", action_fields=action_fields)
def update_network_state(network): """Update the state of a Network based on BackendNetwork states. Update the state of a Network based on the operstate of the networks in the backends that network exists. The state of the network is: * ACTIVE: If it is 'ACTIVE' in at least one backend. * DELETED: If it is is 'DELETED' in all backends that have been created. This function also releases the resources (MAC prefix or Bridge) and the quotas for the network. """ if network.deleted: # Network has already been deleted. Just assert that state is also # DELETED if not network.state == "DELETED": network.state = "DELETED" network.save() return backend_states = [s.operstate for s in network.backend_networks.all()] if not backend_states and network.action != "DESTROY": if network.state != "ACTIVE": network.state = "ACTIVE" network.save() return # Network is deleted when all BackendNetworks go to "DELETED" operstate deleted = reduce(lambda x, y: x == y and "DELETED", backend_states, "DELETED") # Release the resources on the deletion of the Network if deleted: log.info("Network %r deleted. Releasing link %r mac_prefix %r", network.id, network.mac_prefix, network.link) network.deleted = True network.state = "DELETED" if network.mac_prefix: if network.FLAVORS[network.flavor]["mac_prefix"] == "pool": release_resource(res_type="mac_prefix", value=network.mac_prefix) if network.link: if network.FLAVORS[network.flavor]["link"] == "pool": release_resource(res_type="bridge", value=network.link) # Issue commission if network.userid: quotas.issue_and_accept_commission(network, delete=True) elif not network.public: log.warning("Network %s does not have an owner!", network.id) network.save()
def create(user_id, size, server=None, name=None, description=None, source_volume_id=None, source_snapshot_id=None, source_image_id=None, volume_type_id=None, metadata=None, project_id=None, shared_to_project=False): """Create a new volume and optionally attach it to a server. This function serves as the main entry-point for volume creation. It gets the necessary data either from the API or from an snf-manage command and then feeds that data to the lower-level functions that handle the actual creation of the volume and the server attachments. """ volume_type = None # If given a server id, assert that it exists and that it belongs to the # user. if server: volume_type = server.flavor.volume_type # If the server's volume type conflicts with the provided volume type, # raise an exception. if volume_type_id and \ volume_type.id != util.normalize_volume_type_id(volume_type_id): raise faults.BadRequest("Cannot create a volume with type '%s' to" " a server with volume type '%s'." % (volume_type_id, volume_type.id)) # If the user has not provided a valid volume type, raise an exception. if volume_type is None: volume_type = util.get_volume_type(volume_type_id, include_deleted=False, exception=faults.BadRequest) # We cannot create a non-detachable volume without a server. if server is None: util.assert_detachable_volume_type(volume_type) volume = create_common(user_id, size, name=name, description=description, source_image_id=source_image_id, source_snapshot_id=source_snapshot_id, source_volume_id=source_volume_id, volume_type=volume_type, metadata={}, project_id=project_id, shared_to_project=shared_to_project) if server is not None: server_attachments.attach_volume(server, volume) else: quotas.issue_and_accept_commission(volume, action="BUILD") # If the volume has been created in the DB, consider it available. volume.status = "AVAILABLE" volume.save() return volume
def create(user_id, size, server=None, name=None, description=None, source_volume_id=None, source_snapshot_id=None, source_image_id=None, volume_type_id=None, metadata=None, project_id=None, shared_to_project=False): """Create a new volume and optionally attach it to a server. This function serves as the main entry-point for volume creation. It gets the necessary data either from the API or from an snf-manage command and then feeds that data to the lower-level functions that handle the actual creation of the volume and the server attachments. """ volume_type = None # If given a server id, assert that it exists and that it belongs to the # user. if server: volume_type = server.flavor.volume_type # If the server's volume type conflicts with the provided volume type, # raise an exception. if volume_type_id and \ volume_type.id != util.normalize_volume_type_id(volume_type_id): raise faults.BadRequest("Cannot create a volume with type '%s' to" " a server with volume type '%s'." % (volume_type_id, volume_type.id)) # If the user has not provided a valid volume type, raise an exception. if volume_type is None: volume_type = util.get_volume_type(volume_type_id, include_deleted=False, exception=faults.BadRequest) # We cannot create a non-detachable volume without a server. if server is None: util.assert_detachable_volume_type(volume_type) volume = create_common(user_id, size, name=name, description=description, source_image_id=source_image_id, source_snapshot_id=source_snapshot_id, source_volume_id=source_volume_id, volume_type=volume_type, metadata={}, project_id=project_id, shared_to_project=shared_to_project) if server is not None: server_attachments.attach_volume(server, volume) else: quotas.issue_and_accept_commission(volume, action="BUILD") # If the volume has been created in the DB, consider it available. volume.status = "AVAILABLE" volume.save() return volume
def import_server(instance_name, backend_id, flavor_id, image_id, user_id, new_public_nic, stream=sys.stdout): flavor = common.get_flavor(flavor_id) backend = common.get_backend(backend_id) backend_client = backend.get_client() try: instance = backend_client.GetInstance(instance_name) except GanetiApiError as e: if e.code == 404: raise CommandError("Instance %s does not exist in backend %s" % (instance_name, backend)) else: raise CommandError("Unexpected error" + str(e)) if not new_public_nic: check_instance_nics(instance) shutdown_instance(instance, backend_client, stream=stream) # Create the VM in DB stream.write("Creating VM entry in DB\n") vm = VirtualMachine.objects.create(name=instance_name, backend=backend, userid=user_id, imageid=image_id, flavor=flavor) quotas.issue_and_accept_commission(vm) if new_public_nic: remove_instance_nics(instance, backend_client, stream=stream) # Rename instance rename_instance(instance_name, vm.backend_vm_id, backend_client, stream) if new_public_nic: ports = servers.create_instance_ports(user_id) stream.write("Adding new NICs to server") [servers.associate_port_with_machine(port, vm) for port in ports] [connect_to_network(vm, port) for port in ports] # Startup instance startup_instance(vm.backend_vm_id, backend_client, stream=stream) backend.put_client(backend_client) return
def import_server(instance_name, backend_id, flavor_id, image_id, user_id, new_public_nic, stream=sys.stdout): flavor = common.get_flavor(flavor_id) backend = common.get_backend(backend_id) backend_client = backend.get_client() try: instance = backend_client.GetInstance(instance_name) except GanetiApiError as e: if e.code == 404: raise CommandError("Instance %s does not exist in backend %s" % (instance_name, backend)) else: raise CommandError("Unexpected error" + str(e)) if new_public_nic: remove_instance_nics(instance, backend_client, stream=stream) (network, address) = allocate_public_address(backend) if address is None: raise CommandError("Can not allocate a public address." " No available public network.") nic = {'ip': address, 'network': network.backend_id} add_public_nic(instance_name, nic, backend_client, stream=stream) else: check_instance_nics(instance) shutdown_instance(instance, backend_client, stream=stream) # Create the VM in DB stream.write("Creating VM entry in DB\n") vm = VirtualMachine.objects.create(name=instance_name, backend=backend, userid=user_id, imageid=image_id, flavor=flavor) quotas.issue_and_accept_commission(vm) # Rename instance rename_instance(instance_name, vm.backend_vm_id, backend_client, stream) # Startup instance startup_instance(vm.backend_vm_id, backend_client, stream=stream) backend.put_client(backend_client) return
def delete_floating_ip(floating_ip): # 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 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 delete_floating_ip(floating_ip): # 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 _delete_floating_ip(floating_ip_id, credentials, atomic_context=None): floating_ip = util.get_floating_ip_by_id(credentials, floating_ip_id, for_update=True) validate_ip_action(floating_ip, "DELETE", silent=False) # 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", atomic_context=atomic_context) return floating_ip
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 reassign(server_id, project, shared_to_project, credentials=None, atomic_context=None): vm = util.get_vm(server_id, credentials, for_update=True, non_deleted=True, non_suspended=True) commands.validate_server_action(vm, "REASSIGN") if vm.project == project: if vm.shared_to_project != shared_to_project: log.info("%s VM %s to project %s", "Sharing" if shared_to_project else "Unsharing", vm, project) vm.shared_to_project = shared_to_project vm.volumes.filter(index=0, deleted=False)\ .update(shared_to_project=shared_to_project) vm.save() else: action_fields = {"to_project": project, "from_project": vm.project} log.info("Reassigning VM %s from project %s to %s, shared: %s", vm, vm.project, project, shared_to_project) if not (vm.backend.public or vm.backend.projects.filter(project=project).exists()): raise faults.Forbidden("Cannot reassign VM. Target project " "doesn't have access to the VM's backend.") if not FlavorPolicy.has_access_to_flavor( vm.flavor, credentials, project=project): raise faults.Forbidden("Cannot reassign VM. Target project " "doesn't have access to the VM's flavor.") vm.project = project vm.shared_to_project = shared_to_project vm.save() vm.volumes.filter(index=0, deleted=False)\ .update(project=project, shared_to_project=shared_to_project) quotas.issue_and_accept_commission(vm, action="REASSIGN", action_fields=action_fields, atomic_context=atomic_context) return vm
def _delete_floating_ip(floating_ip_id, credentials, atomic_context=None): floating_ip = util.get_floating_ip_by_id(credentials, floating_ip_id, for_update=True) validate_ip_action(floating_ip, "DELETE", silent=False) # 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", atomic_context=atomic_context) return floating_ip
def delete(volume_id, credentials, atomic_context=None): """Delete a Volume. The canonical way of deleting a volume is to send a command to Ganeti to remove the volume from a specific server. There are two cases however when a volume may not be attached to a server: * Case 1: The volume has been created only in DB and was never attached to a server. In this case, we can simply mark the volume as deleted without using Ganeti to do so. * Case 2: The volume has been detached from a VM. This means that there are still data in the storage backend. Thus, in order to delete the volume safely, we must attach it to a helper VM, thereby handing the delete action to the dispatcher. """ volume = util.get_volume(credentials, volume_id, for_update=True, non_deleted=True) server_id = volume.machine_id if server_id is not None: server = get_vm(server_id) server_attachments.delete_volume(server, volume, atomic_context) log.info("Deleting volume '%s' from server '%s', job: %s", volume.id, server_id, volume.backendjobid) elif volume.backendjobid is None: # Case 1: Uninitialized volume if volume.status not in ("AVAILABLE", "ERROR"): raise faults.BadRequest("Volume is in invalid state: %s" % volume.status) log.debug("Attempting to delete uninitialized volume %s.", volume) util.mark_volume_as_deleted(volume, immediate=True) quotas.issue_and_accept_commission(volume, action="DESTROY", atomic_context=atomic_context) log.info("Deleting uninitialized volume '%s'", volume.id) else: # Case 2: Detached volume log.debug("Attempting to delete detached volume %s", volume) delete_detached_volume(volume, atomic_context) log.info("Deleting volume '%s' from helper server '%s', job: %s", volume.id, volume.machine.id, volume.backendjobid) return volume
def reassign_floating_ip(floating_ip, project, shared_to_project): if floating_ip.project == project: if floating_ip.shared_to_project != shared_to_project: log.info("%s floating_ip %s to project %s", "Sharing" if shared_to_project else "Unsharing", floating_ip, project) floating_ip.shared_to_project = shared_to_project floating_ip.save() else: action_fields = {"to_project": project, "from_project": floating_ip.project} log.info("Reassigning floating_ip %s from project %s to %s, shared: %s", floating_ip, floating_ip.project, project, shared_to_project) floating_ip.project = project floating_ip.shared_to_project = shared_to_project floating_ip.save() quotas.issue_and_accept_commission(floating_ip, action="REASSIGN", action_fields=action_fields) return floating_ip
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 reassign(network_id, project, shared_to_project, credentials, atomic_context=None): network = util.get_network(network_id, credentials, for_update=True, non_deleted=True) if network.public: raise faults.Forbidden("Cannot reassign public network") if not credentials.is_admin and credentials.userid != network.userid: raise faults.Forbidden("Action 'reassign' is allowed only to the owner" " of the network.") validate_network_action(network, "REASSIGN") if network.project == project: if network.shared_to_project != shared_to_project: log.info("%s network %s to project %s", "Sharing" if shared_to_project else "Unsharing", network, project) network.shared_to_project = shared_to_project network.save() else: action_fields = { "to_project": project, "from_project": network.project } log.info("Reassigning network %s from project %s to %s, shared: %s", network, network.project, project, shared_to_project) network.project = project network.shared_to_project = shared_to_project network.save() quotas.issue_and_accept_commission(network, action="REASSIGN", action_fields=action_fields, atomic_context=atomic_context) return network
def reassign(network, project, shared_to_project): if network.project == project: if network.shared_to_project != shared_to_project: log.info("%s network %s to project %s", "Sharing" if shared_to_project else "Unsharing", network, project) network.shared_to_project = shared_to_project network.save() else: action_fields = { "to_project": project, "from_project": network.project } log.info("Reassigning network %s from project %s to %s, shared: %s", network, network.project, project, shared_to_project) network.project = project network.shared_to_project = shared_to_project network.save() quotas.issue_and_accept_commission(network, action="REASSIGN", action_fields=action_fields) return network
def handle(self, *args, **options): if len(args) < 1: raise CommandError("Please provide a network ID") network = get_network(args[0]) self.stdout.write('Trying to remove network: %s\n' % str(network)) if network.machines.exists(): raise CommandError('Network is not empty. Can not delete') network.action = 'DESTROY' network.save() if network.userid: quotas.issue_and_accept_commission(network, delete=True) for bnet in network.backend_networks.exclude(operstate="DELETED"): delete_network(network, bnet.backend) self.stdout.write("Successfully submitted Ganeti jobs to" " remove network %s" % network.backend_id)
def reassign(vm, project, shared_to_project): commands.validate_server_action(vm, "REASSIGN") if vm.project == project: if vm.shared_to_project != shared_to_project: log.info("%s VM %s to project %s", "Sharing" if shared_to_project else "Unsharing", vm, project) vm.shared_to_project = shared_to_project vm.volumes.filter(index=0, deleted=False)\ .update(shared_to_project=shared_to_project) vm.save() else: action_fields = {"to_project": project, "from_project": vm.project} log.info("Reassigning VM %s from project %s to %s, shared: %s", vm, vm.project, project, shared_to_project) vm.project = project vm.shared_to_project = shared_to_project vm.save() vm.volumes.filter(index=0, deleted=False).update(project=project, shared_to_project=shared_to_project) quotas.issue_and_accept_commission(vm, action="REASSIGN", action_fields=action_fields) return vm
def reassign(server_id, project, shared_to_project, credentials=None, atomic_context=None): vm = util.get_vm(server_id, credentials, for_update=True, non_deleted=True, non_suspended=True) commands.validate_server_action(vm, "REASSIGN") if vm.project == project: if vm.shared_to_project != shared_to_project: log.info("%s VM %s to project %s", "Sharing" if shared_to_project else "Unsharing", vm, project) vm.shared_to_project = shared_to_project vm.volumes.filter(index=0, deleted=False)\ .update(shared_to_project=shared_to_project) vm.save() else: action_fields = {"to_project": project, "from_project": vm.project} log.info("Reassigning VM %s from project %s to %s, shared: %s", vm, vm.project, project, shared_to_project) if not (vm.backend.public or vm.backend.projects.filter(project=project).exists()): raise faults.Forbidden("Cannot reassign VM. Target project " "doesn't have access to the VM's backend.") if not FlavorPolicy.has_access_to_flavor(vm.flavor, credentials, project=project): raise faults.Forbidden("Cannot reassign VM. Target project " "doesn't have access to the VM's flavor.") vm.project = project vm.shared_to_project = shared_to_project vm.save() vm.volumes.filter(index=0, deleted=False)\ .update(project=project, shared_to_project=shared_to_project) quotas.issue_and_accept_commission(vm, action="REASSIGN", action_fields=action_fields, atomic_context=atomic_context) return vm
def reassign_floating_ip(floating_ip_id, project, shared_to_project, credentials, atomic_context=None): floating_ip = util.get_floating_ip_by_id(credentials, floating_ip_id, for_update=True) if not credentials.is_admin and credentials.userid != floating_ip.userid: raise faults.Forbidden("Action 'reassign' is allowed only to the owner" " of the floating IP.") validate_ip_action(floating_ip, "REASSIGN", silent=False) if floating_ip.project == project: if floating_ip.shared_to_project != shared_to_project: log.info("%s floating_ip %s to project %s", "Sharing" if shared_to_project else "Unsharing", floating_ip, project) floating_ip.shared_to_project = shared_to_project floating_ip.save() else: action_fields = { "to_project": project, "from_project": floating_ip.project } log.info( "Reassigning floating_ip %s from project %s to %s, shared: %s", floating_ip, floating_ip.project, project, shared_to_project) floating_ip.project = project floating_ip.shared_to_project = shared_to_project floating_ip.save() quotas.issue_and_accept_commission(floating_ip, action="REASSIGN", action_fields=action_fields, atomic_context=atomic_context) return floating_ip
def create(userid, name, flavor, link=None, mac_prefix=None, mode=None, floating_ip_pool=False, tags=None, public=False, drained=False): if flavor is None: raise faults.BadRequest("Missing request parameter 'type'") elif flavor not in Network.FLAVORS.keys(): raise faults.BadRequest("Invalid network type '%s'" % flavor) if mac_prefix is not None and flavor == "MAC_FILTERED": raise faults.BadRequest("Cannot override MAC_FILTERED mac-prefix") if link is not None and flavor == "PHYSICAL_VLAN": raise faults.BadRequest("Cannot override PHYSICAL_VLAN link") utils.check_name_length(name, Network.NETWORK_NAME_LENGTH, "Network name " "is too long") try: fmode, flink, fmac_prefix, ftags = util.values_from_flavor(flavor) except EmptyPool: log.error("Failed to allocate resources for network of type: %s", flavor) msg = "Failed to allocate resources for network." raise faults.ServiceUnavailable(msg) mode = mode or fmode link = link or flink mac_prefix = mac_prefix or fmac_prefix tags = tags or ftags validate_mac(mac_prefix + "0:00:00:00") # Check that given link is unique! if (link is not None and flavor == "IP_LESS_ROUTED" and Network.objects.filter(deleted=False, mode=mode, link=link).exists()): msg = "Link '%s' is already used." % link raise faults.BadRequest(msg) network = Network.objects.create( name=name, userid=userid, flavor=flavor, mode=mode, link=link, mac_prefix=mac_prefix, tags=tags, public=public, external_router=public, floating_ip_pool=floating_ip_pool, action='CREATE', state='ACTIVE', drained=drained) if link is None: network.link = "%slink-%d" % (settings.BACKEND_PREFIX_ID, network.id) network.save() # Issue commission to Quotaholder and accept it since at the end of # this transaction the Network object will be created in the DB. # Note: the following call does a commit! if not public: quotas.issue_and_accept_commission(network) return network
def _db_create_server(credentials, name, flavor, image, metadata, networks, use_backend, project, volumes, helper, shared_to_project, key_names, atomic_context=None): rescue_properties = RescueProperties() try: rescue_properties.os = image["metadata"].get("OSFAMILY", '') rescue_properties.os_family = image["metadata"].get("OS", '') except KeyError as e: log.error("Failed to parse iamge info: %s", e) rescue_properties.save() # Create the ports for the server ports = create_instance_ports(credentials, networks) # We must save the VM instance now, so that it gets a valid # vm.backend_vm_id. vm = VirtualMachine.objects.create(name=name, backend=use_backend, userid=credentials.userid, project=project, shared_to_project=shared_to_project, imageid=image["id"], image_version=image["version"], key_names=json.dumps(key_names), flavor=flavor, operstate="BUILD", rescue_properties=rescue_properties, helper=helper) log.info("Created entry in DB for VM '%s'", vm) # Associate the ports with the server for index, port in enumerate(ports): associate_port_with_machine(port, vm) port.index = index port.save() # Create instance volumes server_vtype = flavor.volume_type server_volumes = [] for index, vol_info in enumerate(volumes): if vol_info["source_type"] == "volume": uuid = vol_info["source_uuid"] v = get_volume(credentials, uuid, for_update=True, non_deleted=True, exception=faults.BadRequest) if v.volume_type_id != server_vtype.id: msg = ("Volume '%s' has type '%s' while flavor's volume type" " is '%s'" % (v.id, v.volume_type_id, server_vtype.id)) raise faults.BadRequest(msg) if v.status != "AVAILABLE": raise faults.BadRequest("Cannot use volume while it is in %s" " status" % v.status) v.delete_on_termination = vol_info["delete_on_termination"] else: v = _create_volume(user_id=credentials.userid, volume_type=server_vtype, project=project, index=index, shared_to_project=shared_to_project, **vol_info) assign_volume_to_server(vm, v, index=index) server_volumes.append(v) # Create instance metadata for key, val in metadata.items(): utils.check_name_length(key, VirtualMachineMetadata.KEY_LENGTH, "Metadata key is too long") utils.check_name_length(val, VirtualMachineMetadata.VALUE_LENGTH, "Metadata value is too long") VirtualMachineMetadata.objects.create(meta_key=key, meta_value=val, vm=vm) quotas.issue_and_accept_commission(vm, action="BUILD", atomic_context=atomic_context) return (vm.id, [port.id for port in ports ], [volume.id for volume in server_volumes], {v.id: v.origin_size for v in server_volumes})
def update_network_state(network): """Update the state of a Network based on BackendNetwork states. Update the state of a Network based on the operstate of the networks in the backends that network exists. The state of the network is: * ACTIVE: If it is 'ACTIVE' in at least one backend. * DELETED: If it is is 'DELETED' in all backends that have been created. This function also releases the resources (MAC prefix or Bridge) and the quotas for the network. """ if network.deleted: # Network has already been deleted. Just assert that state is also # DELETED if not network.state == "DELETED": network.state = "DELETED" network.save() return backend_states = [s.operstate for s in network.backend_networks.all()] if not backend_states and network.action != "DESTROY": if network.state != "ACTIVE": network.state = "ACTIVE" network.save() return # Network is deleted when all BackendNetworks go to "DELETED" operstate deleted = reduce(lambda x, y: x == y and "DELETED", backend_states, "DELETED") # Release the resources on the deletion of the Network if deleted: if network.ips.filter(deleted=False, floating_ip=True).exists(): msg = "Cannot delete network %s! Floating IPs still in use!" log.error(msg % network) raise Exception(msg % network) log.info("Network %r deleted. Releasing link %r mac_prefix %r", network.id, network.mac_prefix, network.link) network.deleted = True network.state = "DELETED" # Undrain the network, otherwise the network state will remain # as 'SNF:DRAINED' network.drained = False if network.mac_prefix: if network.FLAVORS[network.flavor]["mac_prefix"] == "pool": release_resource(res_type="mac_prefix", value=network.mac_prefix) if network.link: if network.FLAVORS[network.flavor]["link"] == "pool": release_resource(res_type="bridge", value=network.link) # Set all subnets as deleted network.subnets.update(deleted=True) # And delete the IP pools for subnet in network.subnets.all(): if subnet.ipversion == 4: subnet.ip_pools.all().delete() # And all the backend networks since there are useless network.backend_networks.all().delete() # Issue commission if network.userid: quotas.issue_and_accept_commission(network, action="DESTROY") # the above has already saved the object and committed; # a second save would override others' changes, since the # object is now unlocked return elif not network.public: log.warning("Network %s does not have an owner!", network.id) network.save()
def update_network_state(network): """Update the state of a Network based on BackendNetwork states. Update the state of a Network based on the operstate of the networks in the backends that network exists. The state of the network is: * ACTIVE: If it is 'ACTIVE' in at least one backend. * DELETED: If it is is 'DELETED' in all backends that have been created. This function also releases the resources (MAC prefix or Bridge) and the quotas for the network. """ if network.deleted: # Network has already been deleted. Just assert that state is also # DELETED if not network.state == "DELETED": network.state = "DELETED" network.save() return backend_states = [s.operstate for s in network.backend_networks.all()] if not backend_states and network.action != "DESTROY": if network.state != "ACTIVE": network.state = "ACTIVE" network.save() return # Network is deleted when all BackendNetworks go to "DELETED" operstate deleted = reduce(lambda x, y: x == y and "DELETED", backend_states, "DELETED") # Release the resources on the deletion of the Network if deleted: if network.ips.filter(deleted=False, floating_ip=True).exists(): msg = "Cannot delete network %s! Floating IPs still in use!" log.error(msg % network) raise Exception(msg % network) log.info("Network %r deleted. Releasing link %r mac_prefix %r", network.id, network.mac_prefix, network.link) network.deleted = True network.state = "DELETED" # Undrain the network, otherwise the network state will remain # as 'SNF:DRAINED' network.drained = False if network.mac_prefix: if network.FLAVORS[network.flavor]["mac_prefix"] == "pool": release_resource(res_type="mac_prefix", value=network.mac_prefix) if network.link: if network.FLAVORS[network.flavor]["link"] == "pool": release_resource(res_type="bridge", value=network.link) # Set all subnets as deleted network.subnets.update(deleted=True) # And delete the IP pools for subnet in network.subnets.all(): if subnet.ipversion == 4: subnet.ip_pools.all().delete() # And all the backend networks since there are useless network.backend_networks.all().delete() # Issue commission if network.userid: quotas.issue_and_accept_commission(network, action="DESTROY") # the above has already saved the object and committed; # a second save would override others' changes, since the # object is now unlocked return elif not network.public: log.warning("Network %s does not have an owner!", network.id) network.save()
def create(userid, name, flavor, link=None, mac_prefix=None, mode=None, floating_ip_pool=False, tags=None, public=False, drained=False, project=None, shared_to_project=False): if flavor is None: raise faults.BadRequest("Missing request parameter 'type'") elif flavor not in Network.FLAVORS.keys(): raise faults.BadRequest("Invalid network type '%s'" % flavor) if mac_prefix is not None and flavor == "MAC_FILTERED": raise faults.BadRequest("Cannot override MAC_FILTERED mac-prefix") if link is not None and flavor == "PHYSICAL_VLAN": raise faults.BadRequest("Cannot override PHYSICAL_VLAN link") utils.check_name_length(name, Network.NETWORK_NAME_LENGTH, "Network name " "is too long") try: fmode, flink, fmac_prefix, ftags = util.values_from_flavor(flavor) except EmptyPool: log.error("Failed to allocate resources for network of type: %s", flavor) msg = "Failed to allocate resources for network." raise faults.ServiceUnavailable(msg) mode = mode or fmode link = link or flink mac_prefix = mac_prefix or fmac_prefix tags = tags or ftags validate_mac(mac_prefix + "0:00:00:00") # Check that given link is unique! if (link is not None and flavor == "IP_LESS_ROUTED" and Network.objects.filter(deleted=False, mode=mode, link=link).exists()): msg = "Link '%s' is already used." % link raise faults.BadRequest(msg) if project is None: project = userid network = Network.objects.create(name=name, userid=userid, project=project, shared_to_project=shared_to_project, flavor=flavor, mode=mode, link=link, mac_prefix=mac_prefix, tags=tags, public=public, external_router=public, floating_ip_pool=floating_ip_pool, action='CREATE', state='ACTIVE', drained=drained) if link is None: network.link = "%slink-%d" % (settings.BACKEND_PREFIX_ID, network.id) network.save() # Issue commission to Quotaholder and accept it since at the end of # this transaction the Network object will be created in the DB. # Note: the following call does a commit! if not public: quotas.issue_and_accept_commission(network) return network
def handle(self, *args, **options): if args: raise CommandError("Command doesn't accept any arguments") dry_run = options["dry_run"] name = options['name'] subnet = options['subnet'] backend_id = options['backend_id'] public = options['public'] flavor = options['flavor'] mode = options['mode'] link = options['link'] mac_prefix = options['mac_prefix'] tags = options['tags'] userid = options["owner"] if not name: raise CommandError("Name is required") if not subnet: raise CommandError("Subnet is required") if not flavor: raise CommandError("Flavor is required") if public and not backend_id: raise CommandError("backend-id is required") if not userid and not public: raise CommandError("'owner' is required for private networks") if mac_prefix and flavor == "MAC_FILTERED": raise CommandError("Can not override MAC_FILTERED mac-prefix") if link and flavor == "PHYSICAL_VLAN": raise CommandError("Can not override PHYSICAL_VLAN link") if backend_id: backend = get_backend(backend_id) fmode, flink, fmac_prefix, ftags = values_from_flavor(flavor) mode = mode or fmode link = link or flink mac_prefix = mac_prefix or fmac_prefix tags = tags or ftags try: validate_mac(mac_prefix + "0:00:00:00") except InvalidMacAddress: raise CommandError("Invalid MAC prefix '%s'" % mac_prefix) subnet, gateway, subnet6, gateway6 = validate_network_info(options) if not link or not mode: raise CommandError("Can not create network." " No connectivity link or mode") netinfo = { "name": name, "userid": options["owner"], "subnet": subnet, "gateway": gateway, "gateway6": gateway6, "subnet6": subnet6, "dhcp": options["dhcp"], "flavor": flavor, "public": public, "mode": mode, "link": link, "mac_prefix": mac_prefix, "tags": tags, "state": "ACTIVE"} if dry_run: self.stdout.write("Creating network:\n") pprint_table(self.stdout, tuple(netinfo.items())) return network = Network.objects.create(**netinfo) if userid: quotas.issue_and_accept_commission(network) if backend_id: # Create BackendNetwork only to the specified Backend network.create_backend_network(backend) create_network(network=network, backend=backend, connect=True)
def do_create_server(userid, name, password, flavor, image, metadata={}, personality=[], network=None, backend=None): # Fix flavor for archipelago disk_template, provider = util.get_flavor_provider(flavor) if provider: flavor.disk_template = disk_template flavor.disk_provider = provider flavor.disk_origin = image['checksum'] image['backend_id'] = 'null' else: flavor.disk_provider = None flavor.disk_origin = None try: if backend is None: # Allocate backend to host the server. backend_allocator = BackendAllocator() backend = backend_allocator.allocate(userid, flavor) if backend is None: log.error("No available backend for VM with flavor %s", flavor) raise faults.ServiceUnavailable("No available backends") if network is None: # Allocate IP from public network (network, address) = util.get_public_ip(backend) else: address = util.get_network_free_address(network) # We must save the VM instance now, so that it gets a valid # vm.backend_vm_id. vm = VirtualMachine.objects.create( name=name, backend=backend, userid=userid, imageid=image["id"], flavor=flavor, action="CREATE") log.info("Created entry in DB for VM '%s'", vm) # Create VM's public NIC. Do not wait notification form ganeti hooks to # create this NIC, because if the hooks never run (e.g. building error) # the VM's public IP address will never be released! nic = NetworkInterface.objects.create(machine=vm, network=network, index=0, ipv4=address, state="BUILDING") # Also we must create the VM metadata in the same transaction. for key, val in metadata.items(): VirtualMachineMetadata.objects.create( meta_key=key, meta_value=val, vm=vm) # Issue commission to Quotaholder and accept it since at the end of # this transaction the VirtualMachine object will be created in the DB. # Note: the following call does a commit! quotas.issue_and_accept_commission(vm) except: transaction.rollback() raise else: transaction.commit() try: vm = VirtualMachine.objects.select_for_update().get(id=vm.id) # dispatch server created signal needed to trigger the 'vmapi', which # enriches the vm object with the 'config_url' attribute which must be # passed to the Ganeti job. server_created.send(sender=vm, created_vm_params={ 'img_id': image['backend_id'], 'img_passwd': password, 'img_format': str(image['format']), 'img_personality': json.dumps(personality), 'img_properties': json.dumps(image['metadata']), }) jobID = create_instance(vm, nic, flavor, image) # At this point the job is enqueued in the Ganeti backend vm.backendopcode = "OP_INSTANCE_CREATE" vm.backendjobid = jobID vm.save() transaction.commit() log.info("User %s created VM %s, NIC %s, Backend %s, JobID %s", userid, vm, nic, backend, str(jobID)) except: # If an exception is raised, then the user will never get the VM id. # In order to delete it from DB and release it's resources, we # mock a successful OP_INSTANCE_REMOVE job. process_op_status(vm=vm, etime=datetime.datetime.now(), jobid=-0, opcode="OP_INSTANCE_REMOVE", status="success", logmsg="Reconciled eventd: VM creation failed.") raise return vm
def create_network(request): # Normal Response Code: 202 # Error Response Codes: computeFault (400, 500), # serviceUnavailable (503), # unauthorized (401), # badMediaType(415), # badRequest (400), # forbidden (403) # overLimit (413) try: req = utils.get_request_dict(request) log.info('create_network %s', req) user_id = request.user_uniq try: d = req['network'] name = d['name'] except KeyError: raise faults.BadRequest("Malformed request") # Get and validate flavor. Flavors are still exposed as 'type' in the # API. flavor = d.get("type", None) if flavor is None: raise faults.BadRequest("Missing request parameter 'type'") elif flavor not in Network.FLAVORS.keys(): raise faults.BadRequest("Invalid network type '%s'" % flavor) elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS: raise faults.Forbidden("Can not create network of type '%s'" % flavor) public = d.get("public", False) if public: raise faults.Forbidden("Can not create a public network.") dhcp = d.get('dhcp', True) # Get and validate network parameters subnet = d.get('cidr', '192.168.1.0/24') subnet6 = d.get('cidr6', None) gateway = d.get('gateway', None) gateway6 = d.get('gateway6', None) # Check that user provided a valid subnet util.validate_network_params(subnet, gateway, subnet6, gateway6) try: mode, link, mac_prefix, tags = util.values_from_flavor(flavor) validate_mac(mac_prefix + "0:00:00:00") network = Network.objects.create( name=name, userid=user_id, subnet=subnet, subnet6=subnet6, gateway=gateway, gateway6=gateway6, dhcp=dhcp, flavor=flavor, mode=mode, link=link, mac_prefix=mac_prefix, tags=tags, action='CREATE', state='ACTIVE') except EmptyPool: log.error("Failed to allocate resources for network of type: %s", flavor) raise faults.ServiceUnavailable("Failed to allocate network" " resources") # Issue commission to Quotaholder and accept it since at the end of # this transaction the Network object will be created in the DB. # Note: the following call does a commit! quotas.issue_and_accept_commission(network) except: transaction.rollback() raise else: transaction.commit() networkdict = network_to_dict(network, request.user_uniq) response = render_network(request, networkdict, status=202) return response
def handle(self, *args, **options): if args: raise CommandError("Command doesn't accept any arguments") dry_run = options["dry_run"] name = options['name'] subnet = options['subnet'] backend_id = options['backend_id'] public = options['public'] flavor = options['flavor'] mode = options['mode'] link = options['link'] mac_prefix = options['mac_prefix'] tags = options['tags'] userid = options["owner"] if not name: raise CommandError("Name is required") if not subnet: raise CommandError("Subnet is required") if not flavor: raise CommandError("Flavor is required") if public and not backend_id: raise CommandError("backend-id is required") if not userid and not public: raise CommandError("'owner' is required for private networks") if mac_prefix and flavor == "MAC_FILTERED": raise CommandError("Can not override MAC_FILTERED mac-prefix") if link and flavor == "PHYSICAL_VLAN": raise CommandError("Can not override PHYSICAL_VLAN link") if backend_id: backend = get_backend(backend_id) fmode, flink, fmac_prefix, ftags = values_from_flavor(flavor) mode = mode or fmode link = link or flink mac_prefix = mac_prefix or fmac_prefix tags = tags or ftags try: validate_mac(mac_prefix + "0:00:00:00") except InvalidMacAddress: raise CommandError("Invalid MAC prefix '%s'" % mac_prefix) subnet, gateway, subnet6, gateway6 = validate_network_info(options) if not link or not mode: raise CommandError("Can not create network." " No connectivity link or mode") netinfo = { "name": name, "userid": options["owner"], "subnet": subnet, "gateway": gateway, "gateway6": gateway6, "subnet6": subnet6, "dhcp": options["dhcp"], "flavor": flavor, "public": public, "mode": mode, "link": link, "mac_prefix": mac_prefix, "tags": tags, "state": "ACTIVE" } if dry_run: self.stdout.write("Creating network:\n") pprint_table(self.stdout, tuple(netinfo.items())) return network = Network.objects.create(**netinfo) if userid: quotas.issue_and_accept_commission(network) if backend_id: # Create BackendNetwork only to the specified Backend network.create_backend_network(backend) create_network(network=network, backend=backend, connect=True)
def create_network(request): # Normal Response Code: 202 # Error Response Codes: computeFault (400, 500), # serviceUnavailable (503), # unauthorized (401), # badMediaType(415), # badRequest (400), # forbidden (403) # overLimit (413) req = utils.get_request_dict(request) log.info('create_network %s', req) user_id = request.user_uniq try: d = req['network'] name = d['name'] except KeyError: raise faults.BadRequest("Malformed request") # Get and validate flavor. Flavors are still exposed as 'type' in the # API. flavor = d.get("type", None) if flavor is None: raise faults.BadRequest("Missing request parameter 'type'") elif flavor not in Network.FLAVORS.keys(): raise faults.BadRequest("Invalid network type '%s'" % flavor) elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS: raise faults.Forbidden("Can not create network of type '%s'" % flavor) public = d.get("public", False) if public: raise faults.Forbidden("Can not create a public network.") dhcp = d.get('dhcp', True) # Get and validate network parameters subnet = d.get('cidr', '192.168.1.0/24') subnet6 = d.get('cidr6', None) gateway = d.get('gateway', None) gateway6 = d.get('gateway6', None) # Check that user provided a valid subnet util.validate_network_params(subnet, gateway, subnet6, gateway6) try: mode, link, mac_prefix, tags = util.values_from_flavor(flavor) validate_mac(mac_prefix + "0:00:00:00") network = Network.objects.create( name=name, userid=user_id, subnet=subnet, subnet6=subnet6, gateway=gateway, gateway6=gateway6, dhcp=dhcp, flavor=flavor, mode=mode, link=link, mac_prefix=mac_prefix, tags=tags, action='CREATE', state='ACTIVE') except EmptyPool: log.error("Failed to allocate resources for network of type: %s", flavor) raise faults.ServiceUnavailable("Failed to allocate network resources") # Issue commission to Quotaholder and accept it since at the end of # this transaction the Network object will be created in the DB. # Note: the following call does a commit! quotas.issue_and_accept_commission(network) networkdict = network_to_dict(network, request.user_uniq) response = render_network(request, networkdict, status=202) return response
def _db_create_server( credentials, name, flavor, image, metadata, networks, use_backend, project, volumes, helper, shared_to_project, key_names, atomic_context=None): rescue_properties = RescueProperties() try: rescue_properties.os = image["metadata"].get("OSFAMILY", '') rescue_properties.os_family = image["metadata"].get("OS", '') except KeyError as e: log.error("Failed to parse iamge info: %s", e) rescue_properties.save() # Create the ports for the server ports = create_instance_ports(credentials, networks) # We must save the VM instance now, so that it gets a valid # vm.backend_vm_id. vm = VirtualMachine.objects.create(name=name, backend=use_backend, userid=credentials.userid, project=project, shared_to_project=shared_to_project, imageid=image["id"], image_version=image["version"], key_names=json.dumps(key_names), flavor=flavor, operstate="BUILD", rescue_properties=rescue_properties, helper=helper) log.info("Created entry in DB for VM '%s'", vm) # Associate the ports with the server for index, port in enumerate(ports): associate_port_with_machine(port, vm) port.index = index port.save() # Create instance volumes server_vtype = flavor.volume_type server_volumes = [] for index, vol_info in enumerate(volumes): if vol_info["source_type"] == "volume": uuid = vol_info["source_uuid"] v = get_volume(credentials, uuid, for_update=True, non_deleted=True, exception=faults.BadRequest) if v.volume_type_id != server_vtype.id: msg = ("Volume '%s' has type '%s' while flavor's volume type" " is '%s'" % (v.id, v.volume_type_id, server_vtype.id)) raise faults.BadRequest(msg) if v.status != "AVAILABLE": raise faults.BadRequest("Cannot use volume while it is in %s" " status" % v.status) v.delete_on_termination = vol_info["delete_on_termination"] else: v = _create_volume(user_id=credentials.userid, volume_type=server_vtype, project=project, index=index, shared_to_project=shared_to_project, **vol_info) assign_volume_to_server(vm, v, index=index) server_volumes.append(v) # Create instance metadata for key, val in metadata.items(): utils.check_name_length(key, VirtualMachineMetadata.KEY_LENGTH, "Metadata key is too long") utils.check_name_length(val, VirtualMachineMetadata.VALUE_LENGTH, "Metadata value is too long") VirtualMachineMetadata.objects.create( meta_key=key, meta_value=val, vm=vm) quotas.issue_and_accept_commission(vm, action="BUILD", atomic_context=atomic_context) return (vm.id, [port.id for port in ports], [volume.id for volume in server_volumes], {v.id: v.origin_size for v in server_volumes})