]> git.proxmox.com Git - pve-network.git/blob - src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
ipam: plugins: preparations for DHCP
[pve-network.git] / src / PVE / Network / SDN / Ipams / NetboxPlugin.pm
1 package PVE::Network::SDN::Ipams::NetboxPlugin;
2
3 use strict;
4 use warnings;
5 use PVE::INotify;
6 use PVE::Cluster;
7 use PVE::Tools;
8
9 use base('PVE::Network::SDN::Ipams::Plugin');
10
11 sub type {
12 return 'netbox';
13 }
14
15 sub properties {
16 return {
17 };
18 }
19
20 sub options {
21
22 return {
23 url => { optional => 0},
24 token => { optional => 0 },
25 };
26 }
27
28 # Plugin implementation
29
30 sub add_subnet {
31 my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
32
33 my $cidr = $subnet->{cidr};
34 my $gateway = $subnet->{gateway};
35 my $url = $plugin_config->{url};
36 my $token = $plugin_config->{token};
37 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
38
39 my $internalid = get_prefix_id($url, $cidr, $headers);
40
41 #create subnet
42 if (!$internalid) {
43
44 my $params = { prefix => $cidr };
45
46 eval {
47 my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
48 };
49 if ($@) {
50 die "error add subnet to ipam: $@" if !$noerr;
51 }
52 }
53
54 }
55
56 sub del_subnet {
57 my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
58
59 my $cidr = $subnet->{cidr};
60 my $url = $plugin_config->{url};
61 my $token = $plugin_config->{token};
62 my $gateway = $subnet->{gateway};
63 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
64
65 my $internalid = get_prefix_id($url, $cidr, $headers);
66 return if !$internalid;
67
68 return; #fixme: check that prefix is empty exluding gateway, before delete
69
70 eval {
71 PVE::Network::SDN::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
72 };
73 if ($@) {
74 die "error deleting subnet from ipam: $@" if !$noerr;
75 }
76
77 }
78
79 sub add_ip {
80 my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
81
82 my $mask = $subnet->{mask};
83 my $url = $plugin_config->{url};
84 my $token = $plugin_config->{token};
85 my $section = $plugin_config->{section};
86 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
87
88 my $description = undef;
89 if ($is_gateway) {
90 $description = 'gateway'
91 } elsif ($mac) {
92 $description = "mac:$mac";
93 }
94
95 my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
96
97 eval {
98 PVE::Network::SDN::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params);
99 };
100
101 if ($@) {
102 if($is_gateway) {
103 die "error add subnet ip to ipam: ip $ip already exist: $@" if !is_ip_gateway($url, $ip, $headers) && !$noerr;
104 } else {
105 die "error add subnet ip to ipam: ip already exist: $@" if !$noerr;
106 }
107 }
108 }
109
110 sub update_ip {
111 my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
112
113 my $mask = $subnet->{mask};
114 my $url = $plugin_config->{url};
115 my $token = $plugin_config->{token};
116 my $section = $plugin_config->{section};
117 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
118
119 my $description = undef;
120 if ($is_gateway) {
121 $description = 'gateway'
122 } elsif ($mac) {
123 $description = "mac:$mac";
124 }
125
126 my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
127
128 my $ip_id = get_ip_id($url, $ip, $headers);
129 die "can't find ip $ip in ipam" if !$ip_id;
130
131 eval {
132 PVE::Network::SDN::api_request("PATCH", "$url/ipam/ip-addresses/$ip_id/", $headers, $params);
133 };
134 if ($@) {
135 die "error update ip $ip : $@" if !$noerr;
136 }
137 }
138
139 sub add_next_freeip {
140 my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
141
142 my $cidr = $subnet->{cidr};
143
144 my $url = $plugin_config->{url};
145 my $token = $plugin_config->{token};
146 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
147
148 my $internalid = get_prefix_id($url, $cidr, $headers);
149
150 my $description = "mac:$mac" if $mac;
151
152 my $params = { dns_name => $hostname, description => $description };
153
154 my $ip = undef;
155 eval {
156 my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/$internalid/available-ips/", $headers, $params);
157 $ip = $result->{address};
158 };
159
160 if ($@) {
161 die "can't find free ip in subnet $cidr: $@" if !$noerr;
162 }
163
164 return $ip;
165 }
166
167 sub add_range_next_freeip {
168 my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
169
170 my $url = $plugin_config->{url};
171 my $token = $plugin_config->{token};
172 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
173
174 my $internalid = get_iprange_id($url, $range, $headers);
175 my $description = "mac:$data->{mac}" if $data->{mac};
176
177 my $params = { dns_name => $data->{hostname}, description => $description };
178
179 my $ip = undef;
180 eval {
181 my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/ip-ranges/$internalid/available-ips/", $headers, $params);
182 $ip = $result->{address};
183 print "found ip free $ip in range $range->{'start-address'}-$range->{'end-address'}\n" if $ip;
184 };
185
186 if ($@) {
187 die "can't find free ip in range $range->{'start-address'}-$range->{'end-address'}: $@" if !$noerr;
188 }
189
190 return $ip;
191
192 }
193
194 sub del_ip {
195 my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
196
197 return if !$ip;
198
199 my $url = $plugin_config->{url};
200 my $token = $plugin_config->{token};
201 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
202
203 my $ip_id = get_ip_id($url, $ip, $headers);
204 die "can't find ip $ip in ipam" if !$ip_id;
205
206 eval {
207 PVE::Network::SDN::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers);
208 };
209 if ($@) {
210 die "error delete ip $ip : $@" if !$noerr;
211 }
212 }
213
214 sub get_ips_from_mac {
215 my ($class, $plugin_config, $mac, $zoneid) = @_;
216
217 my $url = $plugin_config->{url};
218 my $token = $plugin_config->{token};
219 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
220
221 my $ip4 = undef;
222 my $ip6 = undef;
223
224 my $data = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?description__ic=$mac", $headers);
225 for my $ip (@{$data->{results}}) {
226 if ($ip->{family}->{value} == 4 && !$ip4) {
227 ($ip4, undef) = split(/\//, $ip->{address});
228 }
229
230 if ($ip->{family}->{value} == 6 && !$ip6) {
231 ($ip6, undef) = split(/\//, $ip->{address});
232 }
233 }
234
235 return ($ip4, $ip6);
236 }
237
238
239 sub verify_api {
240 my ($class, $plugin_config) = @_;
241
242 my $url = $plugin_config->{url};
243 my $token = $plugin_config->{token};
244 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
245
246
247 eval {
248 PVE::Network::SDN::api_request("GET", "$url/ipam/aggregates/", $headers);
249 };
250 if ($@) {
251 die "Can't connect to netbox api: $@";
252 }
253 }
254
255 sub on_update_hook {
256 my ($class, $plugin_config) = @_;
257
258 PVE::Network::SDN::Ipams::NetboxPlugin::verify_api($class, $plugin_config);
259 }
260
261 #helpers
262
263 sub get_prefix_id {
264 my ($url, $cidr, $headers) = @_;
265
266 my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/prefixes/?q=$cidr", $headers);
267 my $data = @{$result->{results}}[0];
268 my $internalid = $data->{id};
269 return $internalid;
270 }
271
272 sub get_iprange_id {
273 my ($url, $range, $headers) = @_;
274
275 my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-ranges/?start_address=$range->{'start-address'}&end_address=$range->{'end-address'}", $headers);
276 my $data = @{$result->{results}}[0];
277 my $internalid = $data->{id};
278 return $internalid;
279 }
280
281 sub get_ip_id {
282 my ($url, $ip, $headers) = @_;
283 my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
284 my $data = @{$result->{results}}[0];
285 my $ip_id = $data->{id};
286 return $ip_id;
287 }
288
289 sub is_ip_gateway {
290 my ($url, $ip, $headers) = @_;
291 my $result = PVE::Network::SDN::api_request("GET", "$url/addresses/search/$ip", $headers);
292 my $data = @{$result->{data}}[0];
293 my $description = $data->{description};
294 my $is_gateway = 1 if $description eq 'gateway';
295 return $is_gateway;
296 }
297
298 1;
299
300