blob: e538ef7e4c91220e3b75b8cbd199b89686495238 [file] [log] [blame]
Jian Lib5ab63c2021-02-03 17:54:28 +09001/*
2 * Copyright 2021-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.kubevirtnetworking.impl;
17
18import com.google.common.collect.Lists;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.IPv4;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.TpPort;
23import org.onlab.packet.UDP;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.LeadershipService;
26import org.onosproject.cluster.NodeId;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
30import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
31import org.onosproject.kubevirtnetworking.api.KubevirtNetworkEvent;
32import org.onosproject.kubevirtnetworking.api.KubevirtNetworkListener;
33import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
34import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
35import org.onosproject.kubevirtnode.api.KubevirtNode;
36import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
37import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
38import org.onosproject.kubevirtnode.api.KubevirtNodeService;
39import org.onosproject.net.Device;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.PortNumber;
42import org.onosproject.net.behaviour.BridgeConfig;
43import org.onosproject.net.behaviour.BridgeDescription;
44import org.onosproject.net.behaviour.BridgeName;
45import org.onosproject.net.behaviour.ControllerInfo;
46import org.onosproject.net.behaviour.DefaultBridgeDescription;
47import org.onosproject.net.behaviour.DefaultPatchDescription;
48import org.onosproject.net.behaviour.InterfaceConfig;
49import org.onosproject.net.behaviour.PatchDescription;
50import org.onosproject.net.device.DeviceAdminService;
51import org.onosproject.net.flow.DefaultTrafficSelector;
52import org.onosproject.net.flow.DefaultTrafficTreatment;
53import org.onosproject.net.flow.TrafficSelector;
54import org.onosproject.net.flow.TrafficTreatment;
55import org.osgi.service.component.annotations.Activate;
56import org.osgi.service.component.annotations.Component;
57import org.osgi.service.component.annotations.Deactivate;
58import org.osgi.service.component.annotations.Reference;
59import org.osgi.service.component.annotations.ReferenceCardinality;
60import org.slf4j.Logger;
61
62import java.util.List;
63import java.util.Objects;
64import java.util.concurrent.ExecutorService;
65
66import static java.lang.Thread.sleep;
67import static java.util.concurrent.Executors.newSingleThreadExecutor;
68import static org.onlab.util.Tools.groupedThreads;
69import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
70import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_DHCP_RULE;
71import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_SWITCHING_RULE;
72import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_DHCP_TABLE;
73import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_FORWARDING_TABLE;
74import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_INBOUND_TABLE;
75import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.segmentIdHex;
76import static org.onosproject.kubevirtnode.api.Constants.TUNNEL_BRIDGE;
77import static org.slf4j.LoggerFactory.getLogger;
78
79/**
80 * Handles kubevirt network events.
81 */
82@Component(immediate = true)
83public class KubevirtNetworkHandler {
84 protected final Logger log = getLogger(getClass());
85 private static final String DEFAULT_OF_PROTO = "tcp";
86 private static final int DEFAULT_OFPORT = 6653;
87 private static final int DPID_BEGIN = 3;
88 private static final long SLEEP_MS = 3000; // we wait 3s for init each node
89
90 public static final String INTEGRATION_TO_TUNNEL_PREFIX = "i-to-t-";
91 public static final String TUNNEL_TO_INTEGRATION_PREFIX = "t-to-i-";
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY)
94 protected CoreService coreService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY)
97 protected ClusterService clusterService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY)
100 protected LeadershipService leadershipService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
103 protected DeviceAdminService deviceService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected KubevirtApiConfigService apiConfigService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
109 protected KubevirtNodeService nodeService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
112 protected KubevirtNetworkService networkService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
115 protected KubevirtFlowRuleService flowService;
116
117 private final KubevirtNetworkListener networkListener = new InternalNetworkEventListener();
118 private final KubevirtNodeListener nodeListener = new InternalNodeEventListener();
119
120 private final ExecutorService eventExecutor = newSingleThreadExecutor(
121 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
122
123 private ApplicationId appId;
124 private NodeId localNodeId;
125
126 @Activate
127 protected void activate() {
128 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
129 localNodeId = clusterService.getLocalNode().id();
130 networkService.addListener(networkListener);
131 nodeService.addListener(nodeListener);
132 leadershipService.runForLeadership(appId.name());
133
134 log.info("Started");
135 }
136
137 @Deactivate
138 protected void deactivate() {
139 networkService.removeListener(networkListener);
140 nodeService.removeListener(nodeListener);
141 leadershipService.withdraw(appId.name());
142 eventExecutor.shutdown();
143
144 log.info("Stopped");
145 }
146
147 private void createBridge(KubevirtNode node, KubevirtNetwork network) {
148
149 Device tunBridge = deviceService.getDevice(network.tenantDeviceId(node.hostname()));
150 if (tunBridge != null) {
151 log.warn("The tunnel bridge {} already exists at node {}",
152 network.tenantBridgeName(), node.hostname());
153 setDefaultRules(node, network);
154 return;
155 }
156
157 Device device = deviceService.getDevice(node.ovsdb());
158
159 IpAddress serverIp = apiConfigService.apiConfig().ipAddress();
160 ControllerInfo controlInfo =
161 new ControllerInfo(serverIp, DEFAULT_OFPORT, DEFAULT_OF_PROTO);
162 List<ControllerInfo> controllers = Lists.newArrayList(controlInfo);
163
164 String dpid = network.tenantDeviceId(
165 node.hostname()).toString().substring(DPID_BEGIN);
166
167 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
168 .name(network.tenantBridgeName())
169 .failMode(BridgeDescription.FailMode.SECURE)
170 .datapathId(dpid)
171 .disableInBand()
172 .controllers(controllers);
173
174 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
175 bridgeConfig.addBridge(builder.build());
176 }
177
178 private void removeBridge(KubevirtNode node, KubevirtNetwork network) {
179 Device device = deviceService.getDevice(node.ovsdb());
180
181 BridgeName bridgeName = BridgeName.bridgeName(network.tenantBridgeName());
182
183 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
184 bridgeConfig.deleteBridge(bridgeName);
185 deviceService.removeDevice(network.tenantDeviceId(node.hostname()));
186 }
187
188 private void createPatchInterface(KubevirtNode node, KubevirtNetwork network) {
189 Device device = deviceService.getDevice(node.ovsdb());
190
191 if (device == null || !device.is(InterfaceConfig.class)) {
192 log.error("Failed to create patch interface on {}", node.ovsdb());
193 return;
194 }
195
196 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
197
198 String intToTunIntf =
199 INTEGRATION_TO_TUNNEL_PREFIX + segmentIdHex(network.segmentId());
200 String tunToIntIntf =
201 TUNNEL_TO_INTEGRATION_PREFIX + segmentIdHex(network.segmentId());
202
203 // integration bridge -> tunnel bridge
204 PatchDescription brIntTunPatchDesc =
205 DefaultPatchDescription.builder()
206 .deviceId(network.tenantBridgeName())
207 .ifaceName(intToTunIntf)
208 .peer(tunToIntIntf)
209 .build();
210
211 ifaceConfig.addPatchMode(intToTunIntf, brIntTunPatchDesc);
212
213 // tunnel bridge -> integration bridge
214 PatchDescription brTunIntPatchDesc =
215 DefaultPatchDescription.builder()
216 .deviceId(TUNNEL_BRIDGE)
217 .ifaceName(tunToIntIntf)
218 .peer(intToTunIntf)
219 .build();
220 ifaceConfig.addPatchMode(tunToIntIntf, brTunIntPatchDesc);
221 }
222
223 private void removePatchInterface(KubevirtNode node, KubevirtNetwork network) {
224 Device device = deviceService.getDevice(node.ovsdb());
225
226 if (device == null || !device.is(InterfaceConfig.class)) {
227 log.error("Failed to create patch interface on {}", node.ovsdb());
228 return;
229 }
230
231 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
232
233 String tunToIntIntf = TUNNEL_TO_INTEGRATION_PREFIX + segmentIdHex(network.segmentId());
234
235 ifaceConfig.removePatchMode(tunToIntIntf);
236 }
237
238 private void setDefaultRules(KubevirtNode node, KubevirtNetwork network) {
239 DeviceId deviceId = network.tenantDeviceId(node.hostname());
240
241 while (!deviceService.isAvailable(deviceId)) {
242 log.warn("Device {} is not ready for installing rules", deviceId);
243
244 try {
245 sleep(SLEEP_MS);
246 } catch (InterruptedException e) {
247 log.error("Failed to check device availability", e);
248 }
249 }
250
251 flowService.connectTables(deviceId, TENANT_INBOUND_TABLE, TENANT_DHCP_TABLE);
252 flowService.connectTables(deviceId, TENANT_DHCP_TABLE, TENANT_FORWARDING_TABLE);
253
254 setDhcpRule(deviceId, true);
255 setForwardingRule(deviceId, true);
256
257 log.info("Install default flow rules for tenant bridge {}", network.tenantBridgeName());
258 }
259
260 private void setDhcpRule(DeviceId deviceId, boolean install) {
261 TrafficSelector selector = DefaultTrafficSelector.builder()
262 .matchEthType(Ethernet.TYPE_IPV4)
263 .matchIPProtocol(IPv4.PROTOCOL_UDP)
264 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
265 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
266 .build();
267
268 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
269 .punt()
270 .build();
271
272 flowService.setRule(
273 appId,
274 deviceId,
275 selector,
276 treatment,
277 PRIORITY_DHCP_RULE,
278 TENANT_DHCP_TABLE,
279 install);
280 }
281
282 public void setForwardingRule(DeviceId deviceId, boolean install) {
283 TrafficSelector selector = DefaultTrafficSelector.builder().build();
284 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
285 .setOutput(PortNumber.NORMAL)
286 .build();
287
288 flowService.setRule(
289 appId,
290 deviceId,
291 selector,
292 treatment,
293 PRIORITY_SWITCHING_RULE,
294 TENANT_FORWARDING_TABLE,
295 install);
296 }
297
298 private class InternalNetworkEventListener implements KubevirtNetworkListener {
299
300 private boolean isRelevantHelper() {
301 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
302 }
303
304 @Override
305 public void event(KubevirtNetworkEvent event) {
306 switch (event.type()) {
307 case KUBEVIRT_NETWORK_CREATED:
308 eventExecutor.execute(() -> processNetworkCreation(event.subject()));
309 break;
310 case KUBEVIRT_NETWORK_REMOVED:
311 eventExecutor.execute(() -> processNetworkRemoval(event.subject()));
312 break;
313 case KUBEVIRT_NETWORK_UPDATED:
314 default:
315 // do nothing
316 break;
317 }
318 }
319
320 private void processNetworkCreation(KubevirtNetwork network) {
321 if (!isRelevantHelper()) {
322 return;
323 }
324
325 switch (network.type()) {
326 case VXLAN:
327 case GRE:
328 case GENEVE:
329 initIntegrationTunnelBridge(network);
330 break;
331 case FLAT:
332 default:
333 // do nothing
334 break;
335 }
336 }
337
338 private void processNetworkRemoval(KubevirtNetwork network) {
339 if (!isRelevantHelper()) {
340 return;
341 }
342
343 switch (network.type()) {
344 case VXLAN:
345 case GRE:
346 case GENEVE:
347 purgeIntegrationTunnelBridge(network);
348 break;
349 case FLAT:
350 default:
351 // do nothing
352 break;
353 }
354 }
355
356 private void initIntegrationTunnelBridge(KubevirtNetwork network) {
357 if (network.segmentId() == null) {
358 return;
359 }
360
361 nodeService.completeNodes().forEach(n -> {
362 createBridge(n, network);
363 createPatchInterface(n, network);
364 setDefaultRules(n, network);
365 });
366 }
367
368 private void purgeIntegrationTunnelBridge(KubevirtNetwork network) {
369 if (network.segmentId() == null) {
370 return;
371 }
372
373 nodeService.completeNodes().forEach(n -> {
374 removePatchInterface(n, network);
375 removeBridge(n, network);
376 });
377 }
378 }
379
380 private class InternalNodeEventListener implements KubevirtNodeListener {
381
382 private boolean isRelevantHelper() {
383 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
384 }
385
386 @Override
387 public void event(KubevirtNodeEvent event) {
388 switch (event.type()) {
389 case KUBEVIRT_NODE_COMPLETE:
390 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
391 break;
392 case KUBEVIRT_NODE_INCOMPLETE:
393 case KUBEVIRT_NODE_UPDATED:
394 default:
395 // do nothing
396 break;
397 }
398 }
399
400 private void processNodeCompletion(KubevirtNode node) {
401 if (!isRelevantHelper()) {
402 return;
403 }
404
405 for (KubevirtNetwork network : networkService.networks()) {
406 switch (network.type()) {
407 case VXLAN:
408 case GRE:
409 case GENEVE:
410 if (network.segmentId() == null) {
411 continue;
412 }
413 createBridge(node, network);
414 createPatchInterface(node, network);
415 setDefaultRules(node, network);
416 break;
417 case FLAT:
418 default:
419 // do nothing
420 break;
421 }
422 }
423 }
424 }
425}