environment/lib/virtual-machine.rb

170 lines
4.8 KiB
Ruby
Raw Normal View History

2025-08-11 10:20:07 +02:00
require 'downloader'
2025-08-11 14:18:59 +02:00
require 'system'
2025-08-11 10:20:07 +02:00
require_relative 'data/resources/iso-images'
2025-08-11 17:36:55 +02:00
require 'vm/qemu'
2025-08-13 10:21:46 +02:00
require 'vm/archive'
2025-08-11 10:20:07 +02:00
module VirtualMachine
def self.distro(name, arch, type = :install)
2025-08-13 10:21:46 +02:00
ISO_URLS[name][arch][type]
2025-08-11 10:20:07 +02:00
end
def self.image_id_dir(options)
File.join("#{options[:distro].to_s}-#{options[:arch].to_s}", options[:name])
end
2025-08-13 10:21:46 +02:00
def self.vm_root_path
ENV["DAT_VM_DATA"] || File.join(User.cache_path, "vm")
end
def self.vm_dir(options)
File.join(vm_root_path, "image", image_id_dir(options))
2025-08-13 10:21:46 +02:00
end
def self.archive_dir(options)
File.join(vm_root_path, "archive", image_id_dir(options))
2025-08-13 10:21:46 +02:00
end
def self.create_archive_path(options)
File.join(archive_dir(options), "archive.tar.zst")
end
def self.get_recent_archive_path(options)
File.join(archive_dir(options), "archive.tar.zst")
end
# https://artur.gurgul.pro/vm/
# https://artur.gurgul.pro/vm/debian-arm64/debian/archive.tar.zst
def self.vm_archive_url(options)
return nil unless ENV["DAT_VM_REPO_URL"]
image_id = image_id_dir(options)
uri = Addressable::URI.parse(ENV["DAT_VM_REPO_URL"])
uri.path = File.join(uri.path, image_id, "archive.tar.zst")
return uri.to_s
2025-08-13 10:21:46 +02:00
end
def self.root_img_path(options)
File.join(vm_dir(options), "root.img")
end
def self.fill_defaults(options)
2025-08-11 14:18:59 +02:00
if options[:name] == nil
options[:name] = options[:distro]
end
if options[:arch] == nil
options[:arch] = System::ARCH
else
options[:arch] = System.arch_to_symbol(options[:arch])
end
2025-08-17 17:21:38 +02:00
if options[:detached] == nil
options[:detached] = false
end
2025-08-26 08:52:28 +02:00
if options[:tpm] == nil
options[:tpm] = false
end
2025-08-26 17:50:38 +02:00
unless options[:vars_fd]
options[:vars_fd] = File.join(vm_dir(options), "vars.fd")
end
unless options[:code_fd]
options[:code_fd] = File.join(vm_dir(options), "code.fd")
end
2025-08-13 10:21:46 +02:00
end
def self.archive(options)
fill_defaults(options)
Archive.create(vm_dir(options), out: create_archive_path(options))
end
def self.restore(options)
fill_defaults(options)
Archive.restore(get_recent_archive_path(options), vm_dir(options))
end
2025-08-11 14:18:59 +02:00
2025-08-13 10:21:46 +02:00
def self.run(options)
fill_defaults(options)
2025-08-11 14:18:59 +02:00
2025-08-13 10:21:46 +02:00
disk_img_path = root_img_path(options)
unless File.exist?(disk_img_path)
url = vm_archive_url(options)
Downloader.get(url, use_cache: false) do |path|
Archive.restore(path, vm_dir(options))
end
#raise "file do not exists"
end
2025-08-13 10:21:46 +02:00
# TODO:
# - if image path not exists, check the cache
# - if cache do not exists try to download
puts "Starting image: #{disk_img_path}"
Qemu.launch(
options[:arch],
disk_img_path,
2025-08-26 17:50:38 +02:00
code_fd: options[:code_fd],
vars_fd: options[:vars_fd],
2025-08-13 10:21:46 +02:00
cpus: [1, System.cpus - 2].max,
2025-08-26 17:50:38 +02:00
detach: options[:detached],
2025-08-28 17:48:34 +02:00
tpm: options[:tpm],
shell: options[:shell]
2025-08-13 10:21:46 +02:00
)
end
2025-08-26 08:52:28 +02:00
# vm setup windows --arch arm64 --iso /Users/agurgul/Downloads/win.iso --tpm
# vm setup windows --tpm --cdrom /Users/agurgul/Downloads/win.iso
2025-08-13 10:21:46 +02:00
def self.setup(options)
fill_defaults(options)
2025-08-11 14:18:59 +02:00
2025-08-26 08:52:28 +02:00
# puts options
# exit -1
2025-08-13 10:21:46 +02:00
2025-08-26 08:52:28 +02:00
get_cdrom_image(options) do |path|
disk_img_path = root_img_path(options)
create_disk_image(disk_img_path, 64000 * 4)
2025-08-13 10:21:46 +02:00
2025-08-11 17:36:55 +02:00
Qemu.launch(
2025-08-13 10:21:46 +02:00
options[:arch],
2025-08-11 17:36:55 +02:00
disk_img_path,
2025-08-26 17:50:38 +02:00
code_fd: options[:code_fd],
vars_fd: options[:vars_fd],
2025-08-11 17:36:55 +02:00
cpus: [1, System.cpus - 2].max,
cdrom: path,
2025-08-17 17:21:38 +02:00
detach: options[:detached],
2025-08-26 08:52:28 +02:00
display: DisplayMode.window,
tpm: options[:tpm]
2025-08-11 17:36:55 +02:00
)
2025-08-11 14:18:59 +02:00
end
end
2025-08-26 08:52:28 +02:00
def self.get_cdrom_image(options)
if options[:cdrom] == nil
url = distro(options[:name], options[:arch], :install)
Downloader.get(url) do |path|
yield path
end
else
yield options[:cdrom]
end
end
2025-08-11 14:18:59 +02:00
# size in MB
2025-08-11 17:36:55 +02:00
# lsof /Users/artur/.cache/dat/vm/debian/arm64/debian/root.img
2025-08-11 14:18:59 +02:00
def self.create_disk_image(path, size)
size_in_bytes = 1024 * 1024 * size
# Ensure the directory exists
dir = File.dirname(path)
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
File.open(path, "wb") do |f|
f.truncate(size_in_bytes)
# f.seek(size_in_bytes - 1)
# f.write("\0")
end
2025-08-11 10:20:07 +02:00
end
2025-08-17 10:30:42 +02:00
end