Hyunsun Moon
Committed by Gerrit Code Review

[Goldeneye] CORD-568 Ensure location transparency of node init operation

- Added MapListener for cordvtn node store and made the init process to be
  triggered by map event, so that the leader can do its job regardless of
  the location where node init CLI command happens
- Fixed equals and hashCode override to use all node attributes except for
  the node init state
- Adjusted some log levels

Change-Id: I45688afa60de3516d91132e8a6c49cf90c4dcae4
...@@ -718,7 +718,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro ...@@ -718,7 +718,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
718 .stream() 718 .stream()
719 .forEach(entry -> { 719 .forEach(entry -> {
720 arpProxy.addGateway(entry.getKey(), entry.getValue()); 720 arpProxy.addGateway(entry.getKey(), entry.getValue());
721 - log.debug("Added public gateway IP {}, MAC {}", 721 + log.info("Added public gateway IP {}, MAC {}",
722 entry.getKey().toString(), entry.getValue().toString()); 722 entry.getKey().toString(), entry.getValue().toString());
723 }); 723 });
724 // TODO notice gateway MAC change to VMs holds this gateway IP 724 // TODO notice gateway MAC change to VMs holds this gateway IP
......
...@@ -97,7 +97,6 @@ public class CordVtnConfig extends Config<ApplicationId> { ...@@ -97,7 +97,6 @@ public class CordVtnConfig extends Config<ApplicationId> {
97 getConfig(jsonNode, DATA_PLANE_INTF), 97 getConfig(jsonNode, DATA_PLANE_INTF),
98 CordVtnNodeState.noState()); 98 CordVtnNodeState.noState());
99 99
100 - log.info("Successfully read {} from the config", hostname);
101 nodes.add(newNode); 100 nodes.add(newNode);
102 } catch (IllegalArgumentException | NullPointerException e) { 101 } catch (IllegalArgumentException | NullPointerException e) {
103 log.error("{}", e.toString()); 102 log.error("{}", e.toString());
...@@ -121,7 +120,6 @@ public class CordVtnConfig extends Config<ApplicationId> { ...@@ -121,7 +120,6 @@ public class CordVtnConfig extends Config<ApplicationId> {
121 log.error("{} is not configured", path); 120 log.error("{} is not configured", path);
122 return null; 121 return null;
123 } else { 122 } else {
124 - log.debug("{} : {}", path, jsonNode.asText());
125 return jsonNode.asText(); 123 return jsonNode.asText();
126 } 124 }
127 } 125 }
......
...@@ -181,11 +181,16 @@ public final class CordVtnNode { ...@@ -181,11 +181,16 @@ public final class CordVtnNode {
181 return true; 181 return true;
182 } 182 }
183 183
184 - // hostname here is a network hostname and it is intended to be
185 - // unique throughout the service.
186 if (obj instanceof CordVtnNode) { 184 if (obj instanceof CordVtnNode) {
187 CordVtnNode that = (CordVtnNode) obj; 185 CordVtnNode that = (CordVtnNode) obj;
188 - if (Objects.equals(hostname, that.hostname)) { 186 + if (Objects.equals(hostname, that.hostname) &&
187 + Objects.equals(hostMgmtIp, that.hostMgmtIp) &&
188 + Objects.equals(localMgmtIp, that.localMgmtIp) &&
189 + Objects.equals(dpIp, that.dpIp) &&
190 + Objects.equals(ovsdbPort, that.ovsdbPort) &&
191 + Objects.equals(sshInfo, that.sshInfo) &&
192 + Objects.equals(bridgeId, that.bridgeId) &&
193 + Objects.equals(dpIntf, that.dpIntf)) {
189 return true; 194 return true;
190 } 195 }
191 } 196 }
...@@ -194,7 +199,8 @@ public final class CordVtnNode { ...@@ -194,7 +199,8 @@ public final class CordVtnNode {
194 199
195 @Override 200 @Override
196 public int hashCode() { 201 public int hashCode() {
197 - return Objects.hash(hostname); 202 + return Objects.hash(hostname, hostMgmtIp, localMgmtIp, dpIp,
203 + ovsdbPort, sshInfo, bridgeId, dpIntf);
198 } 204 }
199 205
200 @Override 206 @Override
......
...@@ -62,6 +62,8 @@ import org.onosproject.ovsdb.controller.OvsdbController; ...@@ -62,6 +62,8 @@ import org.onosproject.ovsdb.controller.OvsdbController;
62 import org.onosproject.ovsdb.controller.OvsdbNodeId; 62 import org.onosproject.ovsdb.controller.OvsdbNodeId;
63 import org.onosproject.store.serializers.KryoNamespaces; 63 import org.onosproject.store.serializers.KryoNamespaces;
64 import org.onosproject.store.service.ConsistentMap; 64 import org.onosproject.store.service.ConsistentMap;
65 +import org.onosproject.store.service.MapEvent;
66 +import org.onosproject.store.service.MapEventListener;
65 import org.onosproject.store.service.Serializer; 67 import org.onosproject.store.service.Serializer;
66 import org.onosproject.store.service.StorageService; 68 import org.onosproject.store.service.StorageService;
67 import org.onosproject.store.service.Versioned; 69 import org.onosproject.store.service.Versioned;
...@@ -164,6 +166,7 @@ public class CordVtnNodeManager { ...@@ -164,6 +166,7 @@ public class CordVtnNodeManager {
164 166
165 private final NetworkConfigListener configListener = new InternalConfigListener(); 167 private final NetworkConfigListener configListener = new InternalConfigListener();
166 private final DeviceListener deviceListener = new InternalDeviceListener(); 168 private final DeviceListener deviceListener = new InternalDeviceListener();
169 + private final MapEventListener<String, CordVtnNode> nodeStoreListener = new InternalMapListener();
167 170
168 private final OvsdbHandler ovsdbHandler = new OvsdbHandler(); 171 private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
169 private final BridgeHandler bridgeHandler = new BridgeHandler(); 172 private final BridgeHandler bridgeHandler = new BridgeHandler();
...@@ -236,6 +239,7 @@ public class CordVtnNodeManager { ...@@ -236,6 +239,7 @@ public class CordVtnNodeManager {
236 configRegistry, 239 configRegistry,
237 DEFAULT_TUNNEL); 240 DEFAULT_TUNNEL);
238 241
242 + nodeStore.addListener(nodeStoreListener);
239 deviceService.addListener(deviceListener); 243 deviceService.addListener(deviceListener);
240 configService.addListener(configListener); 244 configService.addListener(configListener);
241 } 245 }
...@@ -245,22 +249,21 @@ public class CordVtnNodeManager { ...@@ -245,22 +249,21 @@ public class CordVtnNodeManager {
245 configService.removeListener(configListener); 249 configService.removeListener(configListener);
246 deviceService.removeListener(deviceListener); 250 deviceService.removeListener(deviceListener);
247 251
248 - eventExecutor.shutdown(); 252 + nodeStore.removeListener(nodeStoreListener);
249 nodeStore.clear(); 253 nodeStore.clear();
254 +
250 leadershipService.withdraw(appId.name()); 255 leadershipService.withdraw(appId.name());
256 + eventExecutor.shutdown();
251 } 257 }
252 258
253 /** 259 /**
254 - * Adds a new node to the service. 260 + * Adds or updates a new node to the service.
255 * 261 *
256 * @param node cordvtn node 262 * @param node cordvtn node
257 */ 263 */
258 - public void addNode(CordVtnNode node) { 264 + public void addOrUpdateNode(CordVtnNode node) {
259 checkNotNull(node); 265 checkNotNull(node);
260 -
261 - // allow update node attributes
262 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node))); 266 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, getNodeState(node)));
263 - initNode(node);
264 } 267 }
265 268
266 /** 269 /**
...@@ -283,23 +286,12 @@ public class CordVtnNodeManager { ...@@ -283,23 +286,12 @@ public class CordVtnNodeManager {
283 * 286 *
284 * @param node cordvtn node 287 * @param node cordvtn node
285 */ 288 */
286 - public void initNode(CordVtnNode node) { 289 + private void initNode(CordVtnNode node) {
287 checkNotNull(node); 290 checkNotNull(node);
288 291
289 - if (!nodeStore.containsKey(node.hostname())) { 292 + NodeState state = (NodeState) node.state();
290 - log.warn("Node {} does not exist, add node first", node.hostname()); 293 + log.debug("Processing node: {} state: {}", node.hostname(), state);
291 - return;
292 - }
293 -
294 - NodeId leaderNodeId = leadershipService.getLeader(appId.name());
295 - log.debug("Node init requested, local: {} leader: {}", localNodeId, leaderNodeId);
296 - if (!Objects.equals(localNodeId, leaderNodeId)) {
297 - // only the leader performs node init
298 - return;
299 - }
300 294
301 - NodeState state = getNodeState(node);
302 - log.debug("Init node: {} state: {}", node.hostname(), state.toString());
303 state.process(this, node); 295 state.process(this, node);
304 } 296 }
305 297
...@@ -432,9 +424,8 @@ public class CordVtnNodeManager { ...@@ -432,9 +424,8 @@ public class CordVtnNodeManager {
432 private void setNodeState(CordVtnNode node, NodeState newState) { 424 private void setNodeState(CordVtnNode node, NodeState newState) {
433 checkNotNull(node); 425 checkNotNull(node);
434 426
435 - log.debug("Changed {} state: {}", node.hostname(), newState.toString()); 427 + log.debug("Changed {} state: {}", node.hostname(), newState);
436 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState)); 428 nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
437 - newState.process(this, node);
438 } 429 }
439 430
440 /** 431 /**
...@@ -812,7 +803,7 @@ public class CordVtnNodeManager { ...@@ -812,7 +803,7 @@ public class CordVtnNodeManager {
812 return; 803 return;
813 } 804 }
814 805
815 - log.debug("Port {} is added to {}", portName, node.hostname()); 806 + log.info("Port {} is added to {}", portName, node.hostname());
816 807
817 if (portName.startsWith(VPORT_PREFIX)) { 808 if (portName.startsWith(VPORT_PREFIX)) {
818 if (isNodeStateComplete(node)) { 809 if (isNodeStateComplete(node)) {
...@@ -840,7 +831,7 @@ public class CordVtnNodeManager { ...@@ -840,7 +831,7 @@ public class CordVtnNodeManager {
840 return; 831 return;
841 } 832 }
842 833
843 - log.debug("Port {} is removed from {}", portName, node.hostname()); 834 + log.info("Port {} is removed from {}", portName, node.hostname());
844 835
845 if (portName.startsWith(VPORT_PREFIX)) { 836 if (portName.startsWith(VPORT_PREFIX)) {
846 if (isNodeStateComplete(node)) { 837 if (isNodeStateComplete(node)) {
...@@ -861,7 +852,7 @@ public class CordVtnNodeManager { ...@@ -861,7 +852,7 @@ public class CordVtnNodeManager {
861 852
862 NodeId leaderNodeId = leadershipService.getLeader(appId.name()); 853 NodeId leaderNodeId = leadershipService.getLeader(appId.name());
863 if (!Objects.equals(localNodeId, leaderNodeId)) { 854 if (!Objects.equals(localNodeId, leaderNodeId)) {
864 - // only the leader processes events 855 + // do not allow to proceed without leadership
865 return; 856 return;
866 } 857 }
867 858
...@@ -871,19 +862,19 @@ public class CordVtnNodeManager { ...@@ -871,19 +862,19 @@ public class CordVtnNodeManager {
871 862
872 switch (event.type()) { 863 switch (event.type()) {
873 case PORT_ADDED: 864 case PORT_ADDED:
874 - eventExecutor.submit(() -> bridgeHandler.portAdded(event.port())); 865 + eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
875 break; 866 break;
876 case PORT_UPDATED: 867 case PORT_UPDATED:
877 if (!event.port().isEnabled()) { 868 if (!event.port().isEnabled()) {
878 - eventExecutor.submit(() -> bridgeHandler.portRemoved(event.port())); 869 + eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
879 } 870 }
880 break; 871 break;
881 case DEVICE_ADDED: 872 case DEVICE_ADDED:
882 case DEVICE_AVAILABILITY_CHANGED: 873 case DEVICE_AVAILABILITY_CHANGED:
883 if (deviceService.isAvailable(device.id())) { 874 if (deviceService.isAvailable(device.id())) {
884 - eventExecutor.submit(() -> handler.connected(device)); 875 + eventExecutor.execute(() -> handler.connected(device));
885 } else { 876 } else {
886 - eventExecutor.submit(() -> handler.disconnected(device)); 877 + eventExecutor.execute(() -> handler.disconnected(device));
887 } 878 }
888 break; 879 break;
889 default: 880 default:
...@@ -902,14 +893,19 @@ public class CordVtnNodeManager { ...@@ -902,14 +893,19 @@ public class CordVtnNodeManager {
902 return; 893 return;
903 } 894 }
904 895
905 - config.cordVtnNodes().forEach(this::addNode); 896 + config.cordVtnNodes().forEach(this::addOrUpdateNode);
906 - // TODO remove nodes if needed
907 } 897 }
908 898
909 private class InternalConfigListener implements NetworkConfigListener { 899 private class InternalConfigListener implements NetworkConfigListener {
910 900
911 @Override 901 @Override
912 public void event(NetworkConfigEvent event) { 902 public void event(NetworkConfigEvent event) {
903 + NodeId leaderNodeId = leadershipService.getLeader(appId.name());
904 + if (!Objects.equals(localNodeId, leaderNodeId)) {
905 + // do not allow to proceed without leadership
906 + return;
907 + }
908 +
913 if (!event.configClass().equals(CordVtnConfig.class)) { 909 if (!event.configClass().equals(CordVtnConfig.class)) {
914 return; 910 return;
915 } 911 }
...@@ -924,4 +920,46 @@ public class CordVtnNodeManager { ...@@ -924,4 +920,46 @@ public class CordVtnNodeManager {
924 } 920 }
925 } 921 }
926 } 922 }
923 +
924 + private class InternalMapListener implements MapEventListener<String, CordVtnNode> {
925 +
926 + @Override
927 + public void event(MapEvent<String, CordVtnNode> event) {
928 + NodeId leaderNodeId = leadershipService.getLeader(appId.name());
929 + if (!Objects.equals(localNodeId, leaderNodeId)) {
930 + // do not allow to proceed without leadership
931 + return;
932 + }
933 +
934 + CordVtnNode oldNode;
935 + CordVtnNode newNode;
936 +
937 + switch (event.type()) {
938 + case UPDATE:
939 + oldNode = event.oldValue().value();
940 + newNode = event.newValue().value();
941 +
942 + if (!newNode.equals(oldNode)) {
943 + log.info("{} has been updated", newNode.hostname());
944 + log.debug("New node: {}", newNode);
945 + }
946 + // perform init procedure based on current state on any updates,
947 + // insert, or even if the node is the same for robustness since
948 + // it's no harm to run the init procedure multiple times
949 + eventExecutor.execute(() -> initNode(newNode));
950 + break;
951 + case INSERT:
952 + newNode = event.newValue().value();
953 + log.info("Added {}", newNode.hostname());
954 + eventExecutor.execute(() -> initNode(newNode));
955 + break;
956 + case REMOVE:
957 + oldNode = event.oldValue().value();
958 + log.info("{} is removed", oldNode.hostname());
959 + break;
960 + default:
961 + break;
962 + }
963 + }
964 + }
927 } 965 }
......
...@@ -51,7 +51,7 @@ public class CordVtnNodeInitCommand extends AbstractShellCommand { ...@@ -51,7 +51,7 @@ public class CordVtnNodeInitCommand extends AbstractShellCommand {
51 continue; 51 continue;
52 } 52 }
53 53
54 - nodeManager.initNode(node); 54 + nodeManager.addOrUpdateNode(node);
55 } 55 }
56 } 56 }
57 } 57 }
......