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()
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()