]> git.proxmox.com Git - pve-network.git/blob - PVE/Network/SDN/Ipams/PVEPlugin.pm
subnets/ipam : fix is_gateway
[pve-network.git] / PVE / Network / SDN / Ipams / PVEPlugin.pm
1 package PVE::Network::SDN::Ipams::PVEPlugin;
2
3 use strict;
4 use warnings;
5 use PVE::INotify;
6 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file);
7 use PVE::Tools;
8 use JSON;
9 use NetAddr::IP qw(:lower);
10
11 use Net::IP;
12 use Digest::SHA;
13
14 use base('PVE::Network::SDN::Ipams::Plugin');
15
16
17 my $ipamdb_file = "priv/ipam.db";
18
19 PVE::Cluster::cfs_register_file($ipamdb_file,
20 sub { PVE::Network::SDN::Ipams::PVEPlugin->parse_config(@_); },
21 sub { PVE::Network::SDN::Ipams::PVEPlugin->write_config(@_); });
22
23 sub type {
24 return 'pve';
25 }
26
27 sub properties {
28 }
29
30 sub options {
31 }
32
33 # Plugin implementation
34
35 sub add_subnet {
36 my ($class, $plugin_config, $subnetid, $subnet) = @_;
37
38 my $cidr = $subnet->{cidr};
39 my $zone = $subnet->{zone};
40 my $gateway = $subnet->{gateway};
41
42
43 cfs_lock_file($ipamdb_file, undef, sub {
44 my $db = {};
45 $db = read_db();
46
47 $db->{zones}->{$zone} = {} if !$db->{zones}->{$zone};
48 my $zonedb = $db->{zones}->{$zone};
49
50 if(!$zonedb->{subnets}->{$cidr}) {
51 #create subnet
52 $zonedb->{subnets}->{$cidr}->{ips} = {};
53 write_db($db);
54 }
55 });
56 die "$@" if $@;
57 }
58
59 sub del_subnet {
60 my ($class, $plugin_config, $subnetid, $subnet) = @_;
61
62 my $cidr = $subnet->{cidr};
63 my $zone = $subnet->{zone};
64
65 cfs_lock_file($ipamdb_file, undef, sub {
66
67 my $db = read_db();
68
69 my $dbzone = $db->{zones}->{$zone};
70 die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
71 my $dbsubnet = $dbzone->{subnets}->{$cidr};
72 die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
73
74 die "cannot delete subnet '$cidr', not empty\n" if keys %{$dbsubnet->{ips}} > 0;
75
76 delete $dbzone->{subnets}->{$cidr};
77
78 write_db($db);
79 });
80 die "$@" if $@;
81
82 }
83
84 sub add_ip {
85 my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
86
87 my $cidr = $subnet->{cidr};
88 my $zone = $subnet->{zone};
89
90 cfs_lock_file($ipamdb_file, undef, sub {
91
92 my $db = read_db();
93 my $dbzone = $db->{zones}->{$zone};
94 die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
95 my $dbsubnet = $dbzone->{subnets}->{$cidr};
96 die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
97
98 die "IP '$ip' already exist\n" if (!$is_gateway && defined($dbsubnet->{ips}->{$ip})) || ($is_gateway && defined($dbsubnet->{ips}->{$ip}) && !defined($dbsubnet->{ips}->{$ip}->{gateway}));
99 $dbsubnet->{ips}->{$ip} = {};
100 $dbsubnet->{ips}->{$ip} = {gateway => 1} if $is_gateway;
101
102 write_db($db);
103 });
104 die "$@" if $@;
105 }
106
107 sub update_ip {
108 my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
109 return;
110 }
111
112 sub add_next_freeip {
113 my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_;
114
115 my $cidr = $subnet->{cidr};
116 my $network = $subnet->{network};
117 my $zone = $subnet->{zone};
118 my $mask = $subnet->{mask};
119 my $freeip = undef;
120
121 cfs_lock_file($ipamdb_file, undef, sub {
122
123 my $db = read_db();
124 my $dbzone = $db->{zones}->{$zone};
125 die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
126 my $dbsubnet = $dbzone->{subnets}->{$cidr};
127 die "subnet '$cidr' doesn't exist in IPAM DB" if !$dbsubnet;
128
129 if (Net::IP::ip_is_ipv4($network) && $mask == 32) {
130 die "cannot find free IP in subnet '$cidr'\n" if defined($dbsubnet->{ips}->{$network});
131 $freeip = $network;
132 } else {
133 my $iplist = NetAddr::IP->new($cidr);
134 my $lastip = $iplist->last()->canon();
135 $iplist++ if Net::IP::ip_is_ipv4($network); #skip network address for ipv4
136 while(1) {
137 my $ip = $iplist->canon();
138 if (defined($dbsubnet->{ips}->{$ip})) {
139 last if $ip eq $lastip;
140 $iplist++;
141 next;
142 }
143 $freeip = $ip;
144 last;
145 }
146 }
147
148 die "can't find free ip in subnet '$cidr'\n" if !$freeip;
149
150 $dbsubnet->{ips}->{$freeip} = {};
151
152 write_db($db);
153 });
154 die "$@" if $@;
155
156 return "$freeip/$mask";
157 }
158
159 sub del_ip {
160 my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
161
162 my $cidr = $subnet->{cidr};
163 my $zone = $subnet->{zone};
164
165 cfs_lock_file($ipamdb_file, undef, sub {
166
167 my $db = read_db();
168 die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone};
169 my $dbzone = $db->{zones}->{$zone};
170 die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr};
171 my $dbsubnet = $dbzone->{subnets}->{$cidr};
172
173 die "IP '$ip' does not exist in IPAM DB\n" if !defined($dbsubnet->{ips}->{$ip});
174 delete $dbsubnet->{ips}->{$ip};
175
176 write_db($db);
177 });
178 die "$@" if $@;
179 }
180
181 #helpers
182
183 sub read_db {
184 my $db = cfs_read_file($ipamdb_file);
185 return $db;
186 }
187
188 sub write_db {
189 my ($cfg) = @_;
190
191 my $json = to_json($cfg);
192 cfs_write_file($ipamdb_file, $json);
193 }
194
195 sub write_config {
196 my ($class, $filename, $cfg) = @_;
197
198 return $cfg;
199 }
200
201 sub parse_config {
202 my ($class, $filename, $raw) = @_;
203
204 $raw = '{}' if !defined($raw) ||$raw eq '';
205 my $cfg = from_json($raw);
206
207 return $cfg;
208 }
209
210 1;