Ejemplo n.º 1
0
    def check_migrate_parameters(
        self, vm: VM, offline: bool, offline_transport: str,
        disk_size: int = None,
    ):
        if offline_transport not in ['netcat', 'drbd', 'xfs']:
            raise StorageError(
                'Unknown offline transport method {}!'
                .format(offline_transport)
            )

        if disk_size is None:
            return

        if disk_size < 1:
            raise StorageError('disk_size must be at least 1GiB!')
        if not (offline and offline_transport == 'xfs'):
            raise StorageError(
                'disk_size can be applied only with offline transport xfs!'
            )
        allocated_space = vm.dataset_obj['disk_size_gib'] - vm.disk_free()
        if disk_size < allocated_space:
            raise StorageError(
                'disk_size is lower than allocated space: {} < {}!'
                .format(disk_size, allocated_space)
            )
Ejemplo n.º 2
0
    def create_vm_storage(self, vm, transaction=None, vol_name=None):
        """Allocate storage for a VM. Returns the disk path."""
        vol_name = vm.uid_name if vol_name is None else vol_name
        volume_xml = """
            <volume>
                <name>{name}</name>
                <allocation unit="G">{size}</allocation>
                <capacity unit="G">{size}</capacity>
            </volume>
        """.format(
            name=vol_name,
            size=vm.dataset_obj['disk_size_gib'],
        )

        volume = self.get_storage_pool().createXML(volume_xml, 0)
        if volume is None:
            raise StorageError('Failed to create storage volume {}/{}'.format(
                self.get_storage_pool().name(),
                vol_name,
            ))

        if transaction:
            transaction.on_rollback('destroy storage', volume.delete)

        # XXX: When building a VM we use the volumes path to format it right
        # after creation.  Unfortunately the kernel is slow to pick up on zfs
        # volume changes and creates the symlink in /dev/zvol/<pool>/ only
        # after a moment.
        self.run("while [ ! -L '{}' ]; do sleep 1; done".format(volume.path()))
Ejemplo n.º 3
0
    def check_netcat(self, port):
        pid = self.run('pgrep -f "^/bin/nc.openbsd -l -p {}"'.format(port),
                       warn_only=True,
                       silent=True)

        if pid:
            raise StorageError(
                'Listening netcat already found on destination hypervisor.')
Ejemplo n.º 4
0
    def get_volume_by_vm(self, vm):
        """Get logical volume information of a VM"""
        for vol_name in self.get_storage_pool().listVolumes():
            # Match the LV based on the object_id encoded within its name
            if vm.match_uid_name(vol_name):
                return self.get_storage_pool().storageVolLookupByName(vol_name)

        raise StorageError(
            'No existing storage volume found for VM "{}" on "{}".'.format(
                vm.fqdn, self.fqdn))
Ejemplo n.º 5
0
    def get_volume_by_vm(self, vm):
        """Get logical volume information of a VM"""
        domain = self._find_domain(vm)
        for vol_name in self.get_storage_pool().listVolumes():
            if (
                    # Match the LV based on the object_id encoded within its name
                    vm.match_uid_name(vol_name) or
                    # XXX: Deprecated matching for LVs w/o an uid_name
                    domain and vol_name == domain.name()):
                return self.get_storage_pool().storageVolLookupByName(vol_name)

        raise StorageError(
            'No existing storage volume found for VM "{}" on "{}".'.format(
                vm.fqdn, self.fqdn))
Ejemplo n.º 6
0
    def migrate_vm(
        self,
        vm,
        target_hypervisor,
        offline,
        offline_transport,
        transaction,
        no_shutdown,
    ):
        if offline_transport not in ['netcat', 'drbd']:
            raise StorageError('Unknown offline transport method {}!'.format(
                offline_transport))

        if offline:
            log.info(
                'Starting offline migration of vm {} from {} to {}'.format(
                    vm, vm.hypervisor, target_hypervisor))
            target_hypervisor.create_vm_storage(vm, transaction)
            if offline_transport == 'drbd':
                is_lvm_storage = (self.get_storage_type() == 'logical'
                                  and target_hypervisor.get_storage_type()
                                  == 'logical')

                if not is_lvm_storage:
                    raise NotImplementedError(
                        'DRBD migration is supported only between hypervisors '
                        'using LVM storage!')

                host_drbd = DRBD(self, vm, master_role=True)
                peer_drbd = DRBD(target_hypervisor, vm)
                if vm.hypervisor.vm_running(vm):
                    vm_block_size = vm.get_block_size('/dev/vda')
                    src_block_size = vm.hypervisor.get_block_size(
                        vm.hypervisor.get_volume_by_vm(vm).path())
                    dst_block_size = target_hypervisor.get_block_size(
                        target_hypervisor.get_volume_by_vm(vm).path())
                    log.debug(
                        'Block sizes: VM {}, Source HV {}, Destination HV {}'.
                        format(vm_block_size, src_block_size, dst_block_size))
                    vm.set_block_size(
                        'vda',
                        min(
                            vm_block_size,
                            src_block_size,
                            dst_block_size,
                        ))
                with host_drbd.start(peer_drbd), peer_drbd.start(host_drbd):
                    # XXX: Do we really need to wait for the both?
                    host_drbd.wait_for_sync()
                    peer_drbd.wait_for_sync()
                    vm.set_state('maintenance', transaction=transaction)

                    if vm.is_running():
                        if no_shutdown:
                            log.info('Please shut down the VM manually now')
                            vm.wait_for_running(running=False, timeout=86400)
                        else:
                            vm.shutdown(
                                check_vm_up_on_transaction=False,
                                transaction=transaction,
                            )

            elif offline_transport == 'netcat':
                vm.set_state('maintenance', transaction=transaction)
                if vm.is_running():
                    if no_shutdown:
                        log.info('Please shut down the VM manually now')
                        vm.wait_for_running(running=False, timeout=86400)
                    else:
                        vm.shutdown(
                            check_vm_up_on_transaction=False,
                            transaction=transaction,
                        )

                vm_disk_path = target_hypervisor.get_volume_by_vm(vm).path()
                with target_hypervisor.netcat_to_device(vm_disk_path) as args:
                    self.device_to_netcat(
                        self.get_volume_by_vm(vm).path(),
                        vm.dataset_obj['disk_size_gib'] * 1024**3,
                        args,
                    )
            target_hypervisor.define_vm(vm, transaction)
        else:
            # For online migrations always use same volume name as VM
            # already has.
            target_hypervisor.create_vm_storage(
                vm,
                transaction,
                vm.hypervisor.get_volume_by_vm(vm).name(),
            )
            migrate_live(self, target_hypervisor, vm, self._get_domain(vm))