Пример #1
0
    def test_assign_to_server(self):
        """Test if volume assignment to server works properly."""
        vm = mf.VirtualMachineFactory()

        # Assign a volume to a server with no volumes.
        vol1 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol1)
        # Assert that the volume is associated with the server and that its
        # index is 0.
        self.assertEqual(vol1.machine, vm)
        self.assertItemsEqual(vm.volumes.all(), [vol1])
        self.assertEqual(vol1.index, 0)

        # Assign a volume to a server with a volume.
        vol2 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol2)
        # Assert that the volume is associated with the server and that its
        # index is 1.
        self.assertEqual(vol2.machine, vm)
        self.assertItemsEqual(vm.volumes.all(), [vol1, vol2])
        self.assertEqual(vol2.index, 1)

        # Assign a volume to a server with more than one volume and set its
        # index to a custom value (e.g. 9)
        vol3 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol3, index=9)
        # Assert that the volume is associated with the server and that its
        # index is set to 9.
        self.assertEqual(vol3.machine, vm)
        self.assertItemsEqual(vm.volumes.all(), [vol1, vol2, vol3])
        self.assertEqual(vol3.index, 9)

        # Assign a volume to a server with a volume whose index is 1 and a
        # deleted volume whose index is 9.
        vol1.machine = None
        vol1.save()
        vol3.deleted = True
        vol3.save()
        vol4 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol4)
        # Assert that the volume is associated with the server and that its
        # index is 2.
        self.assertEqual(vol4.machine, vm)
        self.assertItemsEqual(vm.volumes.filter(deleted=False), [vol2, vol4])
        self.assertEqual(vol4.index, 2)

        # Assert that the same index cannot be assigned to a different volume.
        vol5 = mf.VolumeFactory()
        with self.assertRaisesMessage(faults.BadRequest,
                                      self.wrong_msg.format(vol5, 2, vm)):
            util.assign_volume_to_server(vm, vol5, index=2)
Пример #2
0
    def test_assign_to_server(self):
        """Test if volume assignment to server works properly."""
        vm = mf.VirtualMachineFactory()

        # Assign a volume to a server with no volumes.
        vol1 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol1)
        # Assert that the volume is associated with the server and that its
        # index is 0.
        self.assertEqual(vol1.machine, vm)
        self.assertItemsEqual(vm.volumes.all(), [vol1])
        self.assertEqual(vol1.index, 0)

        # Assign a volume to a server with a volume.
        vol2 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol2)
        # Assert that the volume is associated with the server and that its
        # index is 1.
        self.assertEqual(vol2.machine, vm)
        self.assertItemsEqual(vm.volumes.all(), [vol1, vol2])
        self.assertEqual(vol2.index, 1)

        # Assign a volume to a server with more than one volume and set its
        # index to a custom value (e.g. 9)
        vol3 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol3, index=9)
        # Assert that the volume is associated with the server and that its
        # index is set to 9.
        self.assertEqual(vol3.machine, vm)
        self.assertItemsEqual(vm.volumes.all(), [vol1, vol2, vol3])
        self.assertEqual(vol3.index, 9)

        # Assign a volume to a server with a volume whose index is 1 and a
        # deleted volume whose index is 9.
        vol1.machine = None
        vol1.save()
        vol3.deleted = True
        vol3.save()
        vol4 = mf.VolumeFactory()
        util.assign_volume_to_server(vm, vol4)
        # Assert that the volume is associated with the server and that its
        # index is 2.
        self.assertEqual(vol4.machine, vm)
        self.assertItemsEqual(vm.volumes.filter(deleted=False), [vol2, vol4])
        self.assertEqual(vol4.index, 2)

        # Assert that the same index cannot be assigned to a different volume.
        vol5 = mf.VolumeFactory()
        with self.assertRaisesMessage(faults.BadRequest,
                                      self.wrong_msg.format(vol5, 2, vm)):
            util.assign_volume_to_server(vm, vol5, index=2)
Пример #3
0
def _attach_volume(vm, volume):
    """Attach a Volume to a VM and update the Volume's status."""
    util.assign_volume_to_server(vm, volume)
    jobid = backend.attach_volume(vm, volume)
    log.info("Attached volume '%s' to server '%s'. JobID: '%s'", volume.id,
             volume.machine_id, jobid)
    volume.backendjobid = jobid
    volume.machine = vm
    if volume.status == "AVAILABLE":
        volume.status = "ATTACHING"
    else:
        volume.status = "CREATING"
    volume.save()
    return jobid
Пример #4
0
def attach_volume(vm, volume, atomic_context):
    """Attach a volume to a server.

    The volume must be in 'AVAILABLE' status in order to be attached. Also,
    number of the volumes that are attached to the server must remain less
    than 'GANETI_MAX_DISKS_PER_INSTANCE' setting. This function will send
    the corresponding job to Ganeti backend and update the status of the
    volume to 'ATTACHING'.

    """
    # Check volume state
    if volume.status not in ["AVAILABLE", "CREATING"]:
        raise faults.BadRequest("Cannot attach volume while volume is in"
                                " '%s' status." % volume.status)
    elif volume.status == "AVAILABLE":
        util.assert_detachable_volume_type(volume.volume_type)

    # Check that disk templates are the same
    if volume.volume_type.template != vm.flavor.volume_type.template:
        msg = ("Volume and server must have the same volume template. Volume"
               " has volume template'%s' while server has '%s'" %
               (volume.volume_type.template, vm.flavor.volume_type.template))
        raise faults.BadRequest(msg)

    # Check maximum disk per instance hard limit
    vm_volumes_num = vm.volumes.filter(deleted=False).count()
    if vm_volumes_num == settings.GANETI_MAX_DISKS_PER_INSTANCE:
        raise faults.BadRequest("Maximum volumes per server limit reached")

    if volume.status == "CREATING":
        action_fields = {"disks": [("add", volume, {})]}
    else:
        action_fields = None

    with commands.ServerCommand("ATTACH_VOLUME",
                                vm,
                                atomic_context=atomic_context,
                                action_fields=action_fields):
        util.assign_volume_to_server(vm, volume)
        jobid = backend.attach_volume(vm, volume)
        vm.record_job(jobid)
        log.info("Attached volume '%s' to server '%s'. JobID: '%s'", volume.id,
                 volume.machine_id, jobid)
        volume.backendjobid = jobid
        volume.machine = vm
        if volume.status == "AVAILABLE":
            volume.status = "ATTACHING"
        else:
            volume.status = "CREATING"
        volume.save()
Пример #5
0
def attach_volume(vm, volume, atomic_context):
    """Attach a volume to a server.

    The volume must be in 'AVAILABLE' status in order to be attached. Also,
    number of the volumes that are attached to the server must remain less
    than 'GANETI_MAX_DISKS_PER_INSTANCE' setting. This function will send
    the corresponding job to Ganeti backend and update the status of the
    volume to 'ATTACHING'.

    """
    # Check volume state
    if volume.status not in ["AVAILABLE", "CREATING"]:
        raise faults.BadRequest("Cannot attach volume while volume is in"
                                " '%s' status." % volume.status)
    elif volume.status == "AVAILABLE":
        util.assert_detachable_volume_type(volume.volume_type)

    # Check that disk templates are the same
    if volume.volume_type.template != vm.flavor.volume_type.template:
        msg = ("Volume and server must have the same volume template. Volume"
               " has volume template'%s' while server has '%s'"
               % (volume.volume_type.template, vm.flavor.volume_type.template))
        raise faults.BadRequest(msg)

    # Check maximum disk per instance hard limit
    vm_volumes_num = vm.volumes.filter(deleted=False).count()
    if vm_volumes_num == settings.GANETI_MAX_DISKS_PER_INSTANCE:
        raise faults.BadRequest("Maximum volumes per server limit reached")

    if volume.status == "CREATING":
        action_fields = {"disks": [("add", volume, {})]}
    else:
        action_fields = None

    with commands.ServerCommand("ATTACH_VOLUME", vm,
                                atomic_context=atomic_context,
                                action_fields=action_fields):
        util.assign_volume_to_server(vm, volume)
        jobid = backend.attach_volume(vm, volume)
        vm.record_job(jobid)
        log.info("Attached volume '%s' to server '%s'. JobID: '%s'", volume.id,
                 volume.machine_id, jobid)
        volume.backendjobid = jobid
        volume.machine = vm
        if volume.status == "AVAILABLE":
            volume.status = "ATTACHING"
        else:
            volume.status = "CREATING"
        volume.save()
Пример #6
0
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})
Пример #7
0
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,
           key_name=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 project is None:
        project = userid

    if use_backend is None:
        # Allocate server to a Ganeti backend
        use_backend = allocate_new_server(userid, project, flavor)

    # Create the ports for the server
    ports = create_instance_ports(userid, user_projects, 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=userid,
                                       project=project,
                                       shared_to_project=shared_to_project,
                                       imageid=image["id"],
                                       image_version=image["version"],
                                       key_name=key_name,
                                       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)

    public_key = None
    if key_name is not None:
        keypair = util.get_keypair(key_name, userid)
        public_key = keypair.content

    # Create the server in Ganeti.
    vm = create_server(vm, ports, server_volumes, flavor, image, personality,
                       password, public_key)

    return vm
Пример #8
0
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
Пример #9
0
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})