blob: 895c7934963f265cc4ac2bfe61b4facf999f7a68 [file] [log] [blame]
Hyunsun Moonb77b60f2016-01-15 20:03:18 -08001/*
2 * Copyright 2014-2015 Open Networking Laboratory
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.cordvtn;
17
18import com.google.common.collect.Sets;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.util.ItemNotFoundException;
26import org.onlab.util.KryoNamespace;
27import org.onosproject.cluster.ClusterService;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DefaultAnnotations;
33import org.onosproject.net.Device;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.Host;
36import org.onosproject.net.Port;
37import org.onosproject.net.behaviour.BridgeConfig;
38import org.onosproject.net.behaviour.BridgeName;
39import org.onosproject.net.behaviour.ControllerInfo;
40import org.onosproject.net.behaviour.DefaultTunnelDescription;
41import org.onosproject.net.behaviour.TunnelConfig;
42import org.onosproject.net.behaviour.TunnelDescription;
43import org.onosproject.net.behaviour.TunnelName;
44import org.onosproject.net.config.ConfigFactory;
45import org.onosproject.net.config.NetworkConfigEvent;
46import org.onosproject.net.config.NetworkConfigListener;
47import org.onosproject.net.config.NetworkConfigRegistry;
48import org.onosproject.net.config.NetworkConfigService;
49import org.onosproject.net.config.basics.SubjectFactories;
50import org.onosproject.net.device.DeviceAdminService;
51import org.onosproject.net.device.DeviceEvent;
52import org.onosproject.net.device.DeviceListener;
53import org.onosproject.net.device.DeviceService;
54import org.onosproject.net.driver.DriverHandler;
55import org.onosproject.net.driver.DriverService;
56import org.onosproject.net.flow.FlowRuleService;
57import org.onosproject.net.group.GroupService;
58import org.onosproject.net.host.HostService;
59import org.onosproject.ovsdb.controller.OvsdbClientService;
60import org.onosproject.ovsdb.controller.OvsdbController;
61import org.onosproject.ovsdb.controller.OvsdbNodeId;
62import org.onosproject.store.serializers.KryoNamespaces;
63import org.onosproject.store.service.ConsistentMap;
64import org.onosproject.store.service.Serializer;
65import org.onosproject.store.service.StorageService;
66import org.slf4j.Logger;
67
68import java.util.ArrayList;
69import java.util.HashMap;
70import java.util.List;
71import java.util.Map;
72import java.util.concurrent.ExecutorService;
73
74import static com.google.common.base.Preconditions.checkNotNull;
75import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
76import static org.onlab.util.Tools.groupedThreads;
77import static org.onosproject.net.Device.Type.SWITCH;
78import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
79import static org.slf4j.LoggerFactory.getLogger;
80
81/**
82 * Reads node information from the network config file and handles the config
83 * update events.
84 * Only a leader controller performs the node addition or deletion.
85 */
86@Component(immediate = true)
87@Service(value = CordVtnNodeManager.class)
88public class CordVtnNodeManager {
89
90 protected final Logger log = getLogger(getClass());
91
92 private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
93 .register(KryoNamespaces.API)
94 .register(CordVtnNode.class)
95 .register(NodeState.class);
96
97 private static final String DEFAULT_BRIDGE = "br-int";
98 private static final String DEFAULT_TUNNEL = "vxlan";
99 private static final String VPORT_PREFIX = "tap";
100 private static final String OK = "OK";
101 private static final String NO = "NO";
102
103 private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
104 {
105 put("key", "flow");
106 put("remote_ip", "flow");
107 }
108 };
109 private static final int DPID_BEGIN = 3;
110 private static final int OFPORT = 6653;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected CoreService coreService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected NetworkConfigRegistry configRegistry;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected NetworkConfigService configService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected StorageService storageService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected DeviceAdminService adminService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected OvsdbController controller;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected ClusterService clusterService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected DriverService driverService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
137 protected DeviceService deviceService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected HostService hostService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
143 protected FlowRuleService flowRuleService;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected MastershipService mastershipService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
149 protected GroupService groupService;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected CordVtnService cordVtnService;
153
154 private final ConfigFactory configFactory =
155 new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
156 @Override
157 public CordVtnConfig createConfig() {
158 return new CordVtnConfig();
159 }
160 };
161
162 private final ExecutorService eventExecutor =
163 newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtncfg", "event-handler"));
164
165 private final NetworkConfigListener configListener = new InternalConfigListener();
166 private final DeviceListener deviceListener = new InternalDeviceListener();
167
168 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
169 private final BridgeHandler bridgeHandler = new BridgeHandler();
170
171 private ConsistentMap<CordVtnNode, NodeState> nodeStore;
172 private CordVtnRuleInstaller ruleInstaller;
173 private ApplicationId appId;
174
175 private enum NodeState {
176
177 INIT {
178 @Override
179 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
180 nodeManager.connectOvsdb(node);
181 }
182 },
183 OVSDB_CONNECTED {
184 @Override
185 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
186 if (!nodeManager.getOvsdbConnectionState(node)) {
187 nodeManager.connectOvsdb(node);
188 } else {
189 nodeManager.createIntegrationBridge(node);
190 }
191 }
192 },
193 BRIDGE_CREATED {
194 @Override
195 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
196 if (!nodeManager.getOvsdbConnectionState(node)) {
197 nodeManager.connectOvsdb(node);
198 } else {
199 nodeManager.createTunnelInterface(node);
200 }
201 }
202 },
203 COMPLETE {
204 @Override
205 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
206 nodeManager.postInit(node);
207 }
208 },
209 INCOMPLETE {
210 @Override
211 public void process(CordVtnNodeManager nodeManager, CordVtnNode node) {
212 }
213 };
214
215 // TODO Add physical port add state
216
217 public abstract void process(CordVtnNodeManager nodeManager, CordVtnNode node);
218 }
219
220 @Activate
221 protected void active() {
222 appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
223
224 nodeStore = storageService.<CordVtnNode, NodeState>consistentMapBuilder()
225 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
226 .withName("cordvtn-nodestore")
227 .withApplicationId(appId)
228 .build();
229
230 ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
231 deviceService,
232 driverService,
233 groupService,
234 mastershipService,
235 DEFAULT_TUNNEL);
236
237 deviceService.addListener(deviceListener);
238 configService.addListener(configListener);
239 configRegistry.registerConfigFactory(configFactory);
240 }
241
242 @Deactivate
243 protected void deactivate() {
244 configRegistry.unregisterConfigFactory(configFactory);
245 configService.removeListener(configListener);
246 deviceService.removeListener(deviceListener);
247
248 eventExecutor.shutdown();
249 nodeStore.clear();
250 }
251
252 /**
253 * Adds a new node to the service.
254 *
255 * @param node cordvtn node
256 */
257 public void addNode(CordVtnNode node) {
258 checkNotNull(node);
259
260 nodeStore.putIfAbsent(node, checkNodeState(node));
261 initNode(node);
262 }
263
264 /**
265 * Deletes a node from the service.
266 *
267 * @param node cordvtn node
268 */
269 public void deleteNode(CordVtnNode node) {
270 checkNotNull(node);
271
272 if (getOvsdbConnectionState(node)) {
273 disconnectOvsdb(node);
274 }
275
276 nodeStore.remove(node);
277 }
278
279 /**
280 * Initiates node to serve virtual tenant network.
281 *
282 * @param node cordvtn node
283 */
284 public void initNode(CordVtnNode node) {
285 checkNotNull(node);
286
287 if (!nodeStore.containsKey(node)) {
288 log.warn("Node {} does not exist, add node first", node.hostname());
289 return;
290 }
291
292 NodeState state = checkNodeState(node);
293 state.process(this, node);
294 }
295
296 /**
297 * Returns node initialization state.
298 *
299 * @param node cordvtn node
300 * @return true if initial node setup is completed, otherwise false
301 */
302 public boolean getNodeInitState(CordVtnNode node) {
303 checkNotNull(node);
304
305 NodeState state = getNodeState(node);
306 return state != null && state.equals(NodeState.COMPLETE);
307 }
308
309 /**
310 * Returns detailed node initialization state.
311 * Return string includes the following information.
312 *
313 * Integration bridge created/connected: OK or NO
314 * VXLAN interface created: OK or NO
315 * Physical interface added: OK or NO
316 *
317 * @param node cordvtn node
318 * @return string including detailed node init state
319 */
320 public String checkNodeInitState(CordVtnNode node) {
321 checkNotNull(node);
322
323 NodeState state = getNodeState(node);
324 if (state == null) {
325 log.warn("Failed to get init state of {}", node.hostname());
326 return null;
327 }
328
329 String result = String.format(
330 "Integration bridge created/connected : %s (%s)%n" +
331 "VXLAN interface created : %s%n" +
332 "Physical interface added : %s (%s)",
333 checkIntegrationBridge(node) ? OK : NO, DEFAULT_BRIDGE,
334 checkTunnelInterface(node) ? OK : NO,
335 checkPhyInterface(node) ? OK : NO, node.phyPortName());
336
337 return result;
338 }
339
340 /**
341 * Returns the number of the nodes known to the service.
342 *
343 * @return number of nodes
344 */
345 public int getNodeCount() {
346 return nodeStore.size();
347 }
348
349 /**
350 * Returns all nodes known to the service.
351 *
352 * @return list of nodes
353 */
354 public List<CordVtnNode> getNodes() {
355 List<CordVtnNode> nodes = new ArrayList<>();
356 nodes.addAll(nodeStore.keySet());
357 return nodes;
358 }
359
360 /**
361 * Returns cordvtn node associated with a given OVSDB device.
362 *
363 * @param ovsdbId OVSDB device id
364 * @return cordvtn node, null if it fails to find the node
365 */
366 private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) {
367 return getNodes().stream()
368 .filter(node -> node.ovsdbId().equals(ovsdbId))
369 .findFirst().orElse(null);
370 }
371
372 /**
373 * Returns cordvtn node associated with a given integration bridge.
374 *
375 * @param bridgeId device id of integration bridge
376 * @return cordvtn node, null if it fails to find the node
377 */
378 private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) {
379 return getNodes().stream()
380 .filter(node -> node.intBrId().equals(bridgeId))
381 .findFirst().orElse(null);
382 }
383
384 /**
385 * Returns state of a given cordvtn node.
386 *
387 * @param node cordvtn node
388 * @return node state, or null if no such node exists
389 */
390 private NodeState getNodeState(CordVtnNode node) {
391 checkNotNull(node);
392
393 try {
394 return nodeStore.get(node).value();
395 } catch (NullPointerException e) {
396 log.error("Failed to get state of {}", node.hostname());
397 return null;
398 }
399 }
400
401 /**
402 * Sets a new state for a given cordvtn node.
403 *
404 * @param node cordvtn node
405 * @param newState new node state
406 */
407 private void setNodeState(CordVtnNode node, NodeState newState) {
408 checkNotNull(node);
409
410 log.debug("Changed {} state: {}", node.hostname(), newState.toString());
411
412 nodeStore.put(node, newState);
413 newState.process(this, node);
414 }
415
416 /**
417 * Checks current state of a given cordvtn node and returns it.
418 *
419 * @param node cordvtn node
420 * @return node state
421 */
422 private NodeState checkNodeState(CordVtnNode node) {
423 checkNotNull(node);
424
425 if (checkIntegrationBridge(node) && checkTunnelInterface(node)) {
426 // TODO add physical port add state
427 if (checkPhyInterface(node)) {
428 return NodeState.COMPLETE;
429 } else {
430 return NodeState.INCOMPLETE;
431 }
432 } else if (checkIntegrationBridge(node)) {
433 return NodeState.BRIDGE_CREATED;
434 } else if (getOvsdbConnectionState(node)) {
435 return NodeState.OVSDB_CONNECTED;
436 } else {
437 return NodeState.INIT;
438 }
439 }
440
441 /**
442 * Performs tasks after node initialization.
443 * It disconnects unnecessary OVSDB connection and installs initial flow
444 * rules on the device.
445 *
446 * @param node cordvtn node
447 */
448 private void postInit(CordVtnNode node) {
449 disconnectOvsdb(node);
450
451 ruleInstaller.init(node.intBrId(), node.phyPortName(), node.localIp());
452
453 // add existing hosts to the service
454 deviceService.getPorts(node.intBrId()).stream()
455 .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) &&
456 port.isEnabled())
457 .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port)));
458
459 // remove stale hosts from the service
460 hostService.getHosts().forEach(host -> {
461 Port port = deviceService.getPort(host.location().deviceId(), host.location().port());
462 if (port == null) {
463 cordVtnService.removeServiceVm(getConnectPoint(host));
464 }
465 });
466
467 log.info("Finished init {}", node.hostname());
468 }
469
470 /**
471 * Returns port name.
472 *
473 * @param port port
474 * @return port name
475 */
476 private String getPortName(Port port) {
477 return port.annotations().value("portName");
478 }
479
480 /**
481 * Returns connection state of OVSDB server for a given node.
482 *
483 * @param node cordvtn node
484 * @return true if it is connected, false otherwise
485 */
486 private boolean getOvsdbConnectionState(CordVtnNode node) {
487 checkNotNull(node);
488
489 OvsdbClientService ovsdbClient = getOvsdbClient(node);
490 return deviceService.isAvailable(node.ovsdbId()) &&
491 ovsdbClient != null && ovsdbClient.isConnected();
492 }
493
494 /**
495 * Connects to OVSDB server for a given node.
496 *
497 * @param node cordvtn node
498 */
499 private void connectOvsdb(CordVtnNode node) {
500 checkNotNull(node);
501
502 if (!nodeStore.containsKey(node)) {
503 log.warn("Node {} does not exist", node.hostname());
504 return;
505 }
506
507 if (!getOvsdbConnectionState(node)) {
508 controller.connect(node.ovsdbIp(), node.ovsdbPort());
509 }
510 }
511
512 /**
513 * Disconnects OVSDB server for a given node.
514 *
515 * @param node cordvtn node
516 */
517 private void disconnectOvsdb(CordVtnNode node) {
518 checkNotNull(node);
519
520 if (!nodeStore.containsKey(node)) {
521 log.warn("Node {} does not exist", node.hostname());
522 return;
523 }
524
525 if (getOvsdbConnectionState(node)) {
526 OvsdbClientService ovsdbClient = getOvsdbClient(node);
527 ovsdbClient.disconnect();
528 }
529 }
530
531 /**
532 * Returns OVSDB client for a given node.
533 *
534 * @param node cordvtn node
535 * @return OVSDB client, or null if it fails to get OVSDB client
536 */
537 private OvsdbClientService getOvsdbClient(CordVtnNode node) {
538 checkNotNull(node);
539
540 OvsdbClientService ovsdbClient = controller.getOvsdbClient(
541 new OvsdbNodeId(node.ovsdbIp(), node.ovsdbPort().toInt()));
542 if (ovsdbClient == null) {
543 log.trace("Couldn't find OVSDB client for {}", node.hostname());
544 }
545 return ovsdbClient;
546 }
547
548 /**
549 * Creates an integration bridge for a given node.
550 *
551 * @param node cordvtn node
552 */
553 private void createIntegrationBridge(CordVtnNode node) {
554 if (checkIntegrationBridge(node)) {
555 return;
556 }
557
558 List<ControllerInfo> controllers = new ArrayList<>();
559 Sets.newHashSet(clusterService.getNodes()).stream()
560 .forEach(controller -> {
561 ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
562 controllers.add(ctrlInfo);
563 });
564
565 String dpid = node.intBrId().toString().substring(DPID_BEGIN);
566
567 try {
568 DriverHandler handler = driverService.createHandler(node.ovsdbId());
569 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
570 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
571 } catch (ItemNotFoundException e) {
572 log.warn("Failed to create integration bridge on {}", node.ovsdbId());
573 }
574 }
575
576 /**
577 * Creates tunnel interface to the integration bridge for a given node.
578 *
579 * @param node cordvtn node
580 */
581 private void createTunnelInterface(CordVtnNode node) {
582 if (checkTunnelInterface(node)) {
583 return;
584 }
585
586 DefaultAnnotations.Builder optionBuilder = DefaultAnnotations.builder();
587 for (String key : DEFAULT_TUNNEL_OPTIONS.keySet()) {
588 optionBuilder.set(key, DEFAULT_TUNNEL_OPTIONS.get(key));
589 }
590
591 TunnelDescription description = new DefaultTunnelDescription(
592 null, null, VXLAN, TunnelName.tunnelName(DEFAULT_TUNNEL),
593 optionBuilder.build());
594
595 try {
596 DriverHandler handler = driverService.createHandler(node.ovsdbId());
597 TunnelConfig tunnelConfig = handler.behaviour(TunnelConfig.class);
598 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
599 } catch (ItemNotFoundException e) {
600 log.warn("Failed to create tunnel interface on {}", node.ovsdbId());
601 }
602 }
603
604 /**
605 * Checks if integration bridge exists and available.
606 *
607 * @param node cordvtn node
608 * @return true if the bridge is available, false otherwise
609 */
610 private boolean checkIntegrationBridge(CordVtnNode node) {
611 return (deviceService.getDevice(node.intBrId()) != null
612 && deviceService.isAvailable(node.intBrId()));
613 }
614
615 /**
616 * Checks if tunnel interface exists.
617 *
618 * @param node cordvtn node
619 * @return true if the interface exists, false otherwise
620 */
621 private boolean checkTunnelInterface(CordVtnNode node) {
622 return deviceService.getPorts(node.intBrId())
623 .stream()
624 .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) &&
625 p.isEnabled())
626 .findAny().isPresent();
627 }
628
629 /**
630 * Checks if physical interface exists.
631 *
632 * @param node cordvtn node
633 * @return true if the interface exists, false otherwise
634 */
635 private boolean checkPhyInterface(CordVtnNode node) {
636 return deviceService.getPorts(node.intBrId())
637 .stream()
638 .filter(p -> getPortName(p).contains(node.phyPortName()) &&
639 p.isEnabled())
640 .findAny().isPresent();
641 }
642
643 /**
644 * Returns connect point of a given port.
645 *
646 * @param port port
647 * @return connect point
648 */
649 private ConnectPoint getConnectPoint(Port port) {
650 return new ConnectPoint(port.element().id(), port.number());
651 }
652
653 /**
654 * Returns connect point of a given host.
655 *
656 * @param host host
657 * @return connect point
658 */
659 private ConnectPoint getConnectPoint(Host host) {
660 return new ConnectPoint(host.location().deviceId(), host.location().port());
661 }
662
663 private class OvsdbHandler implements ConnectionHandler<Device> {
664
665 @Override
666 public void connected(Device device) {
667 CordVtnNode node = getNodeByOvsdbId(device.id());
668 if (node != null) {
669 setNodeState(node, checkNodeState(node));
670 } else {
671 log.debug("{} is detected on unregistered node, ignore it.", device.id());
672 }
673 }
674
675 @Override
676 public void disconnected(Device device) {
677 if (!deviceService.isAvailable(device.id())) {
678 adminService.removeDevice(device.id());
679 }
680 }
681 }
682
683 private class BridgeHandler implements ConnectionHandler<Device> {
684
685 @Override
686 public void connected(Device device) {
687 CordVtnNode node = getNodeByBridgeId(device.id());
688 if (node != null) {
689 setNodeState(node, checkNodeState(node));
690 } else {
691 log.debug("{} is detected on unregistered node, ignore it.", device.id());
692 }
693 }
694
695 @Override
696 public void disconnected(Device device) {
697 CordVtnNode node = getNodeByBridgeId(device.id());
698 if (node != null) {
699 log.debug("Integration Bridge is disconnected from {}", node.hostname());
700 setNodeState(node, NodeState.INCOMPLETE);
701 }
702 }
703
704 /**
705 * Handles port added situation.
706 * If the added port is tunnel or physical port, proceed remaining node
707 * initialization. Otherwise, do nothing.
708 *
709 * @param port port
710 */
711 public void portAdded(Port port) {
712 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
713 String portName = getPortName(port);
714
715 if (node == null) {
716 log.debug("{} is added to unregistered node, ignore it.", portName);
717 return;
718 }
719
720 log.debug("Port {} is added to {}", portName, node.hostname());
721
722 if (portName.startsWith(VPORT_PREFIX)) {
723 if (getNodeInitState(node)) {
724 cordVtnService.addServiceVm(node, getConnectPoint(port));
725 } else {
726 log.debug("VM is detected on incomplete node, ignore it.", portName);
727 }
728 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.phyPortName())) {
729 setNodeState(node, checkNodeState(node));
730 }
731 }
732
733 /**
734 * Handles port removed situation.
735 * If the removed port is tunnel or physical port, proceed remaining node
736 * initialization.Others, do nothing.
737 *
738 * @param port port
739 */
740 public void portRemoved(Port port) {
741 CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id());
742 String portName = getPortName(port);
743
744 if (node == null) {
745 return;
746 }
747
748 log.debug("Port {} is removed from {}", portName, node.hostname());
749
750 if (portName.startsWith(VPORT_PREFIX)) {
751 if (getNodeInitState(node)) {
752 cordVtnService.removeServiceVm(getConnectPoint(port));
753 } else {
754 log.debug("VM is vanished from incomplete node, ignore it.", portName);
755 }
756 } else if (portName.contains(DEFAULT_TUNNEL) || portName.equals(node.phyPortName())) {
757 setNodeState(node, NodeState.INCOMPLETE);
758 }
759 }
760 }
761
762 private class InternalDeviceListener implements DeviceListener {
763
764 @Override
765 public void event(DeviceEvent event) {
766
767 Device device = event.subject();
768 ConnectionHandler<Device> handler =
769 (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
770
771 switch (event.type()) {
772 case PORT_ADDED:
773 eventExecutor.submit(() -> bridgeHandler.portAdded(event.port()));
774 break;
775 case PORT_UPDATED:
776 if (!event.port().isEnabled()) {
777 eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port()));
778 }
779 break;
780 case DEVICE_ADDED:
781 case DEVICE_AVAILABILITY_CHANGED:
782 if (deviceService.isAvailable(device.id())) {
783 eventExecutor.submit(() -> handler.connected(device));
784 } else {
785 eventExecutor.submit(() -> handler.disconnected(device));
786 }
787 break;
788 default:
789 break;
790 }
791 }
792 }
793
794 /**
795 * Reads node configuration from config file.
796 */
797 private void readConfiguration() {
798 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
799
800 if (config == null) {
801 log.warn("No configuration found");
802 return;
803 }
804
805 config.cordVtnNodes().forEach(node -> {
806 CordVtnNode cordVtnNode = new CordVtnNode(
807 node.hostname(),
808 node.ovsdbIp(),
809 node.ovsdbPort(),
810 node.bridgeId(),
811 node.phyPortName(),
812 node.localIp());
813
814 addNode(cordVtnNode);
815 });
816 }
817
818 private class InternalConfigListener implements NetworkConfigListener {
819
820 @Override
821 public void event(NetworkConfigEvent event) {
822 if (!event.configClass().equals(CordVtnConfig.class)) {
823 return;
824 }
825
826 switch (event.type()) {
827 case CONFIG_ADDED:
828 log.info("Network configuration added");
829 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
830 break;
831 case CONFIG_UPDATED:
832 log.info("Network configuration updated");
833 eventExecutor.execute(CordVtnNodeManager.this::readConfiguration);
834 break;
835 default:
836 break;
837 }
838 }
839 }
840}