]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2.pm
move formater registration to HTTPServer
[pve-manager.git] / PVE / API2.pm
1 package PVE::API2;
2
3 use strict;
4 use warnings;
5
6 use PVE::pvecfg;
7 use PVE::HTTPServer;
8 use PVE::RESTHandler;
9 use HTTP::Status;
10 use JSON;
11 use HTML::Entities;
12 use PVE::JSONSchema;
13
14 use base qw(PVE::RESTHandler);
15
16 # preload classes
17 use PVE::API2::Cluster;
18 use PVE::API2::Nodes;
19 use PVE::API2::Pool;
20 use PVE::API2::AccessControl;
21 use PVE::API2::Storage::Config;
22
23 __PACKAGE__->register_method ({
24 subclass => "PVE::API2::Cluster",
25 path => 'cluster',
26 });
27
28 __PACKAGE__->register_method ({
29 subclass => "PVE::API2::Nodes",
30 path => 'nodes',
31 });
32
33 __PACKAGE__->register_method ({
34 subclass => "PVE::API2::Storage::Config",
35 path => 'storage',
36 });
37
38 __PACKAGE__->register_method ({
39 subclass => "PVE::API2::AccessControl",
40 path => 'access',
41 });
42
43 __PACKAGE__->register_method ({
44 subclass => "PVE::API2::Pool",
45 path => 'pools',
46 });
47
48 __PACKAGE__->register_method ({
49 name => 'index',
50 path => '',
51 method => 'GET',
52 permissions => { user => 'all' },
53 description => "Directory index.",
54 parameters => {
55 additionalProperties => 0,
56 properties => {},
57 },
58 returns => {
59 type => 'array',
60 items => {
61 type => "object",
62 properties => {
63 subdir => { type => 'string' },
64 },
65 },
66 links => [ { rel => 'child', href => "{subdir}" } ],
67 },
68 code => sub {
69 my ($resp, $param) = @_;
70
71 my $res = [ { subdir => 'version' } ];
72
73 my $ma = PVE::API2->method_attributes();
74
75 foreach my $info (@$ma) {
76 next if !$info->{subclass};
77
78 my $subpath = $info->{match_re}->[0];
79
80 push @$res, { subdir => $subpath };
81 }
82
83 return $res;
84 }});
85
86 __PACKAGE__->register_method ({
87 name => 'version',
88 path => 'version',
89 method => 'GET',
90 permissions => { user => 'all' },
91 description => "API version details. The result also includes the global datacenter confguration.",
92 parameters => {
93 additionalProperties => 0,
94 properties => {},
95 },
96 returns => {
97 type => "object",
98 properties => {
99 version => { type => 'string' },
100 release => { type => 'string' },
101 repoid => { type => 'string' },
102 },
103 },
104 code => sub {
105 my ($resp, $param) = @_;
106
107 my $res = PVE::Cluster::cfs_read_file('datacenter.cfg');
108
109 my $vi = PVE::pvecfg::version_info();
110 foreach my $k (qw(version release repoid)) {
111 $res->{$k} = $vi->{$k};
112 }
113
114 return $res;
115 }});
116
117 # register result formaters
118
119 my $prepare_response_data = sub {
120 my ($format, $res) = @_;
121
122 my $success = 1;
123 my $new = {
124 data => $res->{data},
125 };
126 if (scalar(keys %{$res->{errors}})) {
127 $success = 0;
128 $new->{errors} = $res->{errors};
129 }
130
131 if ($format eq 'extjs' || $format eq 'htmljs') {
132 # HACK: extjs wants 'success' property instead of useful HTTP status codes
133 if (HTTP::Status::is_error($res->{status})) {
134 $success = 0;
135 $new->{message} = $res->{message} || status_message($res->{status});
136 $new->{status} = $res->{status} || 200;
137 $res->{message} = undef;
138 $res->{status} = 200;
139 }
140 $new->{success} = $success;
141 }
142
143 if ($success && $res->{total}) {
144 $new->{total} = $res->{total};
145 }
146
147 if ($success && $res->{changes}) {
148 $new->{changes} = $res->{changes};
149 }
150
151 $res->{data} = $new;
152 };
153
154 PVE::HTTPServer::register_formater('json', sub {
155 my ($res, $data, $param, $path, $auth) = @_;
156
157 my $nocomp = 0;
158
159 my $ct = 'application/json;charset=UTF-8';
160
161 &$prepare_response_data('json', $res);
162
163 my $raw = to_json($res->{data}, {utf8 => 1, allow_nonref => 1});
164
165 return ($raw, $ct, $nocomp);
166 });
167
168
169 PVE::HTTPServer::register_formater('extjs', sub {
170 my ($res, $data, $param, $path, $auth) = @_;
171
172 my $nocomp = 0;
173
174 my $ct = 'application/json;charset=UTF-8';
175
176 &$prepare_response_data('extjs', $res);
177
178 my $raw = to_json($res->{data}, {utf8 => 1, allow_nonref => 1});
179
180 return ($raw, $ct, $nocomp);
181 });
182
183 PVE::HTTPServer::register_formater('htmljs', sub {
184 my ($res, $data, $param, $path, $auth) = @_;
185
186 my $nocomp = 0;
187
188 # we use this for extjs file upload forms
189
190 my $ct = 'text/html;charset=UTF-8';
191
192 &$prepare_response_data('htmljs', $res);
193
194 my $raw = encode_entities(to_json($res->{data}, {allow_nonref => 1}));
195
196 return ($raw, $ct, $nocomp);
197 });
198
199
200 PVE::HTTPServer::register_formater('spiceconfig', sub {
201 my ($res, $data, $param, $path, $auth) = @_;
202
203 my $nocomp = 0;
204
205 my $ct = 'application/x-virt-viewer;charset=UTF-8';
206
207 &$prepare_response_data('spiceconfig', $res);
208
209 $data = $res->{data};
210
211 my $raw;
212
213 if ($data && ref($data) && ref($data->{data})) {
214 $raw = "[virt-viewer]\n";
215 while (my ($key, $value) = each %{$data->{data}}) {
216 $raw .= "$key=$value\n" if defined($value);
217 }
218 }
219
220 return ($raw, $ct, $nocomp);
221 });
222
223 PVE::HTTPServer::register_formater('png', sub {
224 my ($res, $data, $param, $path, $auth) = @_;
225
226 my $nocomp = 1;
227
228 my $ct = 'image/png';
229
230 &$prepare_response_data('png', $res);
231
232 $data = $res->{data};
233
234 # fixme: better to revove that whole png thing ?
235
236 my $filename;
237 my $raw = '';
238
239 if ($data && ref($data) && ref($data->{data}) &&
240 $data->{data}->{filename} && defined($data->{data}->{image})) {
241 $filename = $data->{data}->{filename};
242 $raw = $data->{data}->{image};
243 }
244
245 return ($raw, $ct, $nocomp);
246 });
247
248 PVE::HTTPServer::register_formater('html', sub {
249 my ($res, $data, $param, $path, $auth) = @_;
250
251 my $nocomp = 0;
252
253 my $ct = 'text/html;charset=UTF-8';
254
255 &$prepare_response_data('html', $res);
256
257 $data = $res->{data};
258
259 my $info = $res->{info};
260
261 my $raw = "<html><body>";
262 if (!HTTP::Status::is_success($res->{status})) {
263 my $msg = $res->{message} || '';
264 $raw .= "<h1>ERROR $res->{status} $msg</h1>";
265 }
266 my $lnk = PVE::JSONSchema::method_get_child_link($info);
267
268 if ($lnk && $data && $data->{data} && HTTP::Status::is_success($res->{status})) {
269
270 my $href = $lnk->{href};
271 if ($href =~ m/^\{(\S+)\}$/) {
272 my $prop = $1;
273 $path =~ s/\/+$//; # remove trailing slash
274 foreach my $elem (sort {$a->{$prop} cmp $b->{$prop}} @{$data->{data}}) {
275 next if !ref($elem);
276
277 if (defined(my $value = $elem->{$prop})) {
278 if ($value ne '') {
279 if (scalar(keys %$elem) > 1) {
280 my $tv = to_json($elem, {allow_nonref => 1, canonical => 1});
281 $raw .= "<a href='$path/$value'>$value</a> <pre>$tv</pre><br>";
282 } else {
283 $raw .= "<a href='$path/$value'>$value</a><br>";
284 }
285 }
286 }
287 }
288 }
289 } else {
290 $raw .= "<pre>";
291 $raw .= encode_entities(to_json($data, {allow_nonref => 1, pretty => 1}));
292 $raw .= "</pre>";
293 }
294 $raw .= "</body></html>";
295
296 return ($raw, $ct, $nocomp);
297 });
298
299 1;