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