]> git.proxmox.com Git - pve-network.git/blob - PVE/Network/SDN.pm
add new status sub and move code from test
[pve-network.git] / PVE / Network / SDN.pm
1 package PVE::Network::SDN;
2
3 use strict;
4 use warnings;
5
6 use Data::Dumper;
7 use JSON;
8
9 use PVE::Tools qw(extract_param dir_glob_regex run_command);
10 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
11 use PVE::Network::SDN::Plugin;
12 use PVE::Network::SDN::VnetPlugin;
13 use PVE::Network::SDN::VlanPlugin;
14 use PVE::Network::SDN::VxlanMulticastPlugin;
15
16 PVE::Network::SDN::VnetPlugin->register();
17 PVE::Network::SDN::VlanPlugin->register();
18 PVE::Network::SDN::VxlanMulticastPlugin->register();
19 PVE::Network::SDN::Plugin->init();
20
21
22 sub sdn_config {
23 my ($cfg, $sdnid, $noerr) = @_;
24
25 die "no sdn ID specified\n" if !$sdnid;
26
27 my $scfg = $cfg->{ids}->{$sdnid};
28 die "sdn '$sdnid' does not exists\n" if (!$noerr && !$scfg);
29
30 return $scfg;
31 }
32
33 sub config {
34 my $config = cfs_read_file("sdn.cfg.new");
35 $config = cfs_read_file("sdn.cfg") if !keys %{$config->{ids}};
36 return $config;
37 }
38
39 sub write_config {
40 my ($cfg) = @_;
41
42 cfs_write_file("sdn.cfg.new", $cfg);
43 }
44
45 sub lock_sdn_config {
46 my ($code, $errmsg) = @_;
47
48 cfs_lock_file("sdn.cfg.new", undef, $code);
49 if (my $err = $@) {
50 $errmsg ? die "$errmsg: $err" : die $err;
51 }
52 }
53
54 sub sdn_ids {
55 my ($cfg) = @_;
56
57 return keys %{$cfg->{ids}};
58 }
59
60 sub complete_sdn {
61 my ($cmdname, $pname, $cvalue) = @_;
62
63 my $cfg = PVE::Network::SDN::config();
64
65 return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_ids($cfg) ];
66 }
67
68 sub ifquery_check {
69
70 my $cmd = ['ifquery', '-a', '-c', '-o','json'];
71
72 my $result = '';
73 my $reader = sub { $result .= shift };
74
75 eval {
76 run_command($cmd, outfunc => $reader);
77 };
78
79 my $resultjson = decode_json($result);
80 my $interfaces = {};
81
82 foreach my $interface (@$resultjson) {
83 my $name = $interface->{name};
84 $interfaces->{$name} = {
85 status => $interface->{status},
86 config => $interface->{config},
87 config_status => $interface->{config_status},
88 };
89 }
90
91 return $interfaces;
92 }
93
94
95 sub generate_etc_network_config {
96
97 my $sdn_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
98 return if !$sdn_cfg;
99
100 #read main config for physical interfaces
101 my $current_config_file = "/etc/network/interfaces";
102 my $fh = IO::File->new($current_config_file);
103 my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
104 $fh->close();
105
106 #check uplinks
107 my $uplinks = {};
108 foreach my $id (keys %{$interfaces_config->{ifaces}}) {
109 my $interface = $interfaces_config->{ifaces}->{$id};
110 if (my $uplink = $interface->{'uplink-id'}) {
111 die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
112 $interface->{name} = $id;
113 $uplinks->{$interface->{'uplink-id'}} = $interface;
114 }
115 }
116
117 my $vnet_cfg = undef;
118 my $transport_cfg = undef;
119
120 foreach my $id (keys %{$sdn_cfg->{ids}}) {
121 if ($sdn_cfg->{ids}->{$id}->{type} eq 'vnet') {
122 $vnet_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id};
123 } else {
124 $transport_cfg->{ids}->{$id} = $sdn_cfg->{ids}->{$id};
125 }
126 }
127
128 #generate configuration
129 my $rawconfig = "";
130 foreach my $id (keys %{$vnet_cfg->{ids}}) {
131 my $vnet = $vnet_cfg->{ids}->{$id};
132 my $zone = $vnet->{transportzone};
133
134 if(!$zone) {
135 warn "can't generate vnet $vnet : zone $zone don't exist";
136 next;
137 }
138
139 my $plugin_config = $transport_cfg->{ids}->{$zone};
140
141 if (!defined($plugin_config)) {
142 warn "can't generate vnet $vnet : zone $zone don't exist";
143 next;
144 }
145
146 my $plugin = PVE::Network::SDN::Plugin->lookup($plugin_config->{type});
147 $rawconfig .= $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $uplinks);
148 }
149
150 return $rawconfig;
151 }
152
153 sub write_etc_network_config {
154 my ($rawconfig) = @_;
155
156 return if !$rawconfig;
157 my $sdn_interfaces_file = "/etc/network/interfaces.d/sdn";
158
159 my $writefh = IO::File->new($sdn_interfaces_file,">");
160 print $writefh $rawconfig;
161 $writefh->close();
162 }
163
164
165 sub status {
166
167 my $cluster_sdn_file = "/etc/pve/sdn.cfg";
168 my $local_sdn_file = "/etc/network/interfaces.d/sdn";
169 my $err_config = undef;
170
171 return if !-e $cluster_sdn_file;
172
173 if (!-e $local_sdn_file) {
174 warn "local sdn network configuration is not yet generated, please reload";
175 $err_config = 'pending';
176 } else {
177 # fixme : use some kind of versioning info?
178 my $cluster_sdn_timestamp = (stat($cluster_sdn_file))[9];
179 my $local_sdn_timestamp = (stat($local_sdn_file))[9];
180
181 if ($local_sdn_timestamp < $cluster_sdn_timestamp) {
182 warn "local sdn network configuration is too old, please reload";
183 $err_config = 'unknown';
184 }
185 }
186
187 my $status = ifquery_check();
188
189 my $network_cfg = PVE::Cluster::cfs_read_file('sdn.cfg');
190 my $vnet_cfg = undef;
191 my $transport_cfg = undef;
192
193 my $vnet_status = {};
194 my $transport_status = {};
195
196 foreach my $id (keys %{$network_cfg->{ids}}) {
197 if ($network_cfg->{ids}->{$id}->{type} eq 'vnet') {
198 my $transportzone = $network_cfg->{ids}->{$id}->{transportzone};
199 $vnet_status->{$id}->{transportzone} = $transportzone;
200 $transport_status->{$transportzone}->{status} = 'available' if !defined($transport_status->{$transportzone}->{status});
201
202 if($err_config) {
203 $vnet_status->{$id}->{status} = $err_config;
204 $transport_status->{$transportzone}->{status} = $err_config;
205 } elsif ($status->{$id}->{status} && $status->{$id}->{status} eq 'pass') {
206 $vnet_status->{$id}->{status} = 'available';
207 my $bridgeport = $status->{$id}->{config}->{'bridge-ports'};
208
209 if ($status->{$bridgeport}->{status} && $status->{$bridgeport}->{status} ne 'pass') {
210 $vnet_status->{$id}->{status} = 'error';
211 $transport_status->{$transportzone}->{status} = 'error';
212 }
213 } else {
214 $vnet_status->{$id}->{status} = 'error';
215 $transport_status->{$transportzone}->{status} = 'error';
216 }
217 }
218 }
219 return($transport_status, $vnet_status);
220 }
221
222 1;
223