Example #1
0
    def run(self, load=SNAPSHOT_BASE_NAME, guest_forwards=[], extra_args=[]):
        if load:
            self.vm.time_desync.report(self.vm.time_desync.LARGE)
        run_args = ['-loadvm', load] if load else []

        self.monitor = Monitor(self.vm)
        run_args += ['-qmp', (f'tcp:127.0.0.1:{self.monitor.port},'
                              'server,nowait,nodelay')]

        # TODO: extract SSH into a separate plugin?
        self.vm.ssh = SSH(self.vm,
                          key=path.fingertip('ssh_key', 'fingertip.paramiko'))
        self.vm.shared_directory = SharedDirectory(self.vm)
        self.vm.exec = self.vm.ssh.exec
        ssh_host_forward = f'hostfwd=tcp:127.0.0.1:{self.vm.ssh.port}-:22'
        cache_guest_forward = (CACHE_INTERNAL_IP, CACHE_INTERNAL_PORT,
                               f'nc 127.0.0.1 {self.vm.http_cache.port}')
        guest_forwards = guest_forwards + [cache_guest_forward]
        run_args += ['-device', 'virtio-net,netdev=net0', '-netdev',
                     ','.join(['user', 'id=net0', ssh_host_forward] +
                              (['restrict=yes'] if self.vm.sealed else []) +
                              [f'guestfwd=tcp:{ip}:{port}-cmd:{cmd}'
                               for ip, port, cmd in guest_forwards])]

        image = os.path.join(self.vm.path, 'image.qcow2')
        if self._image_to_clone:
            required_space = os.path.getsize(self._image_to_clone) + 2**30
            lock = fasteners.process_lock.InterProcessLock('/tmp/.fingertip')
            lock.acquire()
            if self.vm._transient and temp.has_space(required_space):
                image = temp.disappearing_file('/tmp', hint='fingertip-qemu')
                reflink.auto(self._image_to_clone, image)
                lock.release()
            else:
                lock.release()
                reflink.auto(self._image_to_clone, image)
            self._image_to_clone = None
        run_args += ['-drive',
                     f'file={image},cache=unsafe,if=virtio,discard=unmap']

        run_args += ['-m', self.ram_size]

        os.makedirs(path.SHARED, exist_ok=True)

        args = QEMU_COMMON_ARGS + self.custom_args + run_args + extra_args
        self.vm.log.debug(' '.join(args))
        if self.vm._backend_mode == 'pexpect':
            pexp = self.vm.log.pseudofile_powered(pexpect.spawn,
                                                  logfile=logging.INFO)
            self.vm.console = pexp(self._qemu, args, echo=False,
                                   timeout=None,
                                   encoding='utf-8', codec_errors='ignore')
            self.live = True
        elif self.vm._backend_mode == 'direct':
            subprocess.run([self._qemu, '-serial', 'mon:stdio'] + args,
                           check=True)
            self.live = False
            self._go_down()
Example #2
0
    def run(self, load=SNAPSHOT_BASE_NAME, guest_forwards=[], extra_args=[]):
        if load:
            self.vm.time_desync.report(self.vm.time_desync.LARGE)
        run_args = ['-loadvm', load] if load else []

        self.monitor = Monitor(self.vm)
        run_args += [
            '-qmp',
            (f'tcp:127.0.0.1:{self.monitor.port},'
             'server,nowait,nodelay')
        ]

        self.vm.ssh.port = free_port.find()
        self.vm.shared_directory = SharedDirectory(self.vm)
        self.vm.exec = self.vm.ssh.exec
        host_forwards = [(self.vm.ssh.port, 22)] + self.vm._host_forwards
        host_forwards = [
            f'hostfwd=tcp:127.0.0.1:{h}-:{g}' for h, g in host_forwards
        ]
        cache_guest_forward = (CACHE_INTERNAL_IP, CACHE_INTERNAL_PORT,
                               f'nc 127.0.0.1 {self.vm.http_cache.port}')
        guest_forwards = guest_forwards + [cache_guest_forward]
        run_args += [
            '-device', 'virtio-net,netdev=net0', '-netdev',
            ','.join(['user', 'id=net0'] + host_forwards +
                     (['restrict=yes'] if self.vm.sealed else []) + [
                         f'guestfwd=tcp:{ip}:{port}-cmd:{cmd}'
                         for ip, port, cmd in guest_forwards
                     ])
        ]

        self.image = os.path.join(self.vm.path, 'image.qcow2')
        if self._image_to_clone:
            # let's try to use /tmp (which is, hopefully, tmpfs) for transients
            # if it looks empty enough
            cloned_to_tmp = False
            required_space = os.path.getsize(self._image_to_clone) + 2 * 2**30
            if self.vm._transient:
                # Would be ideal to have it global (and multiuser-ok)
                tmp_free_lock = path.cache('.tmp-free-space-check-lock')
                with fasteners.process_lock.InterProcessLock(tmp_free_lock):
                    if temp.has_space(required_space, where='/tmp'):
                        self.image = temp.disappearing_file(
                            '/tmp', hint='fingertip-qemu')
                        self.vm.log.info('preloading image to /tmp...')
                        reflink.auto(self._image_to_clone, self.image)
                        self.vm.log.info('preloading image to /tmp completed')
                        cloned_to_tmp = True
            if not cloned_to_tmp:
                reflink.auto(self._image_to_clone, self.image)
            self._image_to_clone = None
        if self.virtio_scsi:
            run_args += [
                '-device', 'virtio-scsi-pci', '-device', 'scsi-hd,drive=hd',
                '-drive', f'file={self.image},cache=unsafe,'
                'if=none,id=hd,discard=unmap'
            ]
        else:
            run_args += [
                '-drive', f'file={self.image},cache=unsafe,'
                'if=virtio,discard=unmap'
            ]

        run_args += ['-m', str(self.vm.ram.max // 2**20)]

        os.makedirs(path.SHARED, exist_ok=True)

        args = QEMU_COMMON_ARGS + self.custom_args + run_args + extra_args
        self.vm.log.debug(' '.join(args))
        if self.vm._backend_mode == 'pexpect':
            # start connecting/negotiating QMP, later starts auto-ballooning
            threading.Thread(target=self.monitor.connect, daemon=True).start()
            pexp = self.vm.log.pseudofile_powered(pexpect.spawn,
                                                  logfile=logging.INFO)
            self.vm.console = pexp(self._qemu,
                                   args,
                                   echo=False,
                                   timeout=None,
                                   encoding='utf-8',
                                   codec_errors='ignore')
            self.live = True
        elif self.vm._backend_mode == 'direct':
            subprocess.run([self._qemu, '-serial', 'mon:stdio'] + args,
                           check=True)
            # FIXME: autoballooning won't start w/o the monitor connection!
            self.live = False
            self._go_down()