]> git.proxmox.com Git - qemu-server.git/blame - test/MigrationTest/QemuMigrateMock.pm
tests: fixup simple1-template.conf.cmd
[qemu-server.git] / test / MigrationTest / QemuMigrateMock.pm
CommitLineData
48831384
FE
1package MigrationTest::QemuMigrateMock;
2
3use strict;
4use warnings;
5
6use JSON;
7use Test::MockModule;
8
9use MigrationTest::Shared;
10
11use PVE::API2::Qemu;
12use PVE::Storage;
13use PVE::Tools qw(file_set_contents file_get_contents);
14
15use PVE::CLIHandler;
16use base qw(PVE::CLIHandler);
17
18my $RUN_DIR_PATH = $ENV{RUN_DIR_PATH} or die "no RUN_DIR_PATH set\n";
19my $QM_LIB_PATH = $ENV{QM_LIB_PATH} or die "no QM_LIB_PATH set\n";
20
21my $source_volids = decode_json(file_get_contents("${RUN_DIR_PATH}/source_volids"));
22my $source_vdisks = decode_json(file_get_contents("${RUN_DIR_PATH}/source_vdisks"));
23my $vm_status = decode_json(file_get_contents("${RUN_DIR_PATH}/vm_status"));
24my $expected_calls = decode_json(file_get_contents("${RUN_DIR_PATH}/expected_calls"));
25my $fail_config = decode_json(file_get_contents("${RUN_DIR_PATH}/fail_config"));
26my $storage_migrate_map = decode_json(file_get_contents("${RUN_DIR_PATH}/storage_migrate_map"));
27my $migrate_params = decode_json(file_get_contents("${RUN_DIR_PATH}/migrate_params"));
28
29my $test_vmid = $migrate_params->{vmid};
30my $test_target = $migrate_params->{target};
31my $test_opts = $migrate_params->{opts};
32my $current_log = '';
33
34my $vm_stop_executed = 0;
35
36# mocked modules
37
38my $inotify_module = Test::MockModule->new("PVE::INotify");
39$inotify_module->mock(
40 nodename => sub {
41 return 'pve0';
42 },
43);
44
45$MigrationTest::Shared::qemu_config_module->mock(
46 move_config_to_node => sub {
47 my ($self, $vmid, $target) = @_;
48 die "moving wrong config: '$vmid'\n" if $vmid ne $test_vmid;
49 die "moving config to wrong node: '$target'\n" if $target ne $test_target;
50 delete $expected_calls->{move_config_to_node};
51 },
52);
53
54my $qemu_migrate_module = Test::MockModule->new("PVE::QemuMigrate");
55$qemu_migrate_module->mock(
56 finish_tunnel => sub {
57 delete $expected_calls->{'finish_tunnel'};
58 return;
59 },
60 fork_tunnel => sub {
61 die "fork_tunnel (mocked) - implement me\n"; # currently no call should lead here
62 },
63 read_tunnel => sub {
64 die "read_tunnel (mocked) - implement me\n"; # currently no call should lead here
65 },
66 start_remote_tunnel => sub {
67 my ($self, $raddr, $rport, $ruri, $unix_socket_info) = @_;
68 $expected_calls->{'finish_tunnel'} = 1;
69 $self->{tunnel} = {
70 writer => "mocked",
71 reader => "mocked",
72 pid => 123456,
73 version => 1,
74 };
75 },
76 write_tunnel => sub {
77 my ($self, $tunnel, $timeout, $command) = @_;
78
79 if ($command =~ m/^resume (\d+)$/) {
80 my $vmid = $1;
81 die "resuming wrong VM '$vmid'\n" if $vmid ne $test_vmid;
82 return;
83 }
84 die "write_tunnel (mocked) - implement me: $command\n";
85 },
86 log => sub {
87 my ($self, $level, $message) = @_;
88 $current_log .= "$level: $message\n";
89 },
90 mon_cmd => sub {
91 my ($vmid, $command, %params) = @_;
92
93 if ($command eq 'nbd-server-start') {
94 return;
95 } elsif ($command eq 'block-dirty-bitmap-add') {
96 my $drive = $params{node};
97 delete $expected_calls->{"block-dirty-bitmap-add-${drive}"};
98 return;
99 } elsif ($command eq 'block-dirty-bitmap-remove') {
100 return;
101 } elsif ($command eq 'query-migrate') {
102 return { status => 'failed' } if $fail_config->{'query-migrate'};
103 return { status => 'completed' };
104 } elsif ($command eq 'migrate') {
105 return;
106 } elsif ($command eq 'migrate-set-parameters') {
107 return;
108 } elsif ($command eq 'migrate_cancel') {
109 return;
110 }
111 die "mon_cmd (mocked) - implement me: $command";
112 },
113 transfer_replication_state => sub {
114 delete $expected_calls->{transfer_replication_state};
115 },
116 switch_replication_job_target => sub {
117 delete $expected_calls->{switch_replication_job_target};
118 },
119);
120
121$MigrationTest::Shared::qemu_server_module->mock(
122 kvm_user_version => sub {
123 return "5.0.0";
124 },
125 qemu_blockjobs_cancel => sub {
126 return;
127 },
128 qemu_drive_mirror => sub {
129 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
130
131 die "drive_mirror with wrong vmid: '$vmid'\n" if $vmid ne $test_vmid;
132 die "qemu_drive_mirror '$drive' error\n" if $fail_config->{qemu_drive_mirror}
133 && $fail_config->{qemu_drive_mirror} eq $drive;
134
135 my $nbd_info = decode_json(file_get_contents("${RUN_DIR_PATH}/nbd_info"));
136 die "target does not expect drive mirror for '$drive'\n"
137 if !defined($nbd_info->{$drive});
138 delete $nbd_info->{$drive};
139 file_set_contents("${RUN_DIR_PATH}/nbd_info", to_json($nbd_info));
140 },
141 qemu_drive_mirror_monitor => sub {
0783c3c2
FE
142 my ($vmid, $vmiddst, $jobs, $completion, $qga) = @_;
143
144 if ($fail_config->{qemu_drive_mirror_monitor} &&
145 $fail_config->{qemu_drive_mirror_monitor} eq $completion) {
146 die "qemu_drive_mirror_monitor '$completion' error\n";
147 }
48831384
FE
148 return;
149 },
150 set_migration_caps => sub {
151 return;
152 },
153 vm_stop => sub {
154 $vm_stop_executed = 1;
155 delete $expected_calls->{'vm_stop'};
156 },
157);
158
159my $qemu_server_cpuconfig_module = Test::MockModule->new("PVE::QemuServer::CPUConfig");
160$qemu_server_cpuconfig_module->mock(
161 get_cpu_from_running_vm => sub {
162 die "invalid test: if you specify a custom CPU model you need to " .
163 "specify runningcpu as well\n" if !defined($vm_status->{runningcpu});
164 return $vm_status->{runningcpu};
165 }
166);
167
168my $qemu_server_helpers_module = Test::MockModule->new("PVE::QemuServer::Helpers");
169$qemu_server_helpers_module->mock(
170 vm_running_locally => sub {
171 return $vm_status->{running} && !$vm_stop_executed;
172 },
173);
174
175my $qemu_server_machine_module = Test::MockModule->new("PVE::QemuServer::Machine");
176$qemu_server_machine_module->mock(
177 qemu_machine_pxe => sub {
178 die "invalid test: no runningmachine specified\n"
179 if !defined($vm_status->{runningmachine});
180 return $vm_status->{runningmachine};
181 },
182);
183
184my $ssh_info_module = Test::MockModule->new("PVE::SSHInfo");
185$ssh_info_module->mock(
186 get_ssh_info => sub {
187 my ($node, $network_cidr) = @_;
188 return {
189 ip => '1.2.3.4',
190 name => $node,
191 network => $network_cidr,
192 };
193 },
194);
195
196$MigrationTest::Shared::storage_module->mock(
197 storage_migrate => sub {
198 my ($cfg, $volid, $target_sshinfo, $target_storeid, $opts, $logfunc) = @_;
199
200 die "storage_migrate '$volid' error\n" if $fail_config->{storage_migrate}
201 && $fail_config->{storage_migrate} eq $volid;
202
203 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
204
205 die "invalid test: need to add entry for '$volid' to storage_migrate_map\n"
206 if $storeid ne $target_storeid && !defined($storage_migrate_map->{$volid});
207
208 my $target_volname = $storage_migrate_map->{$volid} // $opts->{target_volname} // $volname;
209 my $target_volid = "${target_storeid}:${target_volname}";
210 MigrationTest::Shared::add_target_volid($target_volid);
211
212 return $target_volid;
213 },
214 vdisk_list => sub { # expects vmid to be set
215 my ($cfg, $storeid, $vmid, $vollist) = @_;
216
217 my @storeids = defined($storeid) ? ($storeid) : keys %{$source_vdisks};
218
219 my $res = {};
220 foreach my $storeid (@storeids) {
221 my $list_for_storeid = $source_vdisks->{$storeid};
222 my @list_for_vm = grep { $_->{vmid} eq $vmid } @{$list_for_storeid};
223 $res->{$storeid} = \@list_for_vm;
224 }
225 return $res;
226 },
227 vdisk_free => sub {
228 my ($scfg, $volid) = @_;
229
93a1c63f
FE
230 PVE::Storage::parse_volume_id($volid);
231
48831384
FE
232 die "vdisk_free '$volid' error\n" if defined($fail_config->{vdisk_free})
233 && $fail_config->{vdisk_free} eq $volid;
234
235 delete $source_volids->{$volid};
236 },
237);
238
239$MigrationTest::Shared::tools_module->mock(
240 get_host_address_family => sub {
241 die "get_host_address_family (mocked) - implement me\n"; # currently no call should lead here
242 },
243 next_migrate_port => sub {
244 die "next_migrate_port (mocked) - implement me\n"; # currently no call should lead here
245 },
246 run_command => sub {
247 my ($cmd_tail, %param) = @_;
248
249 my $cmd_msg = to_json($cmd_tail);
250
251 my $cmd = shift @{$cmd_tail};
252
253 if ($cmd eq '/usr/bin/ssh') {
254 while (scalar(@{$cmd_tail})) {
255 $cmd = shift @{$cmd_tail};
256 if ($cmd eq '/bin/true') {
257 return 0;
258 } elsif ($cmd eq 'qm') {
259 $cmd = shift @{$cmd_tail};
260 if ($cmd eq 'start') {
261 delete $expected_calls->{ssh_qm_start};
262
263 delete $vm_status->{runningmachine};
264 delete $vm_status->{runningcpu};
265
266 my @options = ( @{$cmd_tail} );
267 while (scalar(@options)) {
268 my $opt = shift @options;
269 if ($opt eq '--machine') {
270 $vm_status->{runningmachine} = shift @options;
271 } elsif ($opt eq '--force-cpu') {
272 $vm_status->{runningcpu} = shift @options;
273 }
274 }
275
276 return $MigrationTest::Shared::tools_module->original('run_command')->([
277 '/usr/bin/perl',
278 "-I${QM_LIB_PATH}",
279 "-I${QM_LIB_PATH}/test",
280 "${QM_LIB_PATH}/test/MigrationTest/QmMock.pm",
281 'start',
282 @{$cmd_tail},
283 ], %param);
284
285 } elsif ($cmd eq 'nbdstop') {
286 delete $expected_calls->{ssh_nbdstop};
287 return 0;
288 } elsif ($cmd eq 'resume') {
289 return 0;
290 } elsif ($cmd eq 'unlock') {
291 my $vmid = shift @{$cmd_tail};;
292 die "unlocking wrong vmid: $vmid\n" if $vmid ne $test_vmid;
293 PVE::QemuConfig->remove_lock($vmid);
294 return 0;
295 } elsif ($cmd eq 'stop') {
296 return 0;
297 }
298 die "run_command (mocked) ssh qm command - implement me: ${cmd_msg}";
299 } elsif ($cmd eq 'pvesm') {
300 $cmd = shift @{$cmd_tail};
301 if ($cmd eq 'free') {
302 my $volid = shift @{$cmd_tail};
93a1c63f 303 PVE::Storage::parse_volume_id($volid);
48831384
FE
304 return 1 if $fail_config->{ssh_pvesm_free}
305 && $fail_config->{ssh_pvesm_free} eq $volid;
306 MigrationTest::Shared::remove_target_volid($volid);
307 return 0;
308 }
309 die "run_command (mocked) ssh pvesm command - implement me: ${cmd_msg}";
310 }
311 }
312 die "run_command (mocked) ssh command - implement me: ${cmd_msg}";
313 }
314 die "run_command (mocked) - implement me: ${cmd_msg}";
315 },
316);
317
318eval { PVE::QemuMigrate->migrate($test_target, undef, $test_vmid, $test_opts) };
319my $error = $@;
320
321file_set_contents("${RUN_DIR_PATH}/source_volids", to_json($source_volids));
322file_set_contents("${RUN_DIR_PATH}/vm_status", to_json($vm_status));
323file_set_contents("${RUN_DIR_PATH}/expected_calls", to_json($expected_calls));
324file_set_contents("${RUN_DIR_PATH}/log", $current_log);
325
326die $error if $error;
327
3281;