]>
Commit | Line | Data |
---|---|---|
29505e2c DM |
1 | package PVE::APIClient::Helpers; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
ab79ce78 | 6 | use Storable; |
29505e2c DM |
7 | use JSON; |
8 | use PVE::APIClient::Exception qw(raise); | |
29505e2c DM |
9 | use Encode::Locale; |
10 | use Encode; | |
11 | use HTTP::Status qw(:constants); | |
12 | ||
13 | my $pve_api_definition; | |
14 | my $pve_api_path_hash; | |
15 | ||
ab79ce78 | 16 | my $pve_api_definition_fn = "/usr/share/pve-client/pve-api-definition.dat"; |
29505e2c | 17 | |
b133a905 DM |
18 | my $method_map = { |
19 | create => 'POST', | |
20 | set => 'PUT', | |
21 | get => 'GET', | |
22 | delete => 'DELETE', | |
23 | }; | |
24 | ||
29505e2c DM |
25 | my $build_pve_api_path_hash; |
26 | $build_pve_api_path_hash = sub { | |
27 | my ($tree) = @_; | |
28 | ||
29 | my $class = ref($tree); | |
30 | return $tree if !$class; | |
31 | ||
32 | if ($class eq 'ARRAY') { | |
33 | foreach my $el (@$tree) { | |
34 | $build_pve_api_path_hash->($el); | |
35 | } | |
36 | } elsif ($class eq 'HASH') { | |
37 | if (defined($tree->{leaf}) && defined(my $path = $tree->{path})) { | |
38 | $pve_api_path_hash->{$path} = $tree; | |
39 | } | |
40 | foreach my $k (keys %$tree) { | |
41 | $build_pve_api_path_hash->($tree->{$k}); | |
42 | } | |
43 | } | |
44 | }; | |
45 | ||
46 | sub get_api_definition { | |
47 | ||
48 | if (!defined($pve_api_definition)) { | |
29505e2c DM |
49 | open(my $fh, '<', $pve_api_definition_fn) || |
50 | die "unable to open '$pve_api_definition_fn' - $!\n"; | |
ab79ce78 | 51 | $pve_api_definition = Storable::fd_retrieve($fh); |
29505e2c DM |
52 | $build_pve_api_path_hash->($pve_api_definition); |
53 | } | |
54 | ||
29505e2c DM |
55 | return $pve_api_definition; |
56 | } | |
57 | ||
58 | sub lookup_api_method { | |
635c0511 | 59 | my ($path, $method, $noerr) = @_; |
29505e2c DM |
60 | |
61 | get_api_definition(); # make sure API data is loaded | |
62 | ||
635c0511 DM |
63 | my $info = $pve_api_path_hash->{$path}; |
64 | ||
65 | if (!$info) { | |
66 | return undef if $noerr; | |
29505e2c | 67 | die "unable to find API info for path '$path'\n"; |
635c0511 | 68 | } |
29505e2c | 69 | |
635c0511 DM |
70 | my $data = $info->{info}->{$method}; |
71 | ||
72 | if (!$data) { | |
73 | return undef if $noerr; | |
29505e2c | 74 | die "unable to find API method '$method' for path '$path'\n"; |
635c0511 | 75 | } |
29505e2c DM |
76 | |
77 | return $data; | |
78 | } | |
79 | ||
635c0511 DM |
80 | sub complete_api_call_options { |
81 | my ($cmd, $prop, $prev, $cur, $args) = @_; | |
82 | ||
83 | my $print_result = sub { | |
84 | foreach my $p (@_) { | |
85 | print "$p\n" if $p =~ m/^$cur/; | |
86 | } | |
87 | }; | |
88 | ||
89 | my $print_parameter_completion = sub { | |
90 | my ($pname) = @_; | |
91 | my $d = $prop->{$pname}; | |
92 | if ($d->{completion}) { | |
93 | my $vt = ref($d->{completion}); | |
94 | if ($vt eq 'CODE') { | |
95 | my $res = $d->{completion}->($cmd, $pname, $cur, $args); | |
96 | &$print_result(@$res); | |
97 | } | |
98 | } elsif ($d->{type} eq 'boolean') { | |
99 | &$print_result('0', '1'); | |
100 | } elsif ($d->{enum}) { | |
101 | &$print_result(@{$d->{enum}}); | |
102 | } | |
103 | }; | |
104 | ||
105 | my @option_list = (); | |
106 | foreach my $key (keys %$prop) { | |
107 | push @option_list, "--$key"; | |
108 | } | |
109 | ||
110 | if ($cur =~ m/^-/) { | |
111 | &$print_result(@option_list); | |
112 | return; | |
113 | } | |
114 | ||
115 | if ($prev =~ m/^--?(.+)$/ && $prop->{$1}) { | |
116 | my $pname = $1; | |
117 | &$print_parameter_completion($pname); | |
118 | return; | |
119 | } | |
120 | ||
121 | &$print_result(@option_list); | |
122 | } | |
123 | ||
124 | sub complete_api_path { | |
125 | my ($text) = @_; | |
126 | ||
127 | get_api_definition(); # make sure API data is loaded | |
128 | ||
129 | $text =~ s!^/!!; | |
130 | ||
131 | my ($dir, $rest) = $text =~ m|^(?:(.*)/)?(?:([^/]*))?$|; | |
132 | ||
133 | my $info; | |
134 | if (!defined($dir)) { | |
135 | $dir = ''; | |
136 | $info = { children => $pve_api_definition }; | |
137 | } else { | |
138 | $info = $pve_api_path_hash->{"/$dir"}; | |
139 | } | |
140 | ||
b133a905 | 141 | my $res = []; |
635c0511 DM |
142 | if ($info) { |
143 | if (my $children = $info->{children}) { | |
144 | foreach my $c (@$children) { | |
145 | if ($c->{path} =~ m!\Q$dir/$rest!) { | |
b133a905 DM |
146 | push @$res, $c->{path}; |
147 | push @$res, "$c->{path}/" if $c->{children}; | |
635c0511 DM |
148 | } |
149 | } | |
150 | } | |
151 | } | |
b133a905 DM |
152 | return $res; |
153 | } | |
154 | ||
155 | # test for command lines with api calls (or similar bash completion calls): | |
156 | # example1: pveclient api get remote1 /cluster | |
157 | sub extract_path_info { | |
158 | ||
159 | my $info; | |
160 | ||
161 | my $test_path_properties = sub { | |
162 | my ($args) = @_; | |
163 | ||
164 | return if scalar(@$args) < 5; | |
165 | return if $args->[1] ne 'api'; | |
166 | ||
167 | my $path = $args->[4]; | |
168 | if (my $method = $method_map->{$args->[2]}) { | |
169 | $info = lookup_api_method($path, $method, 1); | |
170 | } | |
171 | }; | |
172 | ||
173 | if (defined(my $cmd = $ARGV[0])) { | |
174 | if ($cmd eq 'api') { | |
175 | $test_path_properties->([$0, @ARGV]); | |
176 | } elsif ($cmd eq 'bashcomplete') { | |
177 | my $cmdline = substr($ENV{COMP_LINE}, 0, $ENV{COMP_POINT}); | |
178 | my $args = PVE::Tools::split_args($cmdline); | |
179 | $test_path_properties->($args); | |
180 | } | |
181 | } | |
182 | ||
183 | return $info; | |
635c0511 DM |
184 | } |
185 | ||
2f964a75 RJ |
186 | sub get_vmid_resource { |
187 | my ($conn, $vmid) = @_; | |
188 | ||
189 | my $resources = $conn->get('api2/json/cluster/resources', {type => 'vm'}); | |
190 | ||
191 | my $resource; | |
192 | for my $tmp (@$resources) { | |
193 | if ($tmp->{vmid} eq $vmid) { | |
194 | $resource = $tmp; | |
195 | last; | |
196 | } | |
197 | } | |
198 | ||
199 | if (!defined($resource)) { | |
200 | die "\"$vmid\" not found"; | |
201 | } | |
202 | ||
203 | return $resource; | |
204 | } | |
205 | ||
206 | sub poll_task { | |
207 | my ($conn, $node, $upid) = @_; | |
208 | ||
209 | my $path = "api2/json/nodes/$node/tasks/$upid/status"; | |
210 | ||
211 | my $task_status; | |
212 | while(1) { | |
213 | $task_status = $conn->get($path, {}); | |
214 | ||
215 | if ($task_status->{status} eq "stopped") { | |
216 | last; | |
217 | } | |
218 | ||
219 | sleep(10); | |
220 | } | |
221 | ||
222 | return $task_status->{exitstatus}; | |
223 | } | |
224 | ||
29505e2c | 225 | 1; |