]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2Client.pm
use max_workers from datacenter.cfg for stopall/migrateall
[pve-manager.git] / PVE / API2Client.pm
1 package PVE::API2Client;
2
3 use strict;
4 use warnings;
5 use URI;
6 use HTTP::Cookies;
7 use LWP::UserAgent;
8 use JSON;
9 use Data::Dumper; # fixme: remove
10 use HTTP::Request::Common;
11
12 sub get {
13 my ($self, $path, $param) = @_;
14
15 return $self->call('GET', $path, $param);
16 }
17
18 sub post {
19 my ($self, $path, $param) = @_;
20
21 return $self->call('POST', $path, $param);
22 }
23
24 sub put {
25 my ($self, $path, $param) = @_;
26
27 return $self->call('PUT', $path, $param);
28 }
29
30 sub delete {
31 my ($self, $path, $param) = @_;
32
33 return $self->call('DELETE', $path, $param);
34 }
35
36 sub update_ticket {
37 my ($self, $ticket) = @_;
38
39 my $domain = "$self->{host}.local" unless $self->{host} =~ /\./;
40 $self->{cookie_jar}->set_cookie(0, 'PVEAuthCookie', $ticket, '/', $domain);
41 }
42
43 sub login {
44 my ($self) = @_;
45
46 my $uri = URI->new();
47 $uri->scheme($self->{protocol});
48 $uri->host($self->{host});
49 $uri->port($self->{port});
50 $uri->path('/api2/json/access/ticket');
51
52 my $ua = $self->{useragent};
53
54 my $response = $ua->post($uri, {
55 username => $self->{username} || 'unknown',
56 password => $self->{password} || ''});
57
58 if (!$response->is_success) {
59 die $response->status_line . "\n";
60 }
61
62 my $data = from_json($response->decoded_content, {utf8 => 1, allow_nonref => 1});
63
64 $self->update_ticket($data->{data}->{ticket});
65 $self->{csrftoken} = $data->{data}->{CSRFPreventionToken};
66
67 return $data;
68 }
69
70 sub call {
71 my ($self, $method, $path, $param) = @_;
72
73 #print "wrapper called\n";
74
75 my $ticket;
76
77 my $ua = $self->{useragent};
78 my $cj = $self->{cookie_jar};
79
80 $cj->scan(sub {
81 my ($version, $key, $val) = @_;
82 $ticket = $val if $key eq 'PVEAuthCookie';
83 });
84
85 if (!$ticket && $self->{username} && $self->{password}) {
86 $self->login();
87 }
88
89 my $uri = URI->new();
90 $uri->scheme($self->{protocol});
91 $uri->host($self->{host});
92 $uri->port($self->{port});
93 $uri->path($path);
94
95 # print $ua->{cookie_jar}->as_string;
96
97 #print "CALL $method : " . $uri->as_string() . "\n";
98
99 if ($self->{csrftoken}) {
100 $self->{useragent}->default_header('CSRFPreventionToken' => $self->{csrftoken});
101 }
102
103 my $response;
104 if ($method eq 'GET') {
105 $uri->query_form($param);
106 $response = $ua->request(HTTP::Request::Common::GET($uri));
107 } elsif ($method eq 'POST') {
108 $response = $ua->request(HTTP::Request::Common::POST($uri, Content => $param));
109 } elsif ($method eq 'PUT') {
110 # We use another temporary URI object to format
111 # the application/x-www-form-urlencoded content.
112
113 my $tmpurl = URI->new('http:');
114 $tmpurl->query_form(%$param);
115 my $content = $tmpurl->query;
116
117 $response = $ua->request(HTTP::Request::Common::PUT($uri, 'Content-Type' => 'application/x-www-form-urlencoded', Content => $content));
118
119 } elsif ($method eq 'DELETE') {
120 $response = $ua->request(HTTP::Request::Common::DELETE($uri));
121 } else {
122 die "method $method not implemented\n";
123 }
124
125 #print "RESP: " . Dumper($response) . "\n";
126
127 my $ct = $response->header('Content-Type') || '';
128
129 if ($response->is_success) {
130
131 die "got unexpected content type" if $ct !~ m|application/json|;
132
133 return from_json($response->decoded_content, {utf8 => 1, allow_nonref => 1});
134
135 } else {
136
137 my $msg = $response->status_line . "\n";
138 eval {
139 return if $ct !~ m|application/json|;
140 my $res = from_json($response->decoded_content, {utf8 => 1, allow_nonref => 1});
141 if (my $errors = $res->{errors}) {
142 foreach my $key (keys $errors) {
143 my $m = $errors->{$key};
144 chomp($m);
145 $m =~s/\n/ -- /g;
146 $msg .= " $key: $m\n";
147 }
148 }
149 };
150 die $msg;
151
152 }
153 }
154
155 sub new {
156 my ($class, %param) = @_;
157
158 my $self = {
159 ticket => $param{ticket},
160 csrftoken => $param{csrftoken},
161 username => $param{username},
162 password => $param{password},
163 host => $param{host} || 'localhost',
164 port => $param{port},
165 protocol => $param{protocol},
166 timeout => $param{timeout} || 60,
167 };
168 bless $self;
169
170 if (!$self->{port}) {
171 $self->{port} = $self->{host} eq 'localhost' ? 85 : 8006;
172 }
173 if (!$self->{protocol}) {
174 $self->{protocol} = $self->{host} eq 'localhost' ? 'http' : 'https';
175 }
176
177 $self->{cookie_jar} = HTTP::Cookies->new (ignore_discard => 1);
178
179 $self->update_ticket($self->{ticket}) if $self->{ticket};
180
181 $self->{useragent} = LWP::UserAgent->new(
182 cookie_jar => $self->{cookie_jar},
183 protocols_allowed => [ 'http', 'https'],
184 ssl_opts => { verify_hostname => 0 },
185 timeout => $self->{timeout},
186 );
187
188 $self->{useragent}->default_header('Accept-Encoding' => 'gzip'); # allow gzip
189
190 return $self;
191 }
192
193 1;