line |
stmt |
code |
1
|
|
# Copyright 2009-2013 Bernhard M. Wiedemann |
2
|
|
# Copyright 2012-2021 SUSE LLC |
3
|
|
# SPDX-License-Identifier: GPL-2.0-or-later |
4
|
|
|
5
|
|
|
6
|
|
use Mojo::Base 'consoles::sshXtermVt', -signatures; |
7
|
2
|
use autodie ':all'; |
|
2
|
|
|
2
|
|
8
|
2
|
require IPC::System::Simple; |
|
2
|
|
|
2
|
|
9
|
|
use XML::LibXML; |
10
|
2
|
use File::Temp 'tempfile'; |
|
2
|
|
|
2
|
|
11
|
2
|
use File::Basename; |
|
2
|
|
|
2
|
|
12
|
2
|
use Mojo::JSON qw(decode_json); |
|
2
|
|
|
2
|
|
13
|
2
|
|
|
2
|
|
|
2
|
|
14
|
|
use backend::svirt; |
15
|
2
|
|
|
2
|
|
|
2
|
|
16
|
|
has [qw(instance name vmm_family vmm_type)]; |
17
|
|
|
18
|
|
my $self = $class->SUPER::new($testapi_console, $args); |
19
|
8
|
|
|
8
|
|
|
8
|
|
|
8
|
|
|
8
|
|
20
|
8
|
$self->instance($bmwqemu::vars{VIRSH_INSTANCE} // 1); |
21
|
|
# default name |
22
|
8
|
$self->name("openQA-SUT-" . $self->instance); |
23
|
|
$self->vmm_family($bmwqemu::vars{VIRSH_VMM_FAMILY} // 'kvm'); |
24
|
8
|
$self->vmm_type($bmwqemu::vars{VIRSH_VMM_TYPE} // 'hvm'); |
25
|
8
|
|
26
|
8
|
return $self; |
27
|
|
} |
28
|
8
|
|
29
|
|
my $args = $self->{args}; |
30
|
|
|
31
|
0
|
# initialize SSH console(s) |
|
0
|
|
|
0
|
|
32
|
0
|
$self->_init_ssh($args); |
33
|
|
|
34
|
|
# start Xvnc |
35
|
0
|
$self->SUPER::activate; |
36
|
|
|
37
|
|
$self->_init_xml(); |
38
|
0
|
} |
39
|
|
|
40
|
0
|
# initializes the SSH credentials, $domain is used to distinguish between the |
41
|
|
# regular SSH and the one to the VMware server |
42
|
|
$self->{ssh_credentials} = { |
43
|
|
default => { |
44
|
|
hostname => $args->{hostname} || die('we need a hostname to ssh to'), |
45
|
5
|
username => $args->{username}, |
|
5
|
|
|
5
|
|
|
5
|
|
46
|
|
password => $args->{password}, |
47
|
|
} |
48
|
|
}; |
49
|
|
if ($self->vmm_family eq 'vmware') { |
50
|
|
$self->{ssh_credentials}->{sshVMwareServer} = |
51
|
|
{ |
52
|
5
|
hostname => $bmwqemu::vars{VMWARE_HOST} || die('Need variable VMWARE_HOST'), |
53
|
5
|
password => $bmwqemu::vars{VMWARE_PASSWORD} || die('Need variable VMWARE_PASSWORD'), |
54
|
|
username => 'root', |
55
|
|
}; |
56
|
|
} |
57
|
3
|
} |
58
|
|
|
59
|
|
$domain //= 'default'; |
60
|
|
die("Unknown ssh credentials domain $domain") unless (exists($self->{ssh_credentials}->{$domain})); |
61
|
|
return %{$self->{ssh_credentials}->{$domain}}; |
62
|
|
} |
63
|
59
|
|
|
59
|
|
|
59
|
|
|
59
|
|
64
|
59
|
# creates an XML document to configure the libvirt domain |
65
|
59
|
# (see https://libvirt.org/formatdomain.html for the specification of that config file) |
66
|
59
|
my $instance = $self->instance; |
|
59
|
|
67
|
|
my $doc = $self->{domainxml} = XML::LibXML::Document->new; |
68
|
|
my $root = $doc->createElement('domain'); |
69
|
|
$root->setAttribute(type => $self->vmm_family); |
70
|
|
$doc->setDocumentElement($root); |
71
|
7
|
|
|
7
|
|
|
7
|
|
|
7
|
|
72
|
7
|
my $elem; |
73
|
7
|
$elem = $doc->createElement('name'); |
74
|
7
|
$elem->appendTextNode($self->name); |
75
|
7
|
$root->appendChild($elem); |
76
|
7
|
|
77
|
|
my $openqa_hostname = $bmwqemu::vars{OPENQA_HOSTNAME} // 'no-webui-set'; |
78
|
7
|
$elem = $doc->createElement('description'); |
79
|
7
|
$elem->appendTextNode("openQA WebUI: $openqa_hostname ($instance): "); |
80
|
7
|
$elem->appendTextNode($bmwqemu::vars{NAME} // '0-no-scenario'); |
81
|
7
|
$root->appendChild($elem); |
82
|
|
|
83
|
7
|
$elem = $doc->createElement('memory'); |
84
|
7
|
$elem->appendTextNode($bmwqemu::vars{QEMURAM} or die 'Need variable QEMURAM'); |
85
|
7
|
$elem->setAttribute(unit => 'MiB'); |
86
|
7
|
$root->appendChild($elem); |
87
|
7
|
|
88
|
|
$elem = $doc->createElement('vcpu'); |
89
|
7
|
$elem->appendTextNode($bmwqemu::vars{QEMUCPUS} or die 'Need variable QEMUCPUS'); |
90
|
7
|
$root->appendChild($elem); |
91
|
7
|
|
92
|
7
|
my $os = $doc->createElement('os'); |
93
|
|
$root->appendChild($os); |
94
|
7
|
|
95
|
7
|
$elem = $doc->createElement('type'); |
96
|
7
|
$elem->appendTextNode($self->vmm_type); |
97
|
|
$os->appendChild($elem); |
98
|
7
|
|
99
|
7
|
# Following 'features' are required for VM to correctly shutdown |
100
|
|
my $features = $doc->createElement('features'); |
101
|
7
|
$root->appendChild($features); |
102
|
7
|
$elem = $doc->createElement('acpi'); |
103
|
7
|
$features->appendChild($elem); |
104
|
|
$elem = $doc->createElement('apic'); |
105
|
|
$features->appendChild($elem); |
106
|
7
|
$elem = $doc->createElement('pae'); |
107
|
7
|
$features->appendChild($elem); |
108
|
7
|
|
109
|
7
|
if ($self->vmm_family eq 'xen' and $self->vmm_type eq 'linux') { |
110
|
7
|
$elem = $doc->createElement('kernel'); |
111
|
7
|
$elem->appendTextNode('/usr/lib/grub2/x86_64-xen/grub.xen'); |
112
|
7
|
$os->appendChild($elem); |
113
|
7
|
} |
114
|
|
|
115
|
7
|
# The root of all problems is this: Xen closes VNC and serial console connections |
116
|
0
|
# on reboot. Unlike KVM. So, to know when we are restarting if we are in the |
117
|
0
|
# state before, or after restart we have to configure libvirt to destroy |
118
|
0
|
# (i.e. turn off) the VM. Then we have to explicitly start it define_and_start. |
119
|
|
# Even if KVM does not need this, from test code POV it's convenient to have it. |
120
|
|
if ($self->vmm_family eq 'xen' || $self->vmm_family eq 'kvm') { |
121
|
|
$elem = $doc->createElement('on_reboot'); |
122
|
|
$elem->appendTextNode('destroy'); |
123
|
|
$root->appendChild($elem); |
124
|
|
} |
125
|
|
|
126
|
7
|
if ($bmwqemu::vars{UEFI} and $bmwqemu::vars{ARCH} eq 'x86_64' and !$bmwqemu::vars{BIOS} and $bmwqemu::vars{VIRSH_VMM_FAMILY} ne 'hyperv') { |
127
|
4
|
foreach my $firmware (@bmwqemu::ovmf_locations) { |
128
|
4
|
if (!$self->run_cmd("test -e $firmware")) { |
129
|
4
|
$bmwqemu::vars{BIOS} = $firmware; |
130
|
|
$elem = $doc->createElement('loader'); |
131
|
|
$elem->appendTextNode($firmware); |
132
|
7
|
$os->appendChild($elem); |
133
|
2
|
last; |
134
|
5
|
} |
135
|
1
|
} |
136
|
1
|
if (!$bmwqemu::vars{BIOS}) { |
137
|
1
|
# We know this won't go well. |
138
|
1
|
my $virsh_hostname = $bmwqemu::vars{VIRSH_HOSTNAME} // ''; |
139
|
1
|
die "No UEFI firmware can be found on hypervisor '$virsh_hostname'. Please specify BIOS or UEFI_BIOS or install an appropriate package."; |
140
|
|
} |
141
|
|
} |
142
|
2
|
|
143
|
|
$self->{devices_element} = $doc->createElement('devices'); |
144
|
1
|
$root->appendChild($self->{devices_element}); |
145
|
1
|
|
146
|
|
return; |
147
|
|
} |
148
|
|
|
149
|
6
|
# allows to add and remove elements in the domain XML |
150
|
6
|
# - add text node: |
151
|
|
# change_domain_element(funny => guy => 'hello'); |
152
|
6
|
# - remove node: |
153
|
|
# change_domain_element(funny => guy => undef); |
154
|
|
# - set attributes: |
155
|
|
# change_domain_element(funny => guy => { hello => 'world' }); |
156
|
|
my $doc = $self->{domainxml}; |
157
|
|
my $elem = $doc->getElementsByTagName('domain')->[0]; |
158
|
|
|
159
|
|
while (@args > 1) { |
160
|
|
my $parent = $elem; |
161
|
|
my $tag_name = shift @args; |
162
|
0
|
$elem = $parent->getElementsByTagName($tag_name)->[0]; |
|
0
|
|
|
0
|
|
|
0
|
|
163
|
0
|
# create it if not existent |
164
|
0
|
if (!$elem) { |
165
|
|
$elem = $doc->createElement($tag_name); |
166
|
0
|
$parent->appendChild($elem); |
167
|
0
|
} |
168
|
0
|
} |
169
|
0
|
my $tag = $args[0]; |
170
|
|
if (!$tag) { |
171
|
0
|
# for undef delete the node |
172
|
0
|
$elem->unbindNode(); |
173
|
0
|
} |
174
|
|
else { |
175
|
|
if (ref($tag) eq 'HASH') { |
176
|
0
|
# for hashes set the attributes |
177
|
0
|
while (my ($key, $value) = each %$tag) { |
178
|
|
$elem->setAttribute($key => $value); |
179
|
0
|
} |
180
|
|
} |
181
|
|
else { |
182
|
0
|
$elem->appendTextNode($tag); |
183
|
|
} |
184
|
0
|
} |
185
|
0
|
|
186
|
|
return; |
187
|
|
} |
188
|
|
|
189
|
0
|
# adds the serial console used for the serial log |
190
|
|
my $doc = $self->{domainxml}; |
191
|
|
my $devices = $self->{devices_element}; |
192
|
|
|
193
|
0
|
my $console = $doc->createElement($args->{pty_dev} || backend::svirt::SERIAL_CONSOLE_DEFAULT_DEVICE); |
194
|
|
$console->setAttribute(type => $args->{pty_dev_type} || 'pty'); |
195
|
|
$devices->appendChild($console); |
196
|
|
|
197
|
2
|
my $elem = $doc->createElement('target'); |
|
2
|
|
|
2
|
|
|
2
|
|
198
|
2
|
if ($args->{target_type}) { |
199
|
2
|
$elem->setAttribute(type => $args->{target_type}); |
200
|
|
} |
201
|
2
|
$elem->setAttribute(port => $args->{target_port}); |
202
|
2
|
$console->appendChild($elem); |
203
|
2
|
|
204
|
|
if ($args->{protocol_type}) { |
205
|
2
|
my $elem = $doc->createElement('protocol'); |
206
|
2
|
$elem->setAttribute(type => $args->{protocol_type}); |
207
|
0
|
$console->appendChild($elem); |
208
|
|
} |
209
|
2
|
|
210
|
2
|
if ($args->{source}) { |
211
|
|
my $elem = $doc->createElement('source'); |
212
|
2
|
$elem->setAttribute(mode => 'bind'); |
213
|
0
|
$elem->setAttribute(host => '0.0.0.0'); |
214
|
0
|
$elem->setAttribute(service => $bmwqemu::vars{VMWARE_SERIAL_PORT}); |
215
|
0
|
$console->appendChild($elem); |
216
|
|
} |
217
|
|
|
218
|
2
|
return; |
219
|
0
|
} |
220
|
0
|
|
221
|
0
|
# this is an equivalent of QEMU's '-vnc' option for tests where we watch |
222
|
0
|
# the system from boot on (e.g. JeOS) |
223
|
0
|
my $doc = $self->{domainxml}; |
224
|
|
my $devices = $self->{devices_element}; |
225
|
|
|
226
|
2
|
my $graphics = $doc->createElement('graphics'); |
227
|
|
$graphics->setAttribute(type => 'vnc'); |
228
|
|
$graphics->setAttribute(port => $args->{port}); |
229
|
|
$graphics->setAttribute(autoport => 'no'); |
230
|
|
$graphics->setAttribute(listen => '0.0.0.0'); |
231
|
1
|
$graphics->setAttribute(sharePolicy => 'force-shared'); |
|
1
|
|
|
1
|
|
|
1
|
|
232
|
1
|
if (my $vnc_password = $testapi::password) { |
233
|
1
|
$graphics->setAttribute(passwd => $vnc_password); |
234
|
|
} |
235
|
1
|
$devices->appendChild($graphics); |
236
|
1
|
|
237
|
1
|
my $elem = $doc->createElement('listen'); |
238
|
1
|
$elem->setAttribute(type => 'address'); |
239
|
1
|
$elem->setAttribute(address => '0.0.0.0'); |
240
|
1
|
$graphics->appendChild($elem); |
241
|
1
|
|
242
|
0
|
return; |
243
|
|
} |
244
|
1
|
|
245
|
|
my $doc = $self->{domainxml}; |
246
|
1
|
my $devices = $self->{devices_element}; |
247
|
1
|
|
248
|
1
|
my $input = $doc->createElement('input'); |
249
|
1
|
$input->setAttribute(type => $args->{type}); |
250
|
|
$input->setAttribute(bus => $args->{bus}); |
251
|
1
|
$devices->appendChild($input); |
252
|
|
|
253
|
|
return; |
254
|
0
|
} |
|
0
|
|
|
0
|
|
|
0
|
|
255
|
0
|
|
256
|
0
|
# network stuff |
257
|
|
my $doc = $self->{domainxml}; |
258
|
0
|
my $devices = $self->{devices_element}; |
259
|
0
|
|
260
|
0
|
my $type = delete $args->{type}; |
261
|
0
|
my $interface = $doc->createElement('interface'); |
262
|
|
$interface->setAttribute(type => $type); |
263
|
0
|
$devices->appendChild($interface); |
264
|
|
|
265
|
|
for my $key (keys %$args) { |
266
|
|
my $elem = $doc->createElement($key); |
267
|
0
|
my $value = $args->{$key}; |
|
0
|
|
|
0
|
|
|
0
|
|
268
|
0
|
for my $attr (keys %$value) { |
269
|
0
|
$elem->setAttribute($attr => $value->{$attr}); |
270
|
|
} |
271
|
0
|
$interface->appendChild($elem); |
272
|
0
|
} |
273
|
0
|
|
274
|
0
|
return; |
275
|
|
} |
276
|
0
|
|
277
|
0
|
my $size = $args->{size} || '20G'; |
278
|
0
|
if ($self->vmm_family eq 'vmware') { |
279
|
0
|
my $vmware_disk_path = $vmware_openqa_datastore . $file; |
280
|
0
|
# Power VM off, delete it's disk image, and create it again. |
281
|
|
# Than wait for some time for the VM to *really* turn off. |
282
|
0
|
my $cmd = |
283
|
|
"( set -x; vmid=\$(vim-cmd vmsvc/getallvms | awk \'/$name/ { print \$1 }\');" . |
284
|
|
'if [ $vmid ]; then ' . |
285
|
0
|
'vim-cmd vmsvc/power.off $vmid;' . |
286
|
|
'vim-cmd vmsvc/destroy $vmid;' . |
287
|
|
'fi;' . |
288
|
22
|
"vmkfstools -v1 -U $vmware_disk_path;" . |
|
22
|
|
|
22
|
|
|
22
|
|
|
22
|
|
|
22
|
|
|
22
|
|
|
22
|
|
289
|
22
|
"vmkfstools -v1 -c $size --diskformat thin $vmware_disk_path; sleep 10 ) 2>&1"; |
290
|
22
|
my $retval = $self->run_cmd($cmd, domain => 'sshVMwareServer'); |
291
|
6
|
die "Can't create VMware image $vmware_disk_path" if $retval; |
292
|
|
} |
293
|
|
else { |
294
|
6
|
$file = $basedir . $file; |
295
|
|
my $bucket = 5; |
296
|
|
# Avoid qemu-img's failure to get a write lock to be the reason for a job to fail |
297
|
|
while (1) { |
298
|
|
my ($ret, $stdout, $stderr) = $self->run_cmd("qemu-img create $file $size -f qcow2", wantarray => 1); |
299
|
|
if ($stderr =~ /lock/i) { |
300
|
|
$bucket--; |
301
|
|
die "Too many attempts to format HDD" unless $bucket; |
302
|
6
|
bmwqemu::diag("Resource is still not free, waiting a bit more. $bucket attempts left"); |
303
|
6
|
sleep 5; |
304
|
|
next; |
305
|
|
} |
306
|
16
|
last unless $ret; |
307
|
16
|
} |
308
|
|
} |
309
|
16
|
return $file; |
310
|
24
|
} |
311
|
24
|
|
312
|
9
|
# If the file exists, make sure someone else is not copying it there right now, |
313
|
9
|
# otherwise copy image from NFS datastore. |
314
|
8
|
my $nfs_dir = $backingfile ? 'hdd' : 'iso'; |
315
|
8
|
my $vmware_nfs_datastore = $bmwqemu::vars{VMWARE_NFS_DATASTORE} or die 'Need variable VMWARE_NFS_DATASTORE'; |
316
|
8
|
my $cmd = |
317
|
|
"if test -e $vmware_openqa_datastore$file_basename; then " . |
318
|
15
|
"while lsof | grep 'cp.*$file_basename'; do " . |
319
|
|
"echo File $file_basename is being copied by other process, sleeping for 60 seconds; sleep 60;" . |
320
|
|
'done;' . |
321
|
21
|
'else ' . |
322
|
|
"cp /vmfs/volumes/$vmware_nfs_datastore/$nfs_dir/$file_basename $vmware_openqa_datastore;" . |
323
|
|
'fi;'; |
324
|
2
|
my $retval = $self->run_cmd($cmd, domain => 'sshVMwareServer'); |
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
325
|
|
die "Can't copy VMware image $file_basename" if $retval; |
326
|
|
return unless $backingfile; |
327
|
2
|
# Power VM off, delete it's disk image, and create it again. |
328
|
2
|
# Than wait for some time for the VM to *really* turn off. |
329
|
2
|
$cmd = |
330
|
|
"( set -x; vmid=\$(vim-cmd vmsvc/getallvms | awk \'/$name/ { print \$1 }\');" . |
331
|
|
'if [ $vmid ]; then ' . |
332
|
|
'vim-cmd vmsvc/power.off $vmid;' . |
333
|
|
'fi;' . |
334
|
|
"vmkfstools -v1 -U $vmware_disk_path_thinfile;" . |
335
|
|
"vmkfstools -v1 -i $vmware_disk_path --diskformat thin $vmware_disk_path_thinfile; sleep 10 ) 2>&1"; |
336
|
|
$retval = $self->run_cmd($cmd, domain => 'sshVMwareServer'); |
337
|
2
|
die "Can't create thin VMware image" if $retval; |
338
|
2
|
} |
339
|
2
|
|
340
|
|
$self->run_cmd(sprintf("rsync -av '$file' '$basedir/%s'", $file_basename)) && die 'rsync failed'; |
341
|
|
if ($file_basename =~ /(.*)\.xz$/) { |
342
|
1
|
$self->run_cmd(sprintf("nice ionice unxz -f -k '$basedir/%s'", $file_basename)) unless -e "$basedir$1"; |
343
|
|
$file_basename = $1; |
344
|
|
} |
345
|
|
} |
346
|
|
|
347
|
|
# Copy image to VM host |
348
|
|
die 'No file given' unless $args->{file}; |
349
|
1
|
my $file_basename = basename($args->{file}); |
350
|
1
|
my $backingfile = $args->{backingfile}; |
351
|
|
my $vmware_disk_path = $vmware_openqa_datastore . $file_basename; |
352
|
|
my $vmware_disk_path_thinfile = $vmware_disk_path =~ s/\.vmdk/_${name}_thinfile\.vmdk/r; |
353
|
8
|
if ($cdrom || $backingfile) { |
|
8
|
|
|
8
|
|
|
8
|
|
|
8
|
|
|
8
|
|
354
|
8
|
if ($self->vmm_family eq 'vmware') { |
355
|
8
|
$self->_copy_image_vmware($name, $backingfile, $file_basename, $vmware_openqa_datastore, $vmware_disk_path, $vmware_disk_path_thinfile); |
356
|
1
|
} |
357
|
1
|
else { |
358
|
|
$self->_copy_image_else($args->{file}, $file_basename, $basedir); |
359
|
|
} |
360
|
|
} |
361
|
15
|
|
|
15
|
|
|
15
|
|
|
15
|
|
|
15
|
|
|
15
|
|
|
15
|
|
|
15
|
|
|
15
|
|
362
|
|
# e.g. cdrom |
363
|
15
|
return ($self->vmm_family eq 'vmware' ? '' : $basedir) . $file_basename unless $backingfile; |
364
|
13
|
|
365
|
13
|
if ($self->vmm_family eq 'vmware') { |
366
|
13
|
$file = basename($vmware_disk_path_thinfile); |
367
|
13
|
} |
368
|
13
|
else { |
369
|
10
|
$file = $basedir . $file; |
370
|
2
|
# requested expected value in GBs, if there is any passed as an argument |
371
|
|
my $size = $args->{size} // 0; |
372
|
|
# expected value in Bytes |
373
|
8
|
my (undef, $json) = $self->run_cmd("qemu-img info --output=json $args->{file}", wantarray => 1); |
374
|
|
my $image_vsize = decode_json($json)->{'virtual-size'}; |
375
|
|
$size = (($size * 1024 * 1024 * 1024) <= $image_vsize) ? $image_vsize : $size . 'G'; |
376
|
|
$self->run_cmd(sprintf("qemu-img create '${file}' -f qcow2 -b '$basedir/%s' ${size}", $file_basename)) |
377
|
|
&& die 'qemu-img create with backing file failed'; |
378
|
13
|
} |
379
|
|
return $file; |
380
|
5
|
} |
381
|
1
|
|
382
|
|
my $elem = $doc->createElement('driver'); |
383
|
|
$elem->setAttribute(name => 'qemu'); |
384
|
4
|
if ($cdrom) { |
385
|
|
$elem->setAttribute(type => 'raw'); |
386
|
4
|
} |
387
|
|
else { |
388
|
4
|
$elem->setAttribute(type => 'qcow2'); |
389
|
4
|
$elem->setAttribute(cache => 'unsafe'); |
390
|
4
|
} |
391
|
4
|
return $elem; |
392
|
|
} |
393
|
|
|
394
|
5
|
return ("sd$dev_id", 'scsi') if $cdrom && $vmm_family eq 'xen'; |
395
|
|
return ("xvd$dev_id", 'xen') if $vmm_family eq 'xen'; |
396
|
|
return ("hd$dev_id", 'ide') if $vmm_family eq 'vmware'; |
397
|
25
|
return ("hd$dev_id", 'ide') if $cdrom && $vmm_family eq 'kvm'; |
|
25
|
|
|
25
|
|
|
25
|
|
398
|
25
|
return ("vd$dev_id", 'virtio') if $vmm_family eq 'kvm'; |
399
|
25
|
return (undef, undef); |
400
|
25
|
} |
401
|
4
|
|
402
|
|
my $elem = $doc->createElement('boot'); |
403
|
|
$elem->setAttribute(order => $bootorder); |
404
|
21
|
return $elem; |
405
|
21
|
} |
406
|
|
|
407
|
25
|
my $cdrom = $args->{cdrom}; |
408
|
|
my $name = $self->name; |
409
|
|
my $file = $name . $args->{dev_id} . ($self->vmm_family eq 'vmware' ? '.vmdk' : '.img'); |
410
|
34
|
my $basedir = '/var/lib/libvirt/images/'; |
|
34
|
|
|
34
|
|
|
34
|
|
|
34
|
|
411
|
34
|
my $vmware_datastore = $bmwqemu::vars{VMWARE_DATASTORE} // ''; |
412
|
33
|
my $vmware_openqa_datastore = "/vmfs/volumes/$vmware_datastore/openQA/"; |
413
|
22
|
if ($args->{create}) { |
414
|
13
|
$file = $self->_create_disk($args, $vmware_openqa_datastore, $file, $name, $basedir); |
415
|
10
|
} |
416
|
0
|
else { |
417
|
|
$file = $self->_copy_image_to_vm_host($args, $vmware_openqa_datastore, $file, $name, $basedir, $cdrom); |
418
|
|
} |
419
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
420
|
2
|
my $doc = $self->{domainxml}; |
421
|
2
|
my $devices = $self->{devices_element}; |
422
|
2
|
|
423
|
|
my $disk = $doc->createElement('disk'); |
424
|
|
$disk->setAttribute(type => 'file'); |
425
|
37
|
$disk->setAttribute(device => $cdrom ? 'cdrom' : 'disk'); |
|
37
|
|
|
37
|
|
|
37
|
|
426
|
37
|
$devices->appendChild($disk); |
427
|
37
|
|
428
|
37
|
# there's no <driver> property on VMware |
429
|
37
|
$disk->appendChild(_driver_elem($doc, $cdrom)) if $self->vmm_family ne 'vmware'; |
430
|
37
|
my ($dev_type, $bus_type) = _handle_disk_type($self->vmm_family, $cdrom, $args->{dev_id}); |
431
|
37
|
my $elem = $doc->createElement('target'); |
432
|
37
|
$elem->setAttribute(dev => $dev_type); |
433
|
22
|
$elem->setAttribute(bus => $bus_type); |
434
|
|
$disk->appendChild($elem); |
435
|
|
|
436
|
15
|
$elem = $doc->createElement('source'); |
437
|
|
$file =~ s/\.xz$//; |
438
|
|
$elem->setAttribute(file => $self->vmm_family eq 'vmware' ? "[$vmware_datastore] openQA/$file" : $file); |
439
|
34
|
$disk->appendChild($elem); |
440
|
34
|
|
441
|
|
if (my $bootorder = $args->{bootorder}) { $disk->appendChild(_bootorder_elem($doc, $bootorder)) } |
442
|
34
|
return; |
443
|
34
|
} |
444
|
34
|
|
445
|
34
|
my $virsh = 'virsh'; |
446
|
|
$virsh .= ' ' . $bmwqemu::vars{VMWARE_REMOTE_VMM} if $bmwqemu::vars{VMWARE_REMOTE_VMM}; |
447
|
|
return $virsh; |
448
|
34
|
} |
449
|
34
|
|
450
|
34
|
$self->run_cmd(virsh() . " suspend " . $self->name) && die "Can't suspend VM "; |
451
|
34
|
bmwqemu::diag "VM " . $self->name . " suspended"; |
452
|
34
|
} |
453
|
34
|
|
454
|
|
$self->run_cmd(virsh() . " resume " . $self->name) && die "Can't resume VM "; |
455
|
34
|
bmwqemu::diag "VM " . $self->name . " resumed"; |
456
|
34
|
} |
457
|
34
|
|
458
|
34
|
|
459
|
|
my $remote_vmm = ""; |
460
|
34
|
if ($self->vmm_family eq 'vmware') { |
|
2
|
|
461
|
34
|
my ($fh, $libvirtauthfilename) = File::Temp::tempfile(DIR => "/tmp/"); |
462
|
|
|
463
|
|
# The libvirt esx driver supports connection over HTTP(S) only. When |
464
|
0
|
# asked to authenticate we provide the password via 'authfile'. |
|
0
|
|
465
|
0
|
$self->run_cmd( |
466
|
0
|
"cat > $libvirtauthfilename <<__END |
467
|
0
|
[credentials-vmware] |
468
|
|
username=" . ($bmwqemu::vars{VMWARE_USERNAME} or die 'Need variable VMWARE_USERNAME') . " |
469
|
|
password=" . ($bmwqemu::vars{VMWARE_PASSWORD} or die 'Need variable VMWARE_PASSWORD') . " |
470
|
0
|
[auth-esx-" . ($bmwqemu::vars{VMWARE_HOST} or die 'Need variable VMWARE_HOST') . "] |
|
0
|
|
|
0
|
|
471
|
0
|
credentials=vmware |
472
|
0
|
__END" |
473
|
|
); |
474
|
|
my $user = $bmwqemu::vars{VMWARE_USERNAME} or die 'Need variable VMWARE_USERNAME'; |
475
|
0
|
my $host = $bmwqemu::vars{VMWARE_HOST} or die 'Need variable VMWARE_HOST'; |
|
0
|
|
|
0
|
|
476
|
0
|
$remote_vmm = "-c esx://$user\@$host/?no_verify=1\\&authfile=$libvirtauthfilename "; |
477
|
0
|
$bmwqemu::vars{VMWARE_REMOTE_VMM} = $remote_vmm; |
478
|
|
} |
479
|
|
|
480
|
0
|
my $instance = $self->instance; |
|
0
|
|
|
0
|
|
|
0
|
|
481
|
|
my $xmldata = $self->{domainxml}->toString(2); |
482
|
1
|
my $xmlfilename = "/var/lib/libvirt/images/" . $self->name . ".xml"; |
|
1
|
|
|
1
|
|
483
|
1
|
my $ret; |
484
|
1
|
bmwqemu::diag("Creating libvirt configuration file $xmlfilename:\n$xmldata"); |
485
|
1
|
my ($ssh, $chan) = $self->backend->run_ssh("cat > $xmlfilename", $self->get_ssh_credentials(), keep_open => 1); |
486
|
|
# scp_put is unfortunately unreliable (RT#61771) |
487
|
|
$chan->write($xmldata) || $ssh->die_with_error(); |
488
|
|
$chan->send_eof(); |
489
|
|
$chan->close(); |
490
|
|
|
491
|
|
# shut down possibly running previous test (just to be sure) - ignore errors |
492
|
|
# just making sure we continue after the command finished |
493
|
|
my $ignore = ' |& grep -v "\(failed to get domain\|Domain not found\)"'; |
494
|
1
|
$self->run_cmd("virsh $remote_vmm destroy " . $self->name . $ignore); |
495
|
|
$self->run_cmd("virsh $remote_vmm undefine --snapshots-metadata " . $self->name . $ignore); |
496
|
|
|
497
|
|
# define the new domain |
498
|
1
|
$self->run_cmd("virsh $remote_vmm define $xmlfilename") && die "virsh define failed"; |
499
|
1
|
if ($self->vmm_family eq 'vmware') { |
500
|
1
|
$self->run_cmd('echo bios.bootDelay = \"10000\" >> /vmfs/volumes/datastore1/openQA/' . $self->name . '.vmx', domain => 'sshVMwareServer'); |
501
|
1
|
} |
502
|
|
|
503
|
|
$ret = $self->run_cmd("virsh $remote_vmm start " . $self->name . ' 2> >(tee /tmp/os-autoinst-' . $self->name . '-stderr.log >&2)'); |
504
|
1
|
bmwqemu::diag("Dump actually used libvirt configuration file " . ($ret ? "(broken)" : "(working)")); |
505
|
1
|
$self->run_cmd("virsh $remote_vmm dumpxml " . $self->name); |
506
|
1
|
die "virsh start failed" if $ret; |
507
|
1
|
|
508
|
1
|
$self->backend->start_serial_grab($self->name); |
509
|
1
|
|
510
|
|
return; |
511
|
1
|
} |
512
|
1
|
|
513
|
1
|
$args = {name => $args} unless ref $args; |
514
|
|
|
515
|
|
my $name = $args->{name}; |
516
|
|
$self->name($name) if $name; |
517
|
1
|
$self->backend->start_serial_grab($self->name); |
518
|
1
|
|
519
|
1
|
# Setting SVIRT_KEEP_VM_RUNNING variable prevents destruction of a perhaps valuable VM |
520
|
|
# outside of openQA. Set 'stop_vm' argument should the VM be destroyed at the end. |
521
|
|
$bmwqemu::vars{SVIRT_KEEP_VM_RUNNING} = 1 unless $args->{stop_vm}; |
522
|
1
|
} |
523
|
1
|
|
524
|
1
|
|
525
|
|
|
526
|
|
|
527
|
1
|
=head2 run_cmd |
528
|
1
|
|
529
|
1
|
$ret = $svirt->run_cmd($cmd [, domain => 'default'] [, wantarray => 0 ]); |
530
|
1
|
|
531
|
|
With C<<wantarray => 1 >> you will receive a list containing I<exitcode>, |
532
|
1
|
I<stdout> and I<stderr>. |
533
|
|
|
534
|
1
|
($ret, $stdout, $stderr) = $svirt->run_cmd($cmd, wantarray => 1); |
535
|
|
|
536
|
|
|
537
|
3
|
Execute the command via SSH on given C<domain> by default it is the host given |
|
3
|
|
|
3
|
|
|
3
|
|
538
|
3
|
on C<activate()>, normally the libvirt host defined via C<VIRSH_HOSTNAME>, |
539
|
|
C<VIRSH_USERNAME> and C<VIRSH_PASSWORD>. |
540
|
3
|
The second domain B<sshVMwareServer> is available if C<VIRSH_VMM_FAMILY> is |
541
|
3
|
B<vmware> and defined via C<VMWARE_HOST>, C<VMWARE_PASSWORD> and 'root' as |
542
|
3
|
username. |
543
|
|
For further arguments see C<baseclass:run_ssh_cmd()>. |
544
|
|
=cut |
545
|
|
my %credentials = $self->get_ssh_credentials($args{domain}); |
546
|
3
|
delete $args{domain}; |
547
|
|
return $self->backend->run_ssh_cmd($cmd, %credentials, %args); |
548
|
|
} |
549
|
1
|
|
|
1
|
|
|
1
|
|
|
1
|
|
550
|
|
=head2 get_cmd_output |
551
|
1
|
|
|
1
|
|
|
1
|
|
|
1
|
|
552
|
|
$stdout = $svirt->get_cmd_output($cmd , $args = {domain => 'default', wantarray => 0}); |
553
|
|
|
554
|
|
With C<<wantarray => 1>> the function will return a reference to a list which |
555
|
|
contains I<stdout> and I<stderr>. |
556
|
|
This function is B<deprecated>, you should use C<<$svirt->run_cmd()>> instead. |
557
|
|
=cut |
558
|
|
my (undef, $stdout, $stderr) = $self->backend->run_ssh_cmd($cmd, $self->get_ssh_credentials($args->{domain}), wantarray => 1); |
559
|
|
return $args->{wantarray} ? [$stdout, $stderr] : $stdout; |
560
|
|
} |
561
|
|
|
562
|
|
1; |