KVM host image creator.Jessica Lockwood
Revision | e2302b08a354c8084fd8586d5dc2d32c8a3f12a6 (tree) |
---|---|
Time | 2019-09-24 17:23:11 |
Author | Tatsuki Sugiura <sugi@nemu...> |
Commiter | Tatsuki Sugiura |
Change to use mbr for boot disk, split dirs for each disk.
@@ -6,75 +6,77 @@ require 'yaml' | ||
6 | 6 | |
7 | 7 | class SyncDirDef |
8 | 8 | DEFAULT_EXCLUDE = %w[/proc/* /sys/* /dev/mqueue /dev/hugepages /run/* /var/lib/os-prober/mount /swap /dev/shm/* /var/lib/lxcfs/*] |
9 | - attr_accessor :path, :size, :exclude, :size, :srcpath, :fs_features | |
9 | + attr_accessor :path, :size, :exclude, :size, :srcpath, :fs_features, :device | |
10 | 10 | def initialize(path: '/', size: 8, exclude: DEFAULT_EXCLUDE, srcpath: nil, fs_features: nil) |
11 | 11 | @path = path |
12 | 12 | @size = size.to_f |
13 | 13 | @exclude = (DEFAULT_EXCLUDE + [*exclude]).uniq |
14 | 14 | @srcpath = srcpath || path |
15 | 15 | @fs_features = fs_features |
16 | + @device = nil | |
16 | 17 | end |
17 | 18 | end |
18 | 19 | |
19 | - | |
20 | 20 | class ImageCreator |
21 | - attr_accessor :name, :dirs, :src_host | |
21 | + attr_accessor :name, :dirs, :src_host, :img_path_base | |
22 | + MiB = 1024 ** 2 | |
23 | + GiB = 1024 ** 3 | |
22 | 24 | |
23 | 25 | def initialize(name, dirs, src_host: nil) |
24 | 26 | @name = name |
25 | 27 | @dirs = dirs |
26 | 28 | @src_host = src_host || name |
29 | + @img_path_base = "#{name}_#{Time.now.strftime '%FT%T%z'}" | |
27 | 30 | end |
28 | 31 | |
29 | - def size | |
30 | - @size and return @size | |
31 | - @size = 3 * (1024**2) # alignment + BIOS boot partition + gap | |
32 | - dirs.each do |di| | |
33 | - @size += di.size * (1024**3) | |
34 | - end | |
35 | - @size | |
32 | + def imgpath(idx) | |
33 | + "#{img_path_base}_#{idx}.img" | |
36 | 34 | end |
37 | 35 | |
38 | - def imgpath | |
39 | - @imgpath ||= "#{name}_#{Time.now.strftime '%FT%T%z'}.img" | |
36 | + def create_disk | |
37 | + dirs.each_with_index do |di, idx| | |
38 | + _create_disk imgpath(idx), di, idx != 0 | |
39 | + end | |
40 | 40 | end |
41 | 41 | |
42 | - def create_disk | |
43 | - raise "Disk image #{imgpath} is already exists!" if File.exists? imgpath | |
44 | - puts "Creating disk image #{imgpath} (#{'%5.2f' % (size.to_f/(1024**3))} GiB)..." | |
45 | - last_mb = 1 | |
46 | - File.open(imgpath, "w") do |f| | |
47 | - f.truncate size | |
42 | + def _create_disk path, di, use_gpt = false | |
43 | + size_gb = di.size | |
44 | + raise "Disk image #{path} is already exists!" if File.exists? path | |
45 | + puts "Creating disk image #{path} (#{'%5.2f' % size_gb.to_f} GiB)..." | |
46 | + File.open(path, "w") do |f| | |
47 | + f.truncate(size_gb * GiB) | |
48 | 48 | end |
49 | - system("parted", "-s", imgpath, "mklabel", "gpt") or raise "Failed to create partition label" | |
50 | - system("parted", "-s", imgpath, "mkpart", "primary", "#{last_mb}MiB", "#{last_mb+1}MiB") or raise "Failed to create boot partition" | |
51 | - system("parted", "-s", imgpath, "set", "1", "bios_grub", "on") or raise "Failed to set bios boot partition" | |
52 | - last_mb += 1 | |
53 | - dirs.each do |di| | |
54 | - system("parted", "-s", imgpath, "mkpart", "primary", "#{last_mb}MiB", "#{di.size * 1024 + last_mb}MiB") or raise "Failed to create partition: #{l}" | |
55 | - last_mb += di.size * 1024 | |
49 | + system("parted", "-s", path, "mklabel", use_gpt ? 'gpt' : 'msdos') or raise "Failed to create partition label" | |
50 | + system("parted", "-s", path, "mkpart", "primary", "1MiB", "#{size_gb * 1024 - 1}MiB") or raise "Failed to create partition" | |
51 | + if use_gpt | |
52 | + system("parted", "-s", path, "name", "1", di.path) or raise "Failed to set part label" | |
53 | + else | |
54 | + system("parted", "-s", path, "set", "1", "boot", "on") or raise "Failed to set bios boot partition" | |
56 | 55 | end |
57 | 56 | puts "Image partition created." |
58 | - #system "sfdisk", "-l", imgpath | |
57 | + #system "sfdisk", "-l", path | |
59 | 58 | end |
60 | 59 | |
61 | 60 | def with_loopdev &block |
62 | 61 | begin |
63 | - system("kpartx", "-as", imgpath) or raise "Failed to map loop device" | |
64 | - maplines = `kpartx -l #{Shellwords.escape imgpath}`.split("\n") | |
65 | 62 | devices = [] |
66 | - maplines.each do |map| | |
67 | - devices << "/dev/mapper/#{map[/loop\d+p\d+/]}" | |
63 | + dirs.each_with_index do |di, idx| | |
64 | + system("kpartx", "-as", imgpath(idx)) or raise "Failed to map loop device" | |
65 | + di.device = "/dev/mapper/" + `kpartx -l #{Shellwords.escape imgpath(idx)}`.split("\n").first[/loop\d+p\d+/] | |
66 | + devices << di.device | |
68 | 67 | end |
69 | - yield devices, maplines.first[%r{/dev/loop\d+}] | |
68 | + yield devices | |
70 | 69 | ensure |
71 | - system "kpartx", "-d", imgpath, err: "/dev/null" | |
70 | + dirs.each_with_index do |di, idx| | |
71 | + system "kpartx", "-d", imgpath(idx), err: "/dev/null" | |
72 | + di.device = nil | |
73 | + end | |
72 | 74 | end |
73 | 75 | end |
74 | 76 | |
75 | 77 | def create_fs |
76 | - with_loopdev do |devices, root| | |
77 | - devices[1..-1].each_with_index do |(dev, _), index| | |
78 | + with_loopdev do |devices| | |
79 | + devices.each_with_index do |dev, index| | |
78 | 80 | puts "Creating filesystem on #{dev}..." |
79 | 81 | cmd = %w(mkfs.ext4 -q) |
80 | 82 | di = dirs[index] |
@@ -88,8 +90,8 @@ class ImageCreator | ||
88 | 90 | end |
89 | 91 | |
90 | 92 | def sync_dirs |
91 | - with_loopdev do |devices, root| | |
92 | - devices[1..-1].each_with_index do |dev, idx| | |
93 | + with_loopdev do |devices| | |
94 | + devices.each_with_index do |dev, idx| | |
93 | 95 | di = dirs[idx] |
94 | 96 | mount_point = "/mnt/ci-#{$$}-#{name}-#{idx}" |
95 | 97 | system("mkdir", "-p", mount_point) |
@@ -111,20 +113,28 @@ class ImageCreator | ||
111 | 113 | Dir.mktmpdir("ci-#{$$}-#{name}") do |dir| |
112 | 114 | system("cp", "-a", "/usr/lib/grub/i386-pc/boot.img", dir) or raise "Failed to copy boot.img" |
113 | 115 | coreimg = "#{dir}/core.img" |
114 | - system("grub-mkimage", "-o", coreimg, "-O", "i386-pc", "-p", "(hd0,gpt2)/boot/grub", "biosdisk", "part_gpt", "ext2", "gzio", "xzio", "lzopio") or raise "Failed to create grub core image." | |
115 | - with_loopdev do |devices, root| | |
116 | + system("grub-mkimage", "-o", coreimg, "-O", "i386-pc", "-p", "(hd0,msdos1)/boot/grub", "biosdisk", "part_msdos", "ext2", "gzio", "xzio", "lzopio") or raise "Failed to create grub core image." | |
117 | + with_loopdev do |devices| | |
118 | + root_dev = "/dev/#{devices.first[/loop\d+/]}" | |
116 | 119 | puts "Override grub with host version..." |
117 | - system("grub-bios-setup", "-d", dir, root) or raise "Failed to run grub-bios-setup" | |
118 | - rootfs_uuid=`blkid -o value -s UUID #{devices[1]}`.chomp("\n") | |
120 | + system("grub-bios-setup", "-d", dir, root_dev) or raise "Failed to run grub-bios-setup" | |
121 | + fs_uuids = devices.map { |d| `blkid -o value -s UUID #{d}`.chomp("\n") } | |
122 | + rootfs_uuid = fs_uuids.first | |
119 | 123 | puts "New rootfs UUID=#{rootfs_uuid}" |
120 | 124 | begin |
121 | - system("mount", devices[1], dir) | |
125 | + system("mount", devices.first, dir) or raise "Failed to mount #{devices.first} to #{dir}" | |
126 | + dirs[1..-1].each_with_index do |di, idx| | |
127 | + system "mkdir", "-p", "#{dir}#{di.path}" | |
128 | + system("mount", di.device, "#{dir}#{di.path}") or raise "Failed to mount #{di.device} to #{dir}#{path}" | |
129 | + end | |
122 | 130 | |
123 | 131 | puts "Rewrite fstab..." |
124 | - fstab = File.read "#{dir}/etc/fstab" | |
125 | - fstab.gsub!(%r{^(UUID=|/)\S+(\s+/\s+)}, "UUID=#{rootfs_uuid}\\2") | |
126 | - fstab.gsub!(%r{^(\S+\s+\S+\s+\S+\s+sw(?=\b))}, '#\1') | |
127 | - File.write "#{dir}/etc/fstab", fstab | |
132 | + File.open "#{dir}/etc/fstab", "w" do |f| | |
133 | + devices.map.with_index { |d, idx| | |
134 | + f << %W(UUID=#{fs_uuids[idx]} #{dirs[idx].path} ext4 defaults,noatime 0 #{dirs[idx].path == '/' ? 1 : 2}).join("\t") | |
135 | + f << "\n" | |
136 | + } | |
137 | + end | |
128 | 138 | |
129 | 139 | unless File.exists? "#{dir}/vmlinuz" |
130 | 140 | system("chroot", dir, "apt-get", "-qy", "update") |
@@ -144,7 +154,7 @@ class ImageCreator | ||
144 | 154 | system({'DEBIAN_FRONTEND' => 'noninteractive'}, "chroot", dir, "apt-get", "-y", "install", "grub-pc") |
145 | 155 | File.write "#{dir}/boot/grub/grub.cfg", <<-EOC |
146 | 156 | set timeout=5 |
147 | - insmod part_gpt | |
157 | + insmod part_msdos | |
148 | 158 | insmod ext2 |
149 | 159 | insmod linux |
150 | 160 | search --no-floppy --fs-uuid --set=root #{rootfs_uuid} |
@@ -155,6 +165,9 @@ class ImageCreator | ||
155 | 165 | EOC |
156 | 166 | end |
157 | 167 | ensure |
168 | + dirs.reverse[0..-2].each do |di, idx| | |
169 | + system("umount", "#{dir}#{di.path}") | |
170 | + end | |
158 | 171 | system("umount", dir) |
159 | 172 | end |
160 | 173 | end |