]>
Commit | Line | Data |
---|---|---|
19a6b9f1 DM |
1 | package PVE::API2Tools; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
e02db471 TL |
5 | |
6 | use Digest::MD5 qw(md5_hex); | |
fddf562b | 7 | use File::stat; |
a3eff2d9 | 8 | use Net::IP; |
e02db471 TL |
9 | use URI::Escape; |
10 | use URI; | |
a3eff2d9 | 11 | |
e02db471 TL |
12 | use PVE::Cluster; |
13 | use PVE::DataCenterConfig; # so we can cfs-read datacenter.cfg | |
3c54bc91 | 14 | use PVE::Exception qw(raise_param_exc); |
eb6d7497 | 15 | use PVE::INotify; |
5ab9a243 | 16 | use PVE::RPCEnvironment; |
b289829f | 17 | use PVE::SafeSyslog; |
74c8984d | 18 | use PVE::Storage::Plugin; |
e02db471 | 19 | use PVE::Tools; |
2ba6d822 DM |
20 | |
21 | my $hwaddress; | |
f6395eb6 | 22 | my $hwaddress_st = {}; |
2ba6d822 DM |
23 | |
24 | sub get_hwaddress { | |
f6395eb6 MS |
25 | my $fn = '/etc/ssh/ssh_host_rsa_key.pub'; |
26 | my $st = stat($fn); | |
60e049c2 | 27 | |
f6395eb6 MS |
28 | if (defined($hwaddress) |
29 | && $hwaddress_st->{mtime} == $st->mtime | |
30 | && $hwaddress_st->{ino} == $st->ino | |
31 | && $hwaddress_st->{dev} == $st->dev) { | |
32 | return $hwaddress; | |
33 | } | |
2ba6d822 | 34 | |
2ba6d822 DM |
35 | my $sshkey = PVE::Tools::file_get_contents($fn); |
36 | $hwaddress = uc(md5_hex($sshkey)); | |
f6395eb6 | 37 | $hwaddress_st->@{'mtime', 'ino', 'dev'} = ($st->mtime, $st->ino, $st->dev); |
2ba6d822 DM |
38 | |
39 | return $hwaddress; | |
40 | } | |
19a6b9f1 | 41 | |
16b69b6c | 42 | sub extract_node_stats { |
57d56896 | 43 | my ($node, $members, $rrd, $exclude_stats) = @_; |
16b69b6c DM |
44 | |
45 | my $entry = { | |
46 | id => "node/$node", | |
47 | node => $node, | |
48 | type => "node", | |
b67e6398 | 49 | status => 'unknown', |
16b69b6c DM |
50 | }; |
51 | ||
52 | if (my $d = $rrd->{"pve2-node/$node"}) { | |
57d56896 | 53 | |
16b69b6c DM |
54 | if (!$members || # no cluster |
55 | ($members->{$node} && $members->{$node}->{online})) { | |
57d56896 TL |
56 | if (!$exclude_stats) { |
57 | $entry->{uptime} = ($d->[0] || 0) + 0; | |
58 | $entry->{cpu} = ($d->[5] || 0) + 0; | |
59 | $entry->{mem} = ($d->[8] || 0) + 0; | |
60 | $entry->{disk} = ($d->[12] || 0) + 0; | |
61 | } | |
b67e6398 | 62 | $entry->{status} = 'online'; |
16b69b6c DM |
63 | } |
64 | $entry->{level} = $d->[1]; | |
57d56896 TL |
65 | if (!$exclude_stats) { |
66 | $entry->{maxcpu} = ($d->[4] || 0) + 0; | |
67 | $entry->{maxmem} = ($d->[7] || 0) + 0; | |
68 | $entry->{maxdisk} = ($d->[11] || 0) + 0; | |
69 | } | |
16b69b6c DM |
70 | } |
71 | ||
b67e6398 DC |
72 | if ($members && $members->{$node} && |
73 | !$members->{$node}->{online}) { | |
74 | $entry->{status} = 'offline'; | |
75 | } | |
76 | ||
16b69b6c DM |
77 | return $entry; |
78 | } | |
19a6b9f1 DM |
79 | |
80 | sub extract_vm_stats { | |
81 | my ($vmid, $data, $rrd) = @_; | |
82 | ||
83 | my $entry = { | |
84 | id => "$data->{type}/$vmid", | |
60e049c2 | 85 | vmid => $vmid + 0, |
19a6b9f1 DM |
86 | node => $data->{node}, |
87 | type => $data->{type}, | |
30cdb0ca | 88 | status => 'unknown', |
19a6b9f1 DM |
89 | }; |
90 | ||
90c3fae4 DM |
91 | my $d; |
92 | ||
93 | if ($d = $rrd->{"pve2-vm/$vmid"}) { | |
19a6b9f1 DM |
94 | |
95 | $entry->{uptime} = ($d->[0] || 0) + 0; | |
96 | $entry->{name} = $d->[1]; | |
3c353ed2 | 97 | $entry->{status} = $entry->{uptime} ? 'running' : 'stopped'; |
19a6b9f1 DM |
98 | $entry->{maxcpu} = ($d->[3] || 0) + 0; |
99 | $entry->{cpu} = ($d->[4] || 0) + 0; | |
100 | $entry->{maxmem} = ($d->[5] || 0) + 0; | |
101 | $entry->{mem} = ($d->[6] || 0) + 0; | |
102 | $entry->{maxdisk} = ($d->[7] || 0) + 0; | |
103 | $entry->{disk} = ($d->[8] || 0) + 0; | |
cf497f7d AD |
104 | $entry->{netin} = ($d->[9] || 0) + 0; |
105 | $entry->{netout} = ($d->[10] || 0) + 0; | |
106 | $entry->{diskread} = ($d->[11] || 0) + 0; | |
107 | $entry->{diskwrite} = ($d->[12] || 0) + 0; | |
60e049c2 | 108 | |
90c3fae4 | 109 | } elsif ($d = $rrd->{"pve2.3-vm/$vmid"}) { |
3c353ed2 DM |
110 | |
111 | $entry->{uptime} = ($d->[0] || 0) + 0; | |
112 | $entry->{name} = $d->[1]; | |
113 | $entry->{status} = $d->[2]; | |
114 | $entry->{template} = $d->[3] + 0; | |
115 | ||
116 | $entry->{maxcpu} = ($d->[5] || 0) + 0; | |
117 | $entry->{cpu} = ($d->[6] || 0) + 0; | |
118 | $entry->{maxmem} = ($d->[7] || 0) + 0; | |
119 | $entry->{mem} = ($d->[8] || 0) + 0; | |
120 | $entry->{maxdisk} = ($d->[9] || 0) + 0; | |
121 | $entry->{disk} = ($d->[10] || 0) + 0; | |
122 | $entry->{netin} = ($d->[11] || 0) + 0; | |
123 | $entry->{netout} = ($d->[12] || 0) + 0; | |
124 | $entry->{diskread} = ($d->[13] || 0) + 0; | |
125 | $entry->{diskwrite} = ($d->[14] || 0) + 0; | |
19a6b9f1 DM |
126 | }; |
127 | ||
128 | return $entry; | |
ff5fcc8a | 129 | } |
19a6b9f1 DM |
130 | |
131 | sub extract_storage_stats { | |
132 | my ($storeid, $scfg, $node, $rrd) = @_; | |
133 | ||
74c8984d FE |
134 | my $content = PVE::Storage::Plugin::content_hash_to_string($scfg->{content}); |
135 | ||
19a6b9f1 DM |
136 | my $entry = { |
137 | id => "storage/$node/$storeid", | |
60e049c2 TM |
138 | storage => $storeid, |
139 | node => $node, | |
140 | type => 'storage', | |
77a9ce32 | 141 | plugintype => $scfg->{type}, |
88466808 | 142 | status => 'unknown', |
1ce3fa3b | 143 | shared => $scfg->{shared} || 0, |
74c8984d | 144 | content => $content, |
60e049c2 | 145 | }; |
19a6b9f1 DM |
146 | |
147 | if (my $d = $rrd->{"pve2-storage/$node/$storeid"}) { | |
148 | $entry->{maxdisk} = ($d->[1] || 0) + 0; | |
149 | $entry->{disk} = ($d->[2] || 0) + 0; | |
88466808 | 150 | $entry->{status} = 'available'; |
19a6b9f1 DM |
151 | } |
152 | ||
153 | return $entry; | |
ff5fcc8a | 154 | } |
19a6b9f1 | 155 | |
446b9669 DM |
156 | sub parse_http_proxy { |
157 | my ($proxyenv) = @_; | |
158 | ||
159 | my $uri = URI->new($proxyenv); | |
160 | ||
161 | my $scheme = $uri->scheme; | |
162 | my $host = $uri->host; | |
163 | my $port = $uri->port || 3128; | |
164 | ||
165 | my ($username, $password); | |
166 | ||
167 | if (defined(my $p_auth = $uri->userinfo())) { | |
168 | ($username, $password) = map URI::Escape::uri_unescape($_), split(":", $p_auth, 2); | |
169 | } | |
170 | ||
171 | return ("$host:$port", $username, $password); | |
172 | } | |
173 | ||
b289829f DM |
174 | sub run_spiceterm { |
175 | my ($authpath, $permissions, $vmid, $node, $proxy, $title, $shcmd) = @_; | |
176 | ||
177 | my $rpcenv = PVE::RPCEnvironment::get(); | |
178 | ||
179 | my $authuser = $rpcenv->get_user(); | |
b289829f | 180 | |
eb6d7497 WB |
181 | my $nodename = PVE::INotify::nodename(); |
182 | my $family = PVE::Tools::get_host_address_family($nodename); | |
183 | my $port = PVE::Tools::next_spice_port($family); | |
60e049c2 TM |
184 | |
185 | my ($ticket, undef, $remote_viewer_config) = | |
eb7cd2ce | 186 | PVE::AccessControl::remote_viewer_config($authuser, $vmid, $node, $proxy, $title, $port); |
b289829f | 187 | |
60e049c2 | 188 | my $timeout = 40; |
b289829f | 189 | |
c8e4fedd | 190 | my $cmd = ['/usr/bin/spiceterm', '--port', $port, '--addr', 'localhost', |
60e049c2 | 191 | '--timeout', $timeout, '--authpath', $authpath, |
b289829f DM |
192 | '--permissions', $permissions]; |
193 | ||
194 | my $dcconf = PVE::Cluster::cfs_read_file('datacenter.cfg'); | |
195 | push @$cmd, '--keymap', $dcconf->{keyboard} if $dcconf->{keyboard}; | |
196 | ||
197 | push @$cmd, '--', @$shcmd; | |
198 | ||
199 | my $realcmd = sub { | |
200 | my $upid = shift; | |
201 | ||
202 | syslog ('info', "starting spiceterm $upid - $title\n"); | |
203 | ||
204 | my $cmdstr = join (' ', @$cmd); | |
205 | syslog ('info', "launch command: $cmdstr"); | |
206 | ||
3fa0b222 | 207 | eval { |
b289829f | 208 | foreach my $k (keys %ENV) { |
3fa0b222 | 209 | next if $k eq 'PATH' || $k eq 'TERM' || $k eq 'USER' || $k eq 'HOME' || $k eq 'LANG' || $k eq 'LANGUAGE' ; |
b289829f DM |
210 | delete $ENV{$k}; |
211 | } | |
212 | $ENV{PWD} = '/'; | |
213 | $ENV{SPICE_TICKET} = $ticket; | |
3fa0b222 | 214 | |
b0d4b407 | 215 | PVE::Tools::run_command($cmd, errmsg => 'spiceterm failed\n', keeplocale => 1); |
b289829f DM |
216 | }; |
217 | if (my $err = $@) { | |
218 | syslog ('err', $err); | |
219 | } | |
220 | ||
221 | return; | |
222 | }; | |
223 | ||
224 | if ($vmid) { | |
225 | $rpcenv->fork_worker('spiceproxy', $vmid, $authuser, $realcmd); | |
226 | } else { | |
227 | $rpcenv->fork_worker('spiceshell', undef, $authuser, $realcmd); | |
228 | } | |
eb7cd2ce | 229 | |
b289829f DM |
230 | PVE::Tools::wait_for_vnc_port($port); |
231 | ||
eb7cd2ce | 232 | return $remote_viewer_config; |
b289829f DM |
233 | } |
234 | ||
3c54bc91 DM |
235 | sub resolve_proxyto { |
236 | my ($rpcenv, $proxyto_callback, $proxyto, $uri_param) = @_; | |
237 | ||
238 | my $node; | |
239 | if ($proxyto_callback) { | |
240 | $node = $proxyto_callback->($rpcenv, $proxyto, $uri_param); | |
241 | die "internal error - proxyto_callback returned nothing\n" | |
242 | if !$node; | |
243 | } else { | |
244 | $node = $uri_param->{$proxyto}; | |
a7f04758 | 245 | raise_param_exc({ $proxyto => "proxyto parameter does not exist"}) |
3c54bc91 DM |
246 | if !$node; |
247 | } | |
248 | return $node; | |
249 | } | |
250 | ||
f3376261 TM |
251 | sub get_resource_pool_guest_members { |
252 | my ($pool) = @_; | |
253 | ||
254 | my $usercfg = PVE::Cluster::cfs_read_file("user.cfg"); | |
255 | ||
256 | my $vmlist = PVE::Cluster::get_vmlist() || {}; | |
257 | my $idlist = $vmlist->{ids} || {}; | |
258 | ||
259 | my $data = $usercfg->{pools}->{$pool}; | |
260 | ||
5ab9a243 | 261 | die "pool '$pool' does not exist\n" if !$data; |
f3376261 | 262 | |
5ab9a243 | 263 | my $pool_members = [ grep { $idlist->{$_} } keys %{$data->{vms}} ]; |
f3376261 | 264 | |
5ab9a243 | 265 | return $pool_members; |
f3376261 TM |
266 | } |
267 | ||
19a6b9f1 | 268 | 1; |