Пример #1
0
def detach_volume(vm, volume):
    """Detach a Volume from a VM

    The volume must be in 'IN_USE' status in order to be detached. Also,
    the root volume of the instance (index=0) can not be detached. This
    function will send the corresponding job to Ganeti backend and update the
    status of the volume to 'DETACHING'.

    """
    util.assert_detachable_volume_type(volume.volume_type)
    _check_attachment(vm, volume)
    if volume.status not in ["IN_USE", "ERROR"]:
        raise faults.BadRequest("Cannot detach volume while volume is in"
                                " '%s' status." % volume.status)
    if volume.index == 0:
        raise faults.BadRequest("Cannot detach the root volume of server %s." %
                                vm)

    with commands.ServerCommand("DETACH_VOLUME", vm):
        jobid = backend.detach_volume(vm, volume)
        vm.record_job(jobid)
        log.info("Detached volume '%s' from server '%s'. JobID: '%s'",
                 volume.id, volume.machine_id, jobid)
        volume.backendjobid = jobid
        volume.status = "DETACHING"
        volume.save()
Пример #2
0
def detach_volume(vm, volume):
    """Detach a Volume from a VM

    The volume must be in 'IN_USE' status in order to be detached. Also,
    the root volume of the instance (index=0) can not be detached. This
    function will send the corresponding job to Ganeti backend and update the
    status of the volume to 'DETACHING'.

    """
    util.assert_detachable_volume_type(volume.volume_type)
    _check_attachment(vm, volume)
    if volume.status not in ["IN_USE", "ERROR"]:
        raise faults.BadRequest("Cannot detach volume while volume is in"
                                " '%s' status." % volume.status)
    if volume.index == 0:
        raise faults.BadRequest("Cannot detach the root volume of server %s." %
                                vm)

    with commands.ServerCommand("DETACH_VOLUME", vm):
        jobid = backend.detach_volume(vm, volume)
        vm.record_job(jobid)
        log.info("Detached volume '%s' from server '%s'. JobID: '%s'",
                 volume.id, volume.machine_id, jobid)
        volume.backendjobid = jobid
        volume.status = "DETACHING"
        volume.save()
Пример #3
0
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
Пример #4
0
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
Пример #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 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()
Пример #7
0
def detach_volume(vm, volume):
    """Detach a Volume from a VM

    The volume must be in 'IN_USE' status in order to be detached. Also,
    the root volume of the instance (index=0) can not be detached. This
    function will send the corresponding job to Ganeti backend and update the
    status of the volume to 'DETACHING'.

    """
    util.assert_detachable_volume_type(volume.volume_type)
    _check_attachment(vm, volume)
    if volume.status not in ["IN_USE", "ERROR"]:
        raise faults.BadRequest("Cannot detach volume while volume is in"
                                " '%s' status." % volume.status)
    if volume.index == 0:
        raise faults.BadRequest("Cannot detach the root volume of server %s." %
                                vm)

    comm = commands.server_command("DETACH_VOLUME")
    return comm(_detach_volume)(vm, volume)
Пример #8
0
    def test_detachable_volumes_utils(self):
        # No volume type
        volume_type = None
        with self.assertRaisesMessage(faults.BadRequest, self.missing_msg):
            util.is_volume_type_detachable(volume_type)

        # Non-detachable template
        volume_type = MockVolumeType("template3")
        self.assertEqual(util.is_volume_type_detachable(volume_type), False)

        # Detachable template
        volume_type = MockVolumeType("template2")
        self.assertEqual(util.is_volume_type_detachable(volume_type), True)

        # Non-detachable template and assert
        volume_type = MockVolumeType("template3")
        with self.assertRaisesMessage(faults.BadRequest, self.wrong_msg):
            util.assert_detachable_volume_type(volume_type)

        # Detachable template and assert
        volume_type = MockVolumeType("template1")
        util.assert_detachable_volume_type(volume_type)
Пример #9
0
    def test_detachable_volumes_utils(self):
        # No volume type
        volume_type = None
        with self.assertRaisesMessage(faults.BadRequest, self.missing_msg):
            util.is_volume_type_detachable(volume_type)

        # Non-detachable template
        volume_type = MockVolumeType("template3")
        self.assertEqual(util.is_volume_type_detachable(volume_type), False)

        # Detachable template
        volume_type = MockVolumeType("template2")
        self.assertEqual(util.is_volume_type_detachable(volume_type), True)

        # Non-detachable template and assert
        volume_type = MockVolumeType("template3")
        with self.assertRaisesMessage(faults.BadRequest, self.wrong_msg):
            util.assert_detachable_volume_type(volume_type)

        # Detachable template and assert
        volume_type = MockVolumeType("template1")
        util.assert_detachable_volume_type(volume_type)
Пример #10
0
def attach_volume(vm, volume):
    """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_id != vm.flavor.volume_type_id:
        msg = ("Volume and server must have the same volume type. Volume has"
               " volume type '%s' while server has '%s'"
               % (volume.volume_type_id, vm.flavor.volume_type_id))
        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
    comm = commands.server_command("ATTACH_VOLUME",
                                   action_fields=action_fields)
    return comm(_attach_volume)(vm, volume)