]> git.proxmox.com Git - qemu-server.git/blob - PVE/QemuServer/Machine.pm
config: define machine schema as property-string
[qemu-server.git] / PVE / QemuServer / Machine.pm
1 package PVE::QemuServer::Machine;
2
3 use strict;
4 use warnings;
5
6 use PVE::QemuServer::Helpers;
7 use PVE::QemuServer::Monitor;
8 use PVE::JSONSchema qw(get_standard_option parse_property_string);
9
10 # Bump this for VM HW layout changes during a release (where the QEMU machine
11 # version stays the same)
12 our $PVE_MACHINE_VERSION = {
13 '4.1' => 2,
14 };
15
16 my $machine_fmt = {
17 type => {
18 default_key => 1,
19 description => "Specifies the QEMU machine type.",
20 type => 'string',
21 pattern => '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
22 maxLength => 40,
23 format_description => 'machine type',
24 optional => 1,
25 },
26 };
27
28 PVE::JSONSchema::register_format('pve-qemu-machine-fmt', $machine_fmt);
29
30 PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
31 description => "Specify the QEMU machine type.",
32 type => 'string',
33 optional => 1,
34 format => PVE::JSONSchema::get_format('pve-qemu-machine-fmt'),
35 });
36
37 sub parse_machine {
38 my ($value) = @_;
39
40 return if !$value;
41
42 my $res = parse_property_string($machine_fmt, $value);
43 return $res;
44 }
45
46 sub print_machine {
47 my ($machine_conf) = @_;
48 return print_property_string($machine_conf, $machine_fmt);
49 }
50
51 sub machine_type_is_q35 {
52 my ($conf) = @_;
53
54 my $machine_conf = parse_machine($conf->{machine});
55 return $machine_conf->{type} && ($machine_conf->{type} =~ m/q35/) ? 1 : 0;
56 }
57
58 # In list context, also returns whether the current machine is deprecated or not.
59 sub current_from_query_machines {
60 my ($machines) = @_;
61
62 my ($current, $default);
63 for my $machine ($machines->@*) {
64 $default = $machine->{name} if $machine->{'is-default'};
65
66 if ($machine->{'is-current'}) {
67 $current = $machine->{name};
68 # pve-version only exists for the current machine
69 $current .= "+$machine->{'pve-version'}" if $machine->{'pve-version'};
70 return wantarray ? ($current, $machine->{deprecated} ? 1 : 0) : $current;
71 }
72 }
73
74 # fallback to the default machine if current is not supported by qemu - assume never deprecated
75 my $fallback = $default || 'pc';
76 return wantarray ? ($fallback, 0) : $fallback;
77 }
78
79 # This only works if VM is running.
80 # In list context, also returns whether the current machine is deprecated or not.
81 sub get_current_qemu_machine {
82 my ($vmid) = @_;
83
84 my $res = PVE::QemuServer::Monitor::mon_cmd($vmid, 'query-machines');
85
86 return current_from_query_machines($res);
87 }
88
89 # returns a string with major.minor+pve<VERSION>, patch version-part is ignored
90 # as it's seldom ressembling a real QEMU machine type, so it would be '0' 99% of
91 # the time anyway.. This explicitly separates pveversion from the machine.
92 sub extract_version {
93 my ($machine_type, $kvmversion) = @_;
94
95 if (defined($machine_type) && $machine_type =~
96 m/^(?:pc(?:-i440fx|-q35)?|virt)-(\d+)\.(\d+)(?:\.(\d+))?(\+pve\d+)?(?:\.pxe)?/)
97 {
98 my $versionstr = "$1.$2";
99 $versionstr .= $4 if $4;
100 return $versionstr;
101 } elsif (defined($kvmversion)) {
102 if ($kvmversion =~ m/^(\d+)\.(\d+)/) {
103 my $pvever = get_pve_version($kvmversion);
104 return "$1.$2+pve$pvever";
105 }
106 }
107
108 return;
109 }
110
111 sub machine_version {
112 my ($machine_type, $major, $minor, $pve) = @_;
113
114 return PVE::QemuServer::Helpers::min_version(
115 extract_version($machine_type), $major, $minor, $pve);
116 }
117
118 sub get_pve_version {
119 my ($verstr) = @_;
120
121 if ($verstr =~ m/^(\d+\.\d+)/) {
122 return $PVE_MACHINE_VERSION->{$1} // 0;
123 }
124
125 die "internal error: cannot get pve version for invalid string '$verstr'";
126 }
127
128 sub can_run_pve_machine_version {
129 my ($machine_version, $kvmversion) = @_;
130
131 $machine_version =~ m/^(\d+)\.(\d+)(?:\+pve(\d+))?(?:\.pxe)?$/;
132 my $major = $1;
133 my $minor = $2;
134 my $pvever = $3;
135
136 $kvmversion =~ m/(\d+)\.(\d+)/;
137 return 0 if PVE::QemuServer::Helpers::version_cmp($1, $major, $2, $minor) < 0;
138
139 # if $pvever is missing or 0, we definitely support it as long as we didn't
140 # fail the QEMU version check above
141 return 1 if !$pvever;
142
143 my $max_supported = get_pve_version("$major.$minor");
144 return 1 if $max_supported >= $pvever;
145
146 return 0;
147 }
148
149 # dies if a) VM not running or not exisiting b) Version query failed
150 # So, any defined return value is valid, any invalid state can be caught by eval
151 sub runs_at_least_qemu_version {
152 my ($vmid, $major, $minor, $extra) = @_;
153
154 my $v = PVE::QemuServer::Monitor::mon_cmd($vmid, 'query-version');
155 die "could not query currently running version for VM $vmid\n" if !defined($v);
156 $v = $v->{qemu};
157
158 return PVE::QemuServer::Helpers::version_cmp($v->{major}, $major, $v->{minor}, $minor, $v->{micro}, $extra) >= 0;
159 }
160
161 sub qemu_machine_pxe {
162 my ($vmid, $conf) = @_;
163
164 my $machine = get_current_qemu_machine($vmid);
165
166 my $machine_conf = parse_machine($conf->{machine});
167 if ($machine_conf->{type} && $machine_conf->{type} =~ m/\.pxe$/) {
168 $machine .= '.pxe';
169 }
170
171 return $machine;
172 }
173
174 1;