]> git.proxmox.com Git - pve-network.git/blob - PVE/Network/SDN/SubnetPlugin.pm
subnets/ipam: allow same subnet on different zones
[pve-network.git] / PVE / Network / SDN / SubnetPlugin.pm
1 package PVE::Network::SDN::SubnetPlugin;
2
3 use strict;
4 use warnings;
5
6 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
7 use base qw(PVE::SectionConfig);
8 use PVE::JSONSchema qw(get_standard_option);
9 use PVE::Exception qw(raise raise_param_exc);
10 use Net::Subnet qw(subnet_matcher);
11 use PVE::Network::SDN::Vnets;
12 use PVE::Network::SDN::Ipams;
13 use Net::IP;
14
15 PVE::Cluster::cfs_register_file('sdn/subnets.cfg',
16 sub { __PACKAGE__->parse_config(@_); },
17 sub { __PACKAGE__->write_config(@_); });
18
19 PVE::JSONSchema::register_standard_option('pve-sdn-subnet-id', {
20 description => "The SDN subnet object identifier.",
21 type => 'string', format => 'pve-sdn-subnet-id',
22 type => 'string'
23 });
24
25 PVE::JSONSchema::register_format('pve-sdn-subnet-id', \&parse_sdn_subnet_id);
26 sub parse_sdn_subnet_id {
27 my ($id, $noerr) = @_;
28
29 my $cidr = "";
30 if($id =~ /\//) {
31 $cidr = $id;
32 } else {
33 my ($zone, $ip, $mask) = split(/-/, $id);
34 $cidr = "$ip/$mask";
35 }
36
37 if (!(PVE::JSONSchema::pve_verify_cidrv4($cidr, 1) ||
38 PVE::JSONSchema::pve_verify_cidrv6($cidr, 1)))
39 {
40 return undef if $noerr;
41 die "value does not look like a valid CIDR network\n";
42 }
43 return $id;
44 }
45
46 my $defaultData = {
47
48 propertyList => {
49 subnet => get_standard_option('pve-sdn-subnet-id',
50 { completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnet }),
51 },
52 };
53
54 sub type {
55 return 'subnet';
56 }
57
58 sub private {
59 return $defaultData;
60 }
61
62 sub properties {
63 return {
64 vnet => {
65 type => 'string',
66 description => "associated vnet",
67 },
68 gateway => {
69 type => 'string', format => 'ip',
70 description => "Subnet Gateway: Will be assign on vnet for layer3 zones",
71 },
72 snat => {
73 type => 'boolean',
74 description => "enable masquerade for this subnet if pve-firewall",
75 },
76 # #cloudinit, dhcp options
77 # routes => {
78 # type => 'string',
79 # description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
80 # },
81 dnszoneprefix => {
82 type => 'string', format => 'dns-name',
83 description => "dns domain zone prefix ex: 'adm' -> <hostname>.adm.mydomain.com",
84 },
85 };
86 }
87
88 sub options {
89 return {
90 vnet => { optional => 0 },
91 gateway => { optional => 1 },
92 # routes => { optional => 1 },
93 snat => { optional => 1 },
94 dnszoneprefix => { optional => 1 },
95 };
96 }
97
98 sub on_update_hook {
99 my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_;
100
101 my $cidr = $subnet->{cidr};
102 my $mask = $subnet->{mask};
103
104 my $subnet_matcher = subnet_matcher($cidr);
105
106 my $vnetid = $subnet->{vnet};
107 my $gateway = $subnet->{gateway};
108 my $ipam = $zone->{ipam};
109 my $dns = $zone->{dns};
110 my $dnszone = $zone->{dnszone};
111 my $reversedns = $zone->{reversedns};
112
113 my $old_gateway = $old_subnet->{gateway} if $old_subnet;
114
115 if($vnetid) {
116 my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
117 raise_param_exc({ vnet => "$vnetid don't exist"}) if !$vnet;
118 raise_param_exc({ vnet => "you can't add a subnet on a vlanaware vnet"}) if $vnet->{vlanaware};
119 }
120
121 my $pointopoint = 1 if Net::IP::ip_is_ipv4($gateway) && $mask == 32;
122
123 #for /32 pointopoint, we allow gateway outside the subnet
124 raise_param_exc({ gateway => "$gateway is not in subnet $cidr"}) if $gateway && !$subnet_matcher->($gateway) && !$pointopoint;
125
126
127 if ($ipam) {
128 my $ipam_cfg = PVE::Network::SDN::Ipams::config();
129 my $plugin_config = $ipam_cfg->{ids}->{$ipam};
130 my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
131 $plugin->add_subnet($plugin_config, $subnetid, $subnet);
132
133 #don't register gateway for pointopoint
134 return if $pointopoint;
135
136 #delete gateway on removal
137 if (!defined($gateway) && $old_gateway) {
138 eval {
139 PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
140 };
141 warn if $@;
142 }
143 if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
144 PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway);
145 }
146
147 #delete old gateway after update
148 if($gateway && $old_gateway && $gateway ne $old_gateway) {
149 eval {
150 PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
151 };
152 warn if $@;
153 }
154 }
155 }
156
157 sub on_delete_hook {
158 my ($class, $subnetid, $subnet_cfg, $vnet_cfg) = @_;
159
160 return;
161 }
162
163 1;