def _rename(network, name): validate_network_action(network, "RENAME") utils.check_name_length(name, Network.NETWORK_NAME_LENGTH, "Network name " "is too long") network.name = name network.save() return network
def create(userid, name, password, flavor, image, metadata={}, personality=[], networks=None, use_backend=None): if use_backend is None: # Allocate server to a Ganeti backend use_backend = allocate_new_server(userid, flavor) utils.check_name_length(name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") # Create the ports for the server ports = create_instance_ports(userid, networks) # 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 = None if provider in settings.GANETI_CLONE_PROVIDERS: flavor.disk_origin = image['checksum'] image['backend_id'] = 'null' else: flavor.disk_provider = None # 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=userid, imageid=image["id"], flavor=flavor, operstate="BUILD") 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() for key, val in metadata.items(): VirtualMachineMetadata.objects.create(meta_key=key, meta_value=val, vm=vm) # Create the server in Ganeti. vm = create_server(vm, ports, flavor, image, personality, password) return vm
def create_common(user_id, size, name=None, description=None, source_volume_id=None, source_snapshot_id=None, source_image_id=None, volume_type=None, metadata=None, project_id=None, shared_to_project=False): """Common tasks and checks for the creation of a new volume. This function processes the necessary arguments in order to call the `_create_volume` function, which creates the volume in the DB. The main duty of `create_common` is to handle the metadata creation of the volume and to update the quota of the user. """ # Assert that not more than one source are used sources = filter(lambda x: x is not None, [source_volume_id, source_snapshot_id, source_image_id]) if len(sources) > 1: raise faults.BadRequest("Volume can not have more than one source!") if source_volume_id is not None: source_type = "volume" source_uuid = source_volume_id elif source_snapshot_id is not None: source_type = "snapshot" source_uuid = source_snapshot_id elif source_image_id is not None: source_type = "image" source_uuid = source_image_id else: source_type = "blank" source_uuid = None if project_id is None: project_id = user_id if metadata is not None and \ len(metadata) > settings.CYCLADES_VOLUME_MAX_METADATA: raise faults.BadRequest("Volumes cannot have more than %s metadata " "items" % settings.CYCLADES_VOLUME_MAX_METADATA) volume = _create_volume(user_id, project_id, size, source_type, source_uuid, volume_type=volume_type, name=name, description=description, index=None, shared_to_project=shared_to_project) if metadata is not None: for meta_key, meta_val in metadata.items(): utils.check_name_length(meta_key, VolumeMetadata.KEY_LENGTH, "Metadata key is too long") utils.check_name_length(meta_val, VolumeMetadata.VALUE_LENGTH, "Metadata value is too long") volume.metadata.create(key=meta_key, value=meta_val) return volume
def rename(server, new_name): """Rename a VirtualMachine.""" utils.check_name_length(new_name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") old_name = server.name server.name = new_name server.save() log.info("Renamed server '%s' from '%s' to '%s'", server, old_name, new_name) return server
def update_volume_metadata(request, volume_id, reset=False): req = utils.get_json_body(request) log.debug("User: %s, Volume: %s Action: update_metadata, Request: %s", request.user_uniq, volume_id, req) meta_dict = utils.get_attribute(req, "metadata", required=True, attr_type=dict) for key, value in meta_dict.items(): check_name_length(key, VolumeMetadata.KEY_LENGTH, "Metadata key is too long.") check_name_length(value, VolumeMetadata.VALUE_LENGTH, "Metadata value is too long.") volume = util.get_volume(request.user_uniq, request.user_projects, volume_id, for_update=True, non_deleted=True) if reset: if len(meta_dict) > settings.CYCLADES_VOLUME_MAX_METADATA: raise faults.BadRequest("Volumes cannot have more than %s metadata" " items" % settings.CYCLADES_VOLUME_MAX_METADATA) volume.metadata.all().delete() for key, value in meta_dict.items(): volume.metadata.create(key=key, value=value) else: if len(meta_dict) + len(volume.metadata.all()) - \ len(volume.metadata.all().filter(key__in=meta_dict.keys())) > \ settings.CYCLADES_VOLUME_MAX_METADATA: raise faults.BadRequest("Volumes cannot have more than %s metadata" " items" % settings.CYCLADES_VOLUME_MAX_METADATA) for key, value in meta_dict.items(): try: # Update existing metadata meta = volume.metadata.get(key=key) meta.value = value meta.save() except VolumeMetadata.DoesNotExist: # Or create a new one volume.metadata.create(key=key, value=value) log.info("User %s updated metadata for volume %s", request.user_uniq, volume.id) metadata = volume.metadata.values_list('key', 'value') data = json.dumps({"metadata": dict(metadata)}) return HttpResponse(data, content_type="application/json", status=200)
def rename(server_id, new_name, credentials=None): """Rename a VirtualMachine.""" server = util.get_vm(server_id, credentials, for_update=True, non_deleted=True, non_suspended=True) utils.check_name_length(new_name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") old_name = server.name server.name = new_name server.save() log.info("Renamed server '%s' from '%s' to '%s'", server, old_name, new_name) return server
def create(userid, name, password, flavor, image, metadata={}, personality=[], networks=None, use_backend=None): if use_backend is None: # Allocate server to a Ganeti backend use_backend = allocate_new_server(userid, flavor) utils.check_name_length(name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") # Create the ports for the server ports = create_instance_ports(userid, networks) # 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 = None if provider in settings.GANETI_CLONE_PROVIDERS: flavor.disk_origin = image['checksum'] image['backend_id'] = 'null' else: flavor.disk_provider = None # 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=userid, imageid=image["id"], flavor=flavor, operstate="BUILD") 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() for key, val in metadata.items(): VirtualMachineMetadata.objects.create( meta_key=key, meta_value=val, vm=vm) # Create the server in Ganeti. vm = create_server(vm, ports, flavor, image, personality, password) return vm
def update(volume, name=None, description=None, delete_on_termination=None): if name is not None: utils.check_name_length(name, Volume.NAME_LENGTH, "Volume name is too long") volume.name = name if description is not None: utils.check_name_length(description, Volume.DESCRIPTION_LENGTH, "Volume description is too long") volume.description = description if delete_on_termination is not None: validate_volume_termination(volume.volume_type, delete_on_termination) volume.delete_on_termination = delete_on_termination volume.save() return volume
def update_volume_metadata(request, volume_id, reset=False): credentials = request.credentials req = utils.get_json_body(request) log.debug("User: %s, Volume: %s Action: update_metadata, Request: %s", credentials.userid, volume_id, req) meta_dict = utils.get_attribute(req, "metadata", required=True, attr_type=dict) for key, value in meta_dict.items(): check_name_length(key, VolumeMetadata.KEY_LENGTH, "Metadata key is too long.") check_name_length(value, VolumeMetadata.VALUE_LENGTH, "Metadata value is too long.") volume = util.get_volume(request.credentials, volume_id, for_update=True, non_deleted=True) if reset: if len(meta_dict) > settings.CYCLADES_VOLUME_MAX_METADATA: raise faults.BadRequest("Volumes cannot have more than %s metadata" " items" % settings.CYCLADES_VOLUME_MAX_METADATA) volume.metadata.all().delete() for key, value in meta_dict.items(): volume.metadata.create(key=key, value=value) else: if len(meta_dict) + len(volume.metadata.all()) - \ len(volume.metadata.all().filter(key__in=meta_dict.keys())) > \ settings.CYCLADES_VOLUME_MAX_METADATA: raise faults.BadRequest("Volumes cannot have more than %s metadata" " items" % settings.CYCLADES_VOLUME_MAX_METADATA) for key, value in meta_dict.items(): try: # Update existing metadata meta = volume.metadata.get(key=key) meta.value = value meta.save() except VolumeMetadata.DoesNotExist: # Or create a new one volume.metadata.create(key=key, value=value) log.info("User %s updated metadata for volume %s", credentials.userid, volume.id) metadata = volume.metadata.values_list('key', 'value') data = json.dumps({"metadata": dict(metadata)}) return HttpResponse(data, content_type="application/json", status=200)
def update(volume_id, name=None, description=None, delete_on_termination=None, credentials=None): volume = util.get_volume(credentials, volume_id, for_update=True, non_deleted=True) if name is not None: utils.check_name_length(name, Volume.NAME_LENGTH, "Volume name is too long") volume.name = name if description is not None: utils.check_name_length(description, Volume.DESCRIPTION_LENGTH, "Volume description is too long") volume.description = description if delete_on_termination is not None: validate_volume_termination(volume.volume_type, delete_on_termination) volume.delete_on_termination = delete_on_termination volume.save() return volume
def update_subnet(sub_id, name, user_id): """Update the fields of a subnet Only the name can be updated """ log.info('Update subnet %s, name %s' % (sub_id, name)) try: subnet = Subnet.objects.get(id=sub_id) except: raise api.faults.ItemNotFound("Subnet not found") if user_id != subnet.network.userid: raise api.faults.Forbidden("Forbidden operation") utils.check_name_length(name, Subnet.SUBNET_NAME_LENGTH, "Subnet name is " " too long") subnet.name = name subnet.save() return subnet
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 _create_volume(server, user_id, project, size, source_type, source_uuid, volume_type, name=None, description=None, index=None, delete_on_termination=True): utils.check_name_length(name, Volume.NAME_LENGTH, "Volume name is too long") utils.check_name_length(description, Volume.DESCRIPTION_LENGTH, "Volume description is too long") validate_volume_termination(volume_type, delete_on_termination) if index is None: # Counting a server's volumes is safe, because we have an # X-lock on the server. index = server.volumes.filter(deleted=False).count() if size is not None: try: size = int(size) except (TypeError, ValueError): raise faults.BadRequest("Volume 'size' needs to be a positive" " integer value.") if size < 1: raise faults.BadRequest("Volume size must be a positive integer") if size > settings.CYCLADES_VOLUME_MAX_SIZE: raise faults.BadRequest("Maximum volume size is '%sGB'" % settings.CYCLADES_VOLUME_MAX_SIZE) # Only ext_ disk template supports cloning from another source. Otherwise # is must be the root volume so that 'snf-image' fill the volume can_have_source = (index == 0 or volume_type.provider in settings.GANETI_CLONE_PROVIDERS) if not can_have_source and source_type != "blank": msg = ("Cannot specify a 'source' attribute for volume type '%s' with" " disk template '%s'" % (volume_type.id, volume_type.disk_template)) raise faults.BadRequest(msg) source_version = None origin_size = None # TODO: Check Volume/Snapshot Status if source_type == "snapshot": source_snapshot = util.get_snapshot(user_id, source_uuid, exception=faults.BadRequest) snap_status = source_snapshot.get("status", "").upper() if snap_status != OBJECT_AVAILABLE: raise faults.BadRequest("Cannot create volume from snapshot, while" " snapshot is in '%s' status" % snap_status) source = Volume.prefix_source(source_uuid, source_type="snapshot") if size is None: raise faults.BadRequest("Volume size is required") elif (size << 30) < int(source_snapshot["size"]): raise faults.BadRequest("Volume size '%s' is smaller than" " snapshot's size '%s'" % (size << 30, source_snapshot["size"])) source_version = source_snapshot["version"] origin = source_snapshot["mapfile"] origin_size = source_snapshot["size"] elif source_type == "image": source_image = util.get_image(user_id, source_uuid, exception=faults.BadRequest) img_status = source_image.get("status", "").upper() if img_status != OBJECT_AVAILABLE: raise faults.BadRequest("Cannot create volume from image, while" " image is in '%s' status" % img_status) if size is None: raise faults.BadRequest("Volume size is required") elif (size << 30) < int(source_image["size"]): raise faults.BadRequest("Volume size '%s' is smaller than" " image's size '%s'" % (size << 30, source_image["size"])) source = Volume.prefix_source(source_uuid, source_type="image") source_version = source_image["version"] origin = source_image["mapfile"] origin_size = source_image["size"] elif source_type == "blank": if size is None: raise faults.BadRequest("Volume size is required") source = origin = None elif source_type == "volume": # Currently, Archipelago does not support cloning a volume raise faults.BadRequest("Cloning a volume is not supported") # source_volume = util.get_volume(user_id, source_uuid, # for_update=True, non_deleted=True, # exception=faults.BadRequest) # if source_volume.status != "IN_USE": # raise faults.BadRequest("Cannot clone volume while it is in '%s'" # " status" % source_volume.status) # # If no size is specified, use the size of the volume # if size is None: # size = source_volume.size # elif size < source_volume.size: # raise faults.BadRequest("Volume size cannot be smaller than the" # " source volume") # source = Volume.prefix_source(source_uuid, source_type="volume") # origin = source_volume.backend_volume_uuid else: raise faults.BadRequest("Unknown source type") volume = Volume.objects.create(userid=user_id, project=project, size=size, volume_type=volume_type, name=name, machine=server, description=description, delete_on_termination=delete_on_termination, source=source, source_version=source_version, origin=origin, index=index, status="CREATING") # Store the size of the origin in the volume object but not in the DB. # We will have to change this in order to support detachable volumes. volume.origin_size = origin_size return volume
def _create_subnet(network_id, user_id, cidr, name, ipversion=4, gateway=None, dhcp=True, slaac=True, dns_nameservers=None, allocation_pools=None, host_routes=None): """Create a subnet network_id and the desired cidr are mandatory, everything else is optional """ try: network_id = int(network_id) network = Network.objects.select_for_update().get(id=network_id) except (ValueError, TypeError): raise api.faults.BadRequest("Malformed network ID") except Network.DoesNotExist: raise api.faults.ItemNotFound("No network found with that ID") if network.deleted: raise api.faults.BadRequest("Network has been deleted") if user_id != network.userid: raise api.faults.Forbidden("Forbidden operation") if ipversion not in [4, 6]: raise api.faults.BadRequest("Malformed IP version type") check_number_of_subnets(network, ipversion) if network.backend_networks.exists(): raise api.faults.BadRequest("Cannot create subnet in network %s, VMs" " are already connected to this network" % network_id) try: cidr_ip = ipaddr.IPNetwork(cidr) except ValueError: raise api.faults.BadRequest("Malformed CIDR") if ipversion == 6: validate_subnet_params(subnet6=cidr, gateway6=gateway) else: validate_subnet_params(subnet=cidr, gateway=gateway) utils.check_name_length(name, Subnet.SUBNET_NAME_LENGTH, "Subnet " "name is too long") sub = Subnet.objects.create(name=name, network=network, cidr=cidr, ipversion=ipversion, gateway=gateway, userid=network.userid, public=network.public, dhcp=dhcp, host_routes=host_routes, dns_nameservers=dns_nameservers) network.subnet_ids.append(sub.id) network.save() gateway_ip = ipaddr.IPAddress(gateway) if gateway else None if allocation_pools is not None: if ipversion == 6: raise api.faults.Conflict("Can't allocate an IP Pool in IPv6") elif ipversion == 4: # Check if the gateway is the first IP of the subnet, or the last. In # that case create a single ip pool. if gateway_ip: if int(gateway_ip) - int(cidr_ip) == 1: allocation_pools = [(gateway_ip + 1, cidr_ip.broadcast - 1)] elif int(cidr_ip.broadcast) - int(gateway_ip) == 1: allocation_pools = [(cidr_ip.network + 1, gateway_ip - 1)] else: # If the gateway isn't the first available ip, create two # different ip pools adjacent to said ip allocation_pools = [(cidr_ip.network + 1, gateway_ip - 1), (gateway_ip + 1, cidr_ip.broadcast - 1)] else: allocation_pools = [(cidr_ip.network + 1, cidr_ip.broadcast - 1)] if allocation_pools: # Validate the allocation pools validate_pools(allocation_pools, cidr_ip, gateway_ip) create_ip_pools(allocation_pools, cidr_ip, sub) return sub
def rename(network, name): utils.check_name_length(name, Network.NETWORK_NAME_LENGTH, "Network name " "is too long") network.name = name network.save() return network
def _create_volume(user_id, project, size, source_type, source_uuid, volume_type, name=None, description=None, index=None, delete_on_termination=True, shared_to_project=False): """Create the volume in the DB. This function can be called from two different places: 1) During server creation, when creating the volumes of a new server 2) During volume creation. """ utils.check_name_length(name, Volume.NAME_LENGTH, "Volume name is too long") utils.check_name_length(description, Volume.DESCRIPTION_LENGTH, "Volume description is too long") validate_volume_termination(volume_type, delete_on_termination) if size is not None: try: size = int(size) except (TypeError, ValueError): raise faults.BadRequest("Volume size must be a positive integer") if size < 1: raise faults.BadRequest("Volume size must be a positive integer") if size > settings.CYCLADES_VOLUME_MAX_SIZE: raise faults.BadRequest("Maximum volume size is %sGB" % settings.CYCLADES_VOLUME_MAX_SIZE) # Only ext_ disk template supports cloning from another source. Otherwise # it must be the root volume so that 'snf-image' fill the volume can_have_source = (index == 0 or volume_type.provider in settings.GANETI_CLONE_PROVIDERS) if not can_have_source and source_type != "blank": msg = ("Cannot specify a 'source' attribute for volume type '%s' with" " disk template '%s'" % (volume_type.id, volume_type.disk_template)) raise faults.BadRequest(msg) source_version = None origin_size = None # TODO: Check Volume/Snapshot Status if source_type == "snapshot": source_snapshot = util.get_snapshot(user_id, source_uuid, exception=faults.BadRequest) snap_status = source_snapshot.get("status", "").upper() if snap_status != OBJECT_AVAILABLE: raise faults.BadRequest("Cannot create volume from snapshot, while" " snapshot is in '%s' status" % snap_status) source = Volume.prefix_source(source_uuid, source_type="snapshot") if size is None: raise faults.BadRequest("Volume size is required") elif (size << 30) < int(source_snapshot["size"]): raise faults.BadRequest("Volume size '%s' is smaller than" " snapshot's size '%s'" % (size << 30, source_snapshot["size"])) source_version = source_snapshot["version"] origin = source_snapshot["mapfile"] origin_size = source_snapshot["size"] elif source_type == "image": source_image = util.get_image(user_id, source_uuid, exception=faults.BadRequest) img_status = source_image.get("status", "").upper() if img_status != OBJECT_AVAILABLE: raise faults.BadRequest("Cannot create volume from image, while" " image is in '%s' status" % img_status) if size is None: raise faults.BadRequest("Volume size is required") elif (size << 30) < int(source_image["size"]): raise faults.BadRequest("Volume size '%s' is smaller than" " image's size '%s'" % (size << 30, source_image["size"])) source = Volume.prefix_source(source_uuid, source_type="image") source_version = source_image["version"] origin = source_image["mapfile"] origin_size = source_image["size"] elif source_type == "blank": if size is None: raise faults.BadRequest("Volume size is required") source = origin = None elif source_type == "volume": # Currently, Archipelago does not support cloning a volume raise faults.BadRequest("Cloning a volume is not supported") # source_volume = util.get_volume(user_id, source_uuid, # for_update=True, non_deleted=True, # exception=faults.BadRequest) # if source_volume.status != "IN_USE": # raise faults.BadRequest("Cannot clone volume while it is in '%s'" # " status" % source_volume.status) # # If no size is specified, use the size of the volume # if size is None: # size = source_volume.size # elif size < source_volume.size: # raise faults.BadRequest("Volume size cannot be smaller than the" # " source volume") # source = Volume.prefix_source(source_uuid, source_type="volume") # origin = source_volume.backend_volume_uuid else: raise faults.BadRequest("Unknown source type") volume = Volume.objects.create(userid=user_id, project=project, index=index, shared_to_project=shared_to_project, size=size, volume_type=volume_type, name=name, description=description, delete_on_termination=delete_on_termination, source=source, source_version=source_version, origin=origin, status="CREATING") # Store the size of the origin in the volume object but not in the DB. # We will have to change this in order to support detachable volumes. volume.origin_size = origin_size return volume
def create(credentials, name, password, flavor, image_id, metadata={}, personality=[], networks=None, use_backend=None, project=None, volumes=None, helper=False, user_data="", shared_to_project=False, key_names=None): userid = credentials.userid utils.check_name_length(name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") # Get the image, if any, that is used for the first volume vol_image_id = None if volumes: vol = volumes[0] if vol["source_type"] in ["image", "snapshot"]: vol_image_id = vol["source_uuid"] # Check conflict between server's and volume's image if image_id and vol_image_id and image_id != vol_image_id: raise faults.BadRequest("The specified server's image is different" " from the the source of the first volume.") elif vol_image_id and not image_id: image_id = vol_image_id elif not image_id: raise faults.BadRequest("You need to specify either an image or a" " block device mapping.") if len(metadata) > settings.CYCLADES_VM_MAX_METADATA: raise faults.BadRequest("Virtual Machines cannot have more than %s " "metadata items" % settings.CYCLADES_VM_MAX_METADATA) # Get image info image = util.get_image_dict(image_id, userid) if not volumes: # If no volumes are specified, we automatically create a volume with # the size of the flavor and filled with the specified image. volumes = [{ "source_type": "image", "source_uuid": image_id, "size": flavor.disk, "delete_on_termination": True }] assert (len(volumes) > 0), "Cannot create server without volumes" if volumes[0]["source_type"] == "blank": raise faults.BadRequest("Root volume cannot be blank") try: is_system = (image["owner"] == settings.SYSTEM_IMAGES_OWNER) img, created = Image.objects.get_or_create(uuid=image["id"], version=image["version"]) if created: img.owner = image["owner"] img.name = image["name"] img.location = image["location"] img.mapfile = image["mapfile"] img.is_public = image["is_public"] img.is_snapshot = image["is_snapshot"] img.is_system = is_system img.os = image["metadata"].get("OS", "unknown") img.osfamily = image["metadata"].get("OSFAMILY", "unknown") img.save() except Exception as e: # Image info is not critical. Continue if it fails for any reason log.warning("Failed to store image info: %s", e) if project is None: project = userid if use_backend is None: # Allocate server to a Ganeti backend use_backend = allocate_new_server(userid, project, flavor) if key_names is None: key_names = [] auth_keys = '\n'.join( [util.get_keypair(key_name, userid).content for key_name in key_names]) vm_id, port_ids, volume_ids, origin_sizes = _db_create_server( credentials, name, flavor, image, metadata, networks, use_backend, project, volumes, helper, shared_to_project, key_names) return _create_server(vm_id, port_ids, volume_ids, flavor, image, personality, user_data, password, auth_keys, origin_sizes)
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) 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: # Do not allow allocation of new IPs if the network is drained if network.drained: raise faults.Conflict("Cannot create port while network %s is in" " 'SNF:DRAINED' status" % network.id) # 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 create(credentials, name, password, flavor, image_id, metadata={}, personality=[], networks=None, use_backend=None, project=None, volumes=None, helper=False, user_data="", shared_to_project=False, key_names=None): userid = credentials.userid utils.check_name_length(name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") # Get the image, if any, that is used for the first volume vol_image_id = None if volumes: vol = volumes[0] if vol["source_type"] in ["image", "snapshot"]: vol_image_id = vol["source_uuid"] # Check conflict between server's and volume's image if image_id and vol_image_id and image_id != vol_image_id: raise faults.BadRequest("The specified server's image is different" " from the the source of the first volume.") elif vol_image_id and not image_id: image_id = vol_image_id elif not image_id: raise faults.BadRequest("You need to specify either an image or a" " block device mapping.") if len(metadata) > settings.CYCLADES_VM_MAX_METADATA: raise faults.BadRequest("Virtual Machines cannot have more than %s " "metadata items" % settings.CYCLADES_VM_MAX_METADATA) # Get image info image = util.get_image_dict(image_id, userid) if not volumes: # If no volumes are specified, we automatically create a volume with # the size of the flavor and filled with the specified image. volumes = [{"source_type": "image", "source_uuid": image_id, "size": flavor.disk, "delete_on_termination": True}] assert(len(volumes) > 0), "Cannot create server without volumes" if volumes[0]["source_type"] == "blank": raise faults.BadRequest("Root volume cannot be blank") try: is_system = (image["owner"] == settings.SYSTEM_IMAGES_OWNER) img, created = Image.objects.get_or_create(uuid=image["id"], version=image["version"]) if created: img.owner = image["owner"] img.name = image["name"] img.location = image["location"] img.mapfile = image["mapfile"] img.is_public = image["is_public"] img.is_snapshot = image["is_snapshot"] img.is_system = is_system img.os = image["metadata"].get("OS", "unknown") img.osfamily = image["metadata"].get("OSFAMILY", "unknown") img.save() except Exception as e: # Image info is not critical. Continue if it fails for any reason log.warning("Failed to store image info: %s", e) if project is None: project = userid if use_backend is None: # Allocate server to a Ganeti backend use_backend = allocate_new_server(userid, project, flavor) if key_names is None: key_names = [] auth_keys = '\n'.join([ util.get_keypair(key_name, userid).content for key_name in key_names ]) vm_id, port_ids, volume_ids, origin_sizes = _db_create_server( credentials, name, flavor, image, metadata, networks, use_backend, project, volumes, helper, shared_to_project, key_names) return _create_server(vm_id, port_ids, volume_ids, flavor, image, personality, user_data, password, auth_keys, origin_sizes)
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 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 create(user_id, size, server_id, name=None, description=None, source_volume_id=None, source_snapshot_id=None, source_image_id=None, volume_type_id=None, metadata=None, project=None): # Currently we cannot create volumes without being attached to a server if server_id is None: raise faults.BadRequest("Volume must be attached to server") server = util.get_server(user_id, server_id, for_update=True, non_deleted=True, exception=faults.BadRequest) server_vtype = server.flavor.volume_type if volume_type_id is not None: volume_type = util.get_volume_type(volume_type_id, include_deleted=False, exception=faults.BadRequest) if volume_type != server_vtype: raise faults.BadRequest("Cannot create a volume with type '%s' to" " a server with volume type '%s'." % (volume_type.id, server_vtype.id)) else: volume_type = server_vtype # Assert that not more than one source are used sources = filter(lambda x: x is not None, [source_volume_id, source_snapshot_id, source_image_id]) if len(sources) > 1: raise faults.BadRequest("Volume can not have more than one source!") if source_volume_id is not None: source_type = "volume" source_uuid = source_volume_id elif source_snapshot_id is not None: source_type = "snapshot" source_uuid = source_snapshot_id elif source_image_id is not None: source_type = "image" source_uuid = source_image_id else: source_type = "blank" source_uuid = None if project is None: project = user_id if metadata is not None and \ len(metadata) > settings.CYCLADES_VOLUME_MAX_METADATA: raise faults.BadRequest("Volumes cannot have more than %s metadata " "items" % settings.CYCLADES_VOLUME_MAX_METADATA) volume = _create_volume(server, user_id, project, size, source_type, source_uuid, volume_type=volume_type, name=name, description=description, index=None) if metadata is not None: for meta_key, meta_val in metadata.items(): utils.check_name_length(meta_key, VolumeMetadata.KEY_LENGTH, "Metadata key is too long") utils.check_name_length(meta_val, VolumeMetadata.VALUE_LENGTH, "Metadata key is too long") volume.metadata.create(key=meta_key, value=meta_val) server_attachments.attach_volume(server, volume) return volume
def create(userid, name, password, flavor, image_id, metadata={}, personality=[], networks=None, use_backend=None, project=None, volumes=None, helper=False, user_projects=None, shared_to_project=False): utils.check_name_length(name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") # Get the image, if any, that is used for the first volume vol_image_id = None if volumes: vol = volumes[0] if vol["source_type"] in ["image", "snapshot"]: vol_image_id = vol["source_uuid"] # Check conflict between server's and volume's image if image_id and vol_image_id and image_id != vol_image_id: raise faults.BadRequest("The specified server's image is different" " from the the source of the first volume.") elif vol_image_id and not image_id: image_id = vol_image_id elif not image_id: raise faults.BadRequest("You need to specify either an image or a" " block device mapping.") if len(metadata) > settings.CYCLADES_VM_MAX_METADATA: raise faults.BadRequest("Virtual Machines cannot have more than %s " "metadata items" % settings.CYCLADES_VM_MAX_METADATA) # Get image info image = util.get_image_dict(image_id, userid) if not volumes: # If no volumes are specified, we automatically create a volume with # the size of the flavor and filled with the specified image. volumes = [{"source_type": "image", "source_uuid": image_id, "size": flavor.disk, "delete_on_termination": True}] assert(len(volumes) > 0), "Cannot create server without volumes" if volumes[0]["source_type"] == "blank": raise faults.BadRequest("Root volume cannot be blank") try: is_system = (image["owner"] == settings.SYSTEM_IMAGES_OWNER) img, created = Image.objects.get_or_create(uuid=image["id"], version=image["version"]) if created: img.owner = image["owner"] img.name = image["name"] img.location = image["location"] img.mapfile = image["mapfile"] img.is_public = image["is_public"] img.is_snapshot = image["is_snapshot"] img.is_system = is_system img.os = image["metadata"].get("OS", "unknown") img.osfamily = image["metadata"].get("OSFAMILY", "unknown") img.save() except Exception as e: # Image info is not critical. Continue if it fails for any reason log.warning("Failed to store image info: %s", e) if use_backend is None: # Allocate server to a Ganeti backend use_backend = allocate_new_server(userid, flavor) # Create the ports for the server ports = create_instance_ports(userid, user_projects, networks) if project is None: project = userid # 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=userid, project=project, shared_to_project=shared_to_project, imageid=image["id"], image_version=image["version"], flavor=flavor, operstate="BUILD", 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(userid, user_projects, 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=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) # Create the server in Ganeti. vm = create_server(vm, ports, server_volumes, flavor, image, personality, password) return vm
def create(userid, name, password, flavor, image_id, metadata={}, personality=[], networks=None, use_backend=None, project=None, volumes=None): utils.check_name_length(name, VirtualMachine.VIRTUAL_MACHINE_NAME_LENGTH, "Server name is too long") # Get the image, if any, that is used for the first volume vol_image_id = None if volumes: vol = volumes[0] if vol["source_type"] in ["image", "snapshot"]: vol_image_id = vol["source_uuid"] # Check conflict between server's and volume's image if image_id and vol_image_id and image_id != vol_image_id: raise faults.BadRequest("The specified server's image is different" " from the the source of the first volume.") elif vol_image_id and not image_id: image_id = vol_image_id elif not image_id: raise faults.BadRequest("You need to specify either an image or a" " block device mapping.") if len(metadata) > settings.CYCLADES_VM_MAX_METADATA: raise faults.BadRequest("Virtual Machines cannot have more than %s " "metadata items" % settings.CYCLADES_VM_MAX_METADATA) # Get image info image = util.get_image_dict(image_id, userid) if not volumes: # If no volumes are specified, we automatically create a volume with # the size of the flavor and filled with the specified image. volumes = [{ "source_type": "image", "source_uuid": image_id, "size": flavor.disk, "delete_on_termination": True }] assert (len(volumes) > 0), "Cannot create server without volumes" if volumes[0]["source_type"] == "blank": raise faults.BadRequest("Root volume cannot be blank") try: is_system = (image["owner"] == settings.SYSTEM_IMAGES_OWNER) img, created = Image.objects.get_or_create(uuid=image["id"], version=image["version"]) if created: img.owner = image["owner"] img.name = image["name"] img.location = image["location"] img.mapfile = image["mapfile"] img.is_public = image["is_public"] img.is_snapshot = image["is_snapshot"] img.is_system = is_system img.os = image["metadata"].get("OS", "unknown") img.osfamily = image["metadata"].get("OSFAMILY", "unknown") img.save() except Exception as e: # Image info is not critical. Continue if it fails for any reason log.warning("Failed to store image info: %s", e) if use_backend is None: # Allocate server to a Ganeti backend use_backend = allocate_new_server(userid, flavor) # Create the ports for the server ports = create_instance_ports(userid, networks) if project is None: project = userid # 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=userid, project=project, imageid=image["id"], image_version=image["version"], flavor=flavor, operstate="BUILD") 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(userid, 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"] v.machine = vm v.index = index v.save() else: v = _create_volume(server=vm, user_id=userid, volume_type=server_vtype, project=project, index=index, **vol_info) 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) # Create the server in Ganeti. vm = create_server(vm, ports, server_volumes, flavor, image, personality, password) return vm
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 _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})