From 401efea70ea57068edb5951bcb1f32ca8ec22b23 Mon Sep 17 00:00:00 2001 From: Pramila Singh Date: Mon, 8 Jul 2013 16:48:04 -0700 Subject: [PATCH] HA cache sync for switch manager Change-Id: I00ab53d62f2c7622311ee07fd934c9ce8c000064 Signed-off-by: Pramila Singh --- .../test/internal/SimpleClient.java | 5 +- .../implementation/internal/Activator.java | 4 +- .../controller/switchmanager/SpanConfig.java | 2 +- .../controller/switchmanager/Subnet.java | 22 +- .../switchmanager/SubnetConfig.java | 21 +- .../switchmanager/SwitchConfig.java | 4 +- .../internal/SwitchManagerImpl.java | 482 ++++++++++++------ .../internal/SwitchManagerImplTest.java | 4 +- .../internal/SwitchManagerIT.java | 82 ++- .../controller/devices/web/Devices.java | 15 +- 10 files changed, 425 insertions(+), 216 deletions(-) diff --git a/opendaylight/clustering/test/src/main/java/org/opendaylight/controller/clustering/test/internal/SimpleClient.java b/opendaylight/clustering/test/src/main/java/org/opendaylight/controller/clustering/test/internal/SimpleClient.java index b6ab95db33..0a0a9d8828 100644 --- a/opendaylight/clustering/test/src/main/java/org/opendaylight/controller/clustering/test/internal/SimpleClient.java +++ b/opendaylight/clustering/test/src/main/java/org/opendaylight/controller/clustering/test/internal/SimpleClient.java @@ -273,7 +273,7 @@ public class SimpleClient implements CommandProvider { ci.println("\nNo Clustering services available"); return; } - String containerName = ci.nextArgument(); + String containerName = ci.nextArgument().toLowerCase(); if (containerName == null) { ci.println("containerName not supplied"); return; @@ -372,7 +372,7 @@ public class SimpleClient implements CommandProvider { public void _dumper(CommandInterpreter ci) { ConcurrentMap c; - String containerName = ci.nextArgument(); + String containerName = ci.nextArgument().toLowerCase(); if (containerName == null) { ci.println("containerName not supplied"); return; @@ -490,6 +490,7 @@ public class SimpleClient implements CommandProvider { } class DoListenRoleChanged implements IListenRoleChange { + @Override public void newActiveAvailable() { logger.debug("New Active is available"); } diff --git a/opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/Activator.java b/opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/Activator.java index 6cab6b9b4f..1a9675a0d4 100644 --- a/opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/Activator.java +++ b/opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/Activator.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.sal.implementation.internal; +import org.apache.felix.dm.Component; import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase; import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener; import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService; @@ -29,7 +30,6 @@ import org.opendaylight.controller.sal.topology.IPluginOutTopologyService; import org.opendaylight.controller.sal.topology.ITopologyService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.felix.dm.Component; public class Activator extends ComponentActivatorAbstractBase { protected static final Logger logger = LoggerFactory @@ -125,7 +125,7 @@ public class Activator extends ComponentActivatorAbstractBase { c.add(createContainerServiceDependency(containerName) .setService(IPluginInInventoryService.class) .setCallbacks("setPluginService", "unsetPluginService") - .setRequired(true)); + .setRequired(false)); } if (imp.equals(FlowProgrammerService.class)) { diff --git a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SpanConfig.java b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SpanConfig.java index 83cb8b1cc9..522f45946d 100644 --- a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SpanConfig.java +++ b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SpanConfig.java @@ -153,6 +153,6 @@ public class SpanConfig implements Serializable { @Override public String toString() { - return ("Span Config [nodeId=" + nodeId + " spanPort=" + spanPort + "]"); + return ("SpanConfig [nodeId=" + nodeId + ", spanPort=" + spanPort + "]"); } } diff --git a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java index e15303581f..6a8eea2d0c 100644 --- a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java +++ b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java @@ -21,14 +21,14 @@ import org.opendaylight.controller.sal.core.NodeConnector; * The class describes subnet information including L3 address, vlan and set of * ports associated with the subnet. */ -public class Subnet implements Serializable { +public class Subnet implements Cloneable, Serializable { private static final long serialVersionUID = 1L; // Key fields private InetAddress networkAddress; private short subnetMaskLength; // Property fields private short vlan; - private Set nodeConnectors; + private final Set nodeConnectors; public Subnet(InetAddress ip, short maskLen, short vlan) { this.networkAddress = ip; @@ -43,6 +43,13 @@ public class Subnet implements Serializable { nodeConnectors = conf.getSubnetNodeConnectors(); } + public Subnet(Subnet subnet) { + networkAddress = subnet.networkAddress; + subnetMaskLength = subnet.subnetMaskLength; + vlan = subnet.vlan; + nodeConnectors = new HashSet(subnet.nodeConnectors); + } + /** * Add NodeConnectors to a subnet * @@ -220,7 +227,7 @@ public class Subnet implements Serializable { public String toString() { return ("Subnet [networkAddress=" + networkAddress.getHostAddress() + "/" + subnetMaskLength - + ((vlan == 0) ? "" : (" vlan=" + vlan)) + " " + + ((vlan == 0) ? "" : (", vlan=" + vlan)) + ", " + ((isFlatLayer2()) ? "{[*, *]}" : nodeConnectors.toString()) + "]"); } @@ -246,4 +253,13 @@ public class Subnet implements Serializable { } return true; } + + /** + * Implement clonable interface + */ + @Override + public Subnet clone() { + return new Subnet(this); + } + } diff --git a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SubnetConfig.java b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SubnetConfig.java index 895f117321..52b5f5255d 100644 --- a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SubnetConfig.java +++ b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SubnetConfig.java @@ -34,7 +34,7 @@ import org.opendaylight.controller.sal.utils.NodeConnectorCreator; */ @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) -public class SubnetConfig implements Serializable { +public class SubnetConfig implements Cloneable, Serializable { //static fields are by default excluded by Gson parser private static final long serialVersionUID = 1L; private static final String prettyFields[] = { GUIField.NAME.toString(), @@ -60,6 +60,12 @@ public class SubnetConfig implements Serializable { nodePorts = sp; } + public SubnetConfig(SubnetConfig subnetConfig) { + name = subnetConfig.name; + subnet = subnetConfig.subnet; + nodePorts = new ArrayList(subnetConfig.nodePorts); + } + public String getName() { return name; } @@ -233,7 +239,16 @@ public class SubnetConfig implements Serializable { @Override public String toString() { - return ("Subnet Config [Description=" + name + " Subnet=" + subnet - + " NodeConnectors=" + nodePorts + "]"); + return ("SubnetConfig [Description=" + name + ", Subnet=" + subnet + + ", NodeConnectors=" + nodePorts + "]"); + } + + /** + * Implement clonable interface + */ + @Override + public SubnetConfig clone() { + return new SubnetConfig(this); } + } diff --git a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SwitchConfig.java b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SwitchConfig.java index 61b2f0a3a8..c595c43e5b 100644 --- a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SwitchConfig.java +++ b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/SwitchConfig.java @@ -100,7 +100,7 @@ public class SwitchConfig implements Serializable { @Override public String toString() { - return ("Switch Config [Node=" + nodeId + " Description=" + description + - " Tier=" + tier + " Mode=" + mode + "]"); + return ("SwitchConfig [Node=" + nodeId + ", Description=" + description + + ", Tier=" + tier + ", Mode=" + mode + "]"); } } diff --git a/opendaylight/switchmanager/implementation/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImpl.java b/opendaylight/switchmanager/implementation/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImpl.java index d32f98650b..8c95c46d72 100644 --- a/opendaylight/switchmanager/implementation/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImpl.java +++ b/opendaylight/switchmanager/implementation/src/main/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImpl.java @@ -40,6 +40,7 @@ import org.opendaylight.controller.clustering.services.IClusterServices; import org.opendaylight.controller.configuration.IConfigurationContainerAware; import org.opendaylight.controller.sal.core.Bandwidth; import org.opendaylight.controller.sal.core.Config; +import org.opendaylight.controller.sal.core.ConstructionException; import org.opendaylight.controller.sal.core.Description; import org.opendaylight.controller.sal.core.MacAddress; import org.opendaylight.controller.sal.core.Name; @@ -95,7 +96,7 @@ CommandProvider { private final List spanNodeConnectors = new CopyOnWriteArrayList(); private ConcurrentMap subnets; // set of Subnets keyed by the InetAddress private ConcurrentMap subnetsConfigList; - private ConcurrentMap spanConfigList; + private ConcurrentMap spanConfigList; private ConcurrentMap nodeConfigList; // manually configured parameters for the node like name and tier private ConcurrentMap configSaveEvent; private ConcurrentMap> nodeProps; // properties are maintained in global container only @@ -114,6 +115,7 @@ CommandProvider { private IClusterContainerServices clusterContainerService = null; private String containerName = null; private boolean isDefaultContainer = true; + private static final int REPLACE_RETRY = 1; public enum ReasonCode { SUCCESS("Success"), FAILURE("Failure"), INVALID_CONF( @@ -206,6 +208,7 @@ CommandProvider { @SuppressWarnings("deprecation") private void allocateCaches() { if (this.clusterContainerService == null) { + this.nonClusterObjectCreate(); log.warn("un-initialized clusterContainerService, can't create cache"); return; } @@ -251,7 +254,7 @@ CommandProvider { log.error("\nFailed to get cache for subnetsConfigList"); } - spanConfigList = (ConcurrentMap) clusterContainerService + spanConfigList = (ConcurrentMap) clusterContainerService .getCache("switchmanager.spanConfigList"); if (spanConfigList == null) { log.error("\nFailed to get cache for spanConfigList"); @@ -294,9 +297,9 @@ CommandProvider { } } - void nonClusterObjectCreate() { + private void nonClusterObjectCreate() { subnetsConfigList = new ConcurrentHashMap(); - spanConfigList = new ConcurrentHashMap(); + spanConfigList = new ConcurrentHashMap(); nodeConfigList = new ConcurrentHashMap(); subnets = new ConcurrentHashMap(); configSaveEvent = new ConcurrentHashMap(); @@ -305,26 +308,6 @@ CommandProvider { nodeConnectorNames = new ConcurrentHashMap>(); } - @SuppressWarnings("deprecation") - private void destroyCaches(String container) { - if (this.clusterContainerService == null) { - log.info("un-initialized clusterContainerService, can't create cache"); - return; - } - - clusterContainerService.destroyCache("switchmanager.subnetsConfigList"); - clusterContainerService.destroyCache("switchmanager.spanConfigList"); - clusterContainerService.destroyCache("switchmanager.nodeConfigList"); - clusterContainerService.destroyCache("switchmanager.subnets"); - clusterContainerService.destroyCache("switchmanager.configSaveEvent"); - clusterContainerService.destroyCache("switchmanager.nodeProps"); - clusterContainerService - .destroyCache("switchmanager.nodeConnectorProps"); - clusterContainerService - .destroyCache("switchmanager.nodeConnectorNames"); - nonClusterObjectCreate(); - } - @Override public List getSubnetsConfigList() { return new ArrayList(subnetsConfigList.values()); @@ -390,19 +373,26 @@ CommandProvider { return swList; } - private void updateConfig(SubnetConfig conf, boolean add) { + private Status updateConfig(SubnetConfig conf, boolean add) { if (add) { - subnetsConfigList.put(conf.getName(), conf); + if(subnetsConfigList.putIfAbsent(conf.getName(), conf) != null) { + String msg = "Cluster conflict: Subnet with name " + conf.getName() + "already exists."; + return new Status(StatusCode.CONFLICT, msg); + } } else { subnetsConfigList.remove(conf.getName()); } + return new Status(StatusCode.SUCCESS); } - private void updateDatabase(SubnetConfig conf, boolean add) { - Subnet subnet = subnets.get(conf.getIPnum()); + private Status updateDatabase(SubnetConfig conf, boolean add) { if (add) { - if (subnet == null) { + Subnet subnetCurr = subnets.get(conf.getIPnum()); + Subnet subnet; + if (subnetCurr == null) { subnet = new Subnet(conf); + } else { + subnet = subnetCurr.clone(); } // In case of API3 call we may receive the ports along with the // subnet creation @@ -410,29 +400,38 @@ CommandProvider { Set sp = conf.getSubnetNodeConnectors(); subnet.addNodeConnectors(sp); } - subnets.put(conf.getIPnum(), subnet); - } else { // This is the deletion of the whole subnet - if (subnet == null) { - return; + boolean result = false; + if(subnetCurr == null) { + if(subnets.putIfAbsent(conf.getIPnum(), subnet) == null) { + result = true; + } + } else { + result = subnets.replace(conf.getIPnum(), subnetCurr, subnet); + } + if(!result) { + String msg = "Cluster conflict: Conflict while adding the subnet " + conf.getIPnum(); + return new Status(StatusCode.CONFLICT, msg); } + } else { // This is the deletion of the whole subnet subnets.remove(conf.getIPnum()); } + return new Status(StatusCode.SUCCESS); } private Status semanticCheck(SubnetConfig conf) { Subnet newSubnet = new Subnet(conf); Set IPs = subnets.keySet(); if (IPs == null) { - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } for (InetAddress i : IPs) { Subnet existingSubnet = subnets.get(i); if ((existingSubnet != null) && !existingSubnet.isMutualExclusive(newSubnet)) { - return new Status(StatusCode.CONFLICT, null); + return new Status(StatusCode.CONFLICT); } } - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } private Status addRemoveSubnet(SubnetConfig conf, boolean add) { @@ -455,13 +454,19 @@ CommandProvider { return rc; } } - // Update Configuration - updateConfig(conf, add); // Update Database - updateDatabase(conf, add); + Status rc = updateDatabase(conf, add); + + if (rc.isSuccess()) { + // Update Configuration + rc = updateConfig(conf, add); + if(!rc.isSuccess()) { + updateDatabase(conf, (!add)); + } + } - return new Status(StatusCode.SUCCESS, null); + return rc; } /** @@ -488,42 +493,77 @@ CommandProvider { @Override public Status addPortsToSubnet(String name, String switchPorts) { - // Update Configuration - SubnetConfig conf = subnetsConfigList.get(name); - if (conf == null) { + SubnetConfig confCurr = subnetsConfigList.get(name); + if (confCurr == null) { return new Status(StatusCode.NOTFOUND, "Subnet does not exist"); } - if (!conf.isValidSwitchPort(switchPorts)) { + if (!confCurr.isValidSwitchPort(switchPorts)) { return new Status(StatusCode.BADREQUEST, "Invalid switchports"); } - conf.addNodeConnectors(switchPorts); - subnetsConfigList.put(name, conf); + Subnet subCurr = subnets.get(confCurr.getIPnum()); + if (subCurr == null) { + log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPnum()); + return new Status(StatusCode.NOTFOUND, "Subnet does not exist"); + } // Update Database - Subnet sub = subnets.get(conf.getIPnum()); - Set sp = conf.getNodeConnectors(switchPorts); + Subnet sub = subCurr.clone(); + Set sp = confCurr.getNodeConnectors(switchPorts); sub.addNodeConnectors(sp); - subnets.put(conf.getIPnum(), sub); - return new Status(StatusCode.SUCCESS, null); + boolean subnetsReplace = subnets.replace(confCurr.getIPnum(), subCurr, sub); + if (!subnetsReplace) { + String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name; + return new Status(StatusCode.CONFLICT, msg); + } + + // Update Configuration + SubnetConfig conf = confCurr.clone(); + conf.addNodeConnectors(switchPorts); + boolean result = subnetsConfigList.replace(name, confCurr, conf); + if (!result) { + // TODO: recovery using Transactionality + String msg = "Cluster conflict: Conflict while adding ports to the subnet " + name; + return new Status(StatusCode.CONFLICT, msg); + } + + return new Status(StatusCode.SUCCESS); } @Override public Status removePortsFromSubnet(String name, String switchPorts) { - // Update Configuration - SubnetConfig conf = subnetsConfigList.get(name); - if (conf == null) { + SubnetConfig confCurr = subnetsConfigList.get(name); + if (confCurr == null) { + return new Status(StatusCode.NOTFOUND, "Subnet does not exist"); + } + + Subnet subCurr = subnets.get(confCurr.getIPnum()); + if (subCurr == null) { + log.debug("Cluster conflict: Subnet entry {} is not present in the subnets cache.", confCurr.getIPnum()); return new Status(StatusCode.NOTFOUND, "Subnet does not exist"); } - conf.removeNodeConnectors(switchPorts); - subnetsConfigList.put(name, conf); // Update Database - Subnet sub = subnets.get(conf.getIPnum()); - Set sp = conf.getNodeConnectors(switchPorts); + Subnet sub = subCurr.clone(); + Set sp = confCurr.getNodeConnectors(switchPorts); sub.deleteNodeConnectors(sp); - subnets.put(conf.getIPnum(), sub); - return new Status(StatusCode.SUCCESS, null); + boolean subnetsReplace = subnets.replace(confCurr.getIPnum(), subCurr, sub); + if (!subnetsReplace) { + String msg = "Cluster conflict: Conflict while removing ports from the subnet " + name; + return new Status(StatusCode.CONFLICT, msg); + } + + // Update Configuration + SubnetConfig conf = confCurr.clone(); + conf.removeNodeConnectors(switchPorts); + boolean result = subnetsConfigList.replace(name, confCurr, conf); + if (!result) { + // TODO: recovery using Transactionality + String msg = "Cluster conflict: Conflict while removing ports from " + conf; + return new Status(StatusCode.CONFLICT, msg); + } + + return new Status(StatusCode.SUCCESS); } public String getContainerName() { @@ -606,31 +646,51 @@ CommandProvider { return; } + SwitchConfig sc = nodeConfigList.get(cfgObject.getNodeId()); + if (sc == null) { + if (nodeConfigList.putIfAbsent(cfgObject.getNodeId(), cfgObject) != null) { + return; + } + } else { + if (!nodeConfigList.replace(cfgObject.getNodeId(), sc, cfgObject)) { + return; + } + } + boolean modeChange = false; - SwitchConfig sc = nodeConfigList.get(cfgObject.getNodeId()); if ((sc == null) || !cfgObject.getMode().equals(sc.getMode())) { modeChange = true; } - nodeConfigList.put(cfgObject.getNodeId(), cfgObject); try { String nodeId = cfgObject.getNodeId(); Node node = Node.fromString(nodeId); - Map propMap; - if (nodeProps.get(node) != null) { - propMap = nodeProps.get(node); - } else { - propMap = new HashMap(); + Map propMapCurr = nodeProps.get(node); + Map propMap = new HashMap(); + if (propMapCurr != null) { + for (String s : propMapCurr.keySet()) { + propMap.put(s, propMapCurr.get(s).clone()); + } } Property desc = new Description(cfgObject.getNodeDescription()); propMap.put(desc.getName(), desc); Property tier = new Tier(Integer.parseInt(cfgObject.getTier())); propMap.put(tier.getName(), tier); - addNodeProps(node, propMap); - log.info("Set Node {}'s Mode to {}", nodeId, - cfgObject.getMode()); + if (propMapCurr == null) { + if (nodeProps.putIfAbsent(node, propMap) != null) { + // TODO rollback using Transactionality + return; + } + } else { + if (!nodeProps.replace(node, propMapCurr, propMap)) { + // TODO rollback using Transactionality + return; + } + } + + log.info("Set Node {}'s Mode to {}", nodeId, cfgObject.getMode()); if (modeChange) { notifyModeChange(node, cfgObject.isProactive()); @@ -653,7 +713,7 @@ CommandProvider { retS = objWriter.write(new ConcurrentHashMap( subnetsConfigList), subnetFileName); - retP = objWriter.write(new ConcurrentHashMap( + retP = objWriter.write(new ConcurrentHashMap( spanConfigList), spanFileName); retS = objWriter.write(new ConcurrentHashMap( nodeConfigList), switchConfigFileName); @@ -684,17 +744,17 @@ CommandProvider { } // Presence check - if (spanConfigList.containsKey(conf.hashCode())) { + if (spanConfigList.containsKey(conf)) { return new Status(StatusCode.CONFLICT, "Same span config exists"); } - // Update database and notify clients - addSpanPorts(conf.getNode(), conf.getPortArrayList()); - // Update configuration - spanConfigList.put(conf.hashCode(), conf); + if (spanConfigList.putIfAbsent(conf, conf) == null) { + // Update database and notify clients + addSpanPorts(conf.getNode(), conf.getPortArrayList()); + } - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } @Override @@ -702,9 +762,9 @@ CommandProvider { removeSpanPorts(conf.getNode(), conf.getPortArrayList()); // Update configuration - spanConfigList.remove(conf.hashCode()); + spanConfigList.remove(conf); - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } @Override @@ -739,11 +799,12 @@ CommandProvider { return; } - Map propMap; - if (nodeProps.get(node) != null) { - propMap = nodeProps.get(node); - } else { - propMap = new HashMap(); + Map propMapCurr = nodeProps.get(node); + Map propMap = new HashMap(); + if (propMapCurr != null) { + for (String s : propMapCurr.keySet()) { + propMap.put(s, propMapCurr.get(s).clone()); + } } // copy node properties from plugin @@ -759,8 +820,7 @@ CommandProvider { String nodeId = node.toString(); for (SwitchConfig conf : nodeConfigList.values()) { if (conf.getNodeId().equals(nodeId)) { - Property description = new Description( - conf.getNodeDescription()); + Property description = new Description(conf.getNodeDescription()); propMap.put(description.getName(), description); Property tier = new Tier(Integer.parseInt(conf.getTier())); propMap.put(tier.getName(), tier); @@ -769,7 +829,22 @@ CommandProvider { } } } - addNodeProps(node, propMap); + + boolean result = false; + if (propMapCurr == null) { + if (nodeProps.putIfAbsent(node, propMap) == null) { + result = true; + } + } else { + result = nodeProps.replace(node, propMapCurr, propMap); + } + + if (!result) { + log.debug( + "Cluster conflict: Conflict while adding the node properties. Node: {} Properties: {}", + node.getID(), props); + addNodeProps(node, propMap); + } // check if span ports are configed addSpanPorts(node); @@ -804,11 +879,12 @@ CommandProvider { return; } - Map propMap; - if (nodeProps.get(node) != null) { - propMap = nodeProps.get(node); - } else { - propMap = new HashMap(); + Map propMapCurr = nodeProps.get(node); + Map propMap = new HashMap(); + if (propMapCurr != null) { + for (String s : propMapCurr.keySet()) { + propMap.put(s, propMapCurr.get(s).clone()); + } } // copy node properties from plugin @@ -817,7 +893,20 @@ CommandProvider { propMap.put(prop.getName(), prop); } } - addNodeProps(node, propMap); + + if (propMapCurr == null) { + if (nodeProps.putIfAbsent(node, propMap) != null) { + log.debug("Cluster conflict: Conflict while updating the node. Node: {} Properties: {}", + node.getID(), props); + addNodeProps(node, propMap); + } + } else { + if (!nodeProps.replace(node, propMapCurr, propMap)) { + log.debug("Cluster conflict: Conflict while updating the node. Node: {} Properties: {}", + node.getID(), props); + addNodeProps(node, propMap); + } + } /* notify node listeners */ notifyNode(node, UpdateType.CHANGED, propMap); @@ -928,30 +1017,67 @@ CommandProvider { @Override public void setNodeProp(Node node, Property prop) { - /* Get a copy of the property map */ - Map propMap = getNodeProps(node); - if (propMap == null) { - return; - } - propMap.put(prop.getName(), prop); - this.nodeProps.put(node, propMap); + for (int i = 0; i <= REPLACE_RETRY; i++) { + /* Get a copy of the property map */ + Map propMapCurr = getNodeProps(node); + if (propMapCurr == null) { + return; + } + + Map propMap = new HashMap(); + for (String s : propMapCurr.keySet()) { + propMap.put(s, propMapCurr.get(s).clone()); + } + + propMap.put(prop.getName(), prop); + + if (nodeProps.replace(node, propMapCurr, propMap)) { + return; + } + if (!propMapCurr.get(prop.getName()).equals(nodeProps.get(node).get(prop.getName()))) { + log.warn("Cluster conflict: Unable to add property {} to node {}.", prop.getName(), node.getID()); + return; + } + } + log.warn("Cluster conflict: Unable to add property {} to node {}.", prop.getName(), node.getID()); } @Override public Status removeNodeProp(Node node, String propName) { - Map propMap = getNodeProps(node); - if (propMap != null) { - propMap.remove(propName); - this.nodeProps.put(node, propMap); + for (int i = 0; i <= REPLACE_RETRY; i++) { + Map propMapCurr = getNodeProps(node); + if (propMapCurr != null) { + if (!propMapCurr.containsKey(propName)) { + return new Status(StatusCode.SUCCESS); + } + Map propMap = new HashMap(); + for (String s : propMapCurr.keySet()) { + propMap.put(s, propMapCurr.get(s).clone()); + } + + propMap.remove(propName); + if (nodeProps.replace(node, propMapCurr, propMap)) { + return new Status(StatusCode.SUCCESS); + } + if (!propMapCurr.get(propName).equals(nodeProps.get(node).get(propName))) { + String msg = "Cluster conflict: Unable to remove property " + propName + " for node " + + node.getID(); + return new Status(StatusCode.CONFLICT, msg); + } + + } else { + return new Status(StatusCode.SUCCESS); + } } - return new Status(StatusCode.SUCCESS, null); + String msg = "Cluster conflict: Unable to remove property " + propName + " for node " + node.getID(); + return new Status(StatusCode.CONFLICT, msg); } @Override public Status removeNodeAllProps(Node node) { this.nodeProps.remove(node); - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } @Override @@ -1100,36 +1226,73 @@ CommandProvider { @Override public Status addNodeConnectorProp(NodeConnector nodeConnector, Property prop) { - Map propMap = getNodeConnectorProps(nodeConnector); + Map propMapCurr = getNodeConnectorProps(nodeConnector); + Map propMap = new HashMap(); - if (propMap == null) { - propMap = new HashMap(); + if (propMapCurr != null) { + for (String s : propMapCurr.keySet()) { + propMap.put(s, propMapCurr.get(s).clone()); + } } + String msg = "Cluster conflict: Unable to add NodeConnector Property."; // Just add the nodeConnector if prop is not available (in a non-default // container) if (prop == null) { - nodeConnectorProps.put(nodeConnector, propMap); - return new Status(StatusCode.SUCCESS, null); + if (propMapCurr == null) { + if (nodeConnectorProps.putIfAbsent(nodeConnector, propMap) != null) { + return new Status(StatusCode.CONFLICT, msg); + } + } else { + if (!nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap)) { + return new Status(StatusCode.CONFLICT, msg); + } + } + return new Status(StatusCode.SUCCESS); } propMap.put(prop.getName(), prop); - nodeConnectorProps.put(nodeConnector, propMap); + if (propMapCurr == null) { + if (nodeConnectorProps.putIfAbsent(nodeConnector, propMap) != null) { + return new Status(StatusCode.CONFLICT, msg); + } + } else { + if (!nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap)) { + return new Status(StatusCode.CONFLICT, msg); + } + } if (prop.getName().equals(Name.NamePropName)) { if (nodeConnectorNames != null) { Node node = nodeConnector.getNode(); - Map map = nodeConnectorNames.get(node); - if (map == null) { - map = new HashMap(); + Map mapCurr = nodeConnectorNames.get(node); + Map map = new HashMap(); + if (mapCurr != null) { + for (String s : mapCurr.keySet()) { + try { + map.put(s, new NodeConnector(mapCurr.get(s))); + } catch (ConstructionException e) { + e.printStackTrace(); + } + } } map.put(((Name) prop).getValue(), nodeConnector); - nodeConnectorNames.put(node, map); + if (mapCurr == null) { + if (nodeConnectorNames.putIfAbsent(node, map) != null) { + // TODO: recovery using Transactionality + return new Status(StatusCode.CONFLICT, msg); + } + } else { + if (!nodeConnectorNames.replace(node, mapCurr, map)) { + // TODO: recovery using Transactionality + return new Status(StatusCode.CONFLICT, msg); + } + } } } - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } /** @@ -1142,32 +1305,53 @@ CommandProvider { * @return success or failed reason */ @Override - public Status removeNodeConnectorProp(NodeConnector nodeConnector, - String propName) { - Map propMap = getNodeConnectorProps(nodeConnector); + public Status removeNodeConnectorProp(NodeConnector nodeConnector, String propName) { + Map propMapCurr = getNodeConnectorProps(nodeConnector); - if (propMap == null) { + if (propMapCurr == null) { /* Nothing to remove */ - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); + } + + Map propMap = new HashMap(); + + for (String s : propMapCurr.keySet()) { + propMap.put(s, propMapCurr.get(s).clone()); } propMap.remove(propName); - nodeConnectorProps.put(nodeConnector, propMap); + boolean result = nodeConnectorProps.replace(nodeConnector, propMapCurr, propMap); + String msg = "Cluster conflict: Unable to remove NodeConnector property."; + if (!result) { + return new Status(StatusCode.CONFLICT, msg); + } - if (nodeConnectorNames != null) { - Name name = ((Name) getNodeConnectorProp(nodeConnector, - Name.NamePropName)); - if (name != null) { - Node node = nodeConnector.getNode(); - Map map = nodeConnectorNames.get(node); - if (map != null) { - map.remove(name.getValue()); - nodeConnectorNames.put(node, map); + if (propName.equals(Name.NamePropName)) { + if (nodeConnectorNames != null) { + Name name = ((Name) getNodeConnectorProp(nodeConnector, Name.NamePropName)); + if (name != null) { + Node node = nodeConnector.getNode(); + Map mapCurr = nodeConnectorNames.get(node); + if (mapCurr != null) { + Map map = new HashMap(); + for (String s : mapCurr.keySet()) { + try { + map.put(s, new NodeConnector(mapCurr.get(s))); + } catch (ConstructionException e) { + e.printStackTrace(); + } + } + map.remove(name.getValue()); + if (!nodeConnectorNames.replace(node, mapCurr, map)) { + // TODO: recovery using Transactionality + return new Status(StatusCode.CONFLICT, msg); + } + } } } } - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } /** @@ -1180,20 +1364,31 @@ CommandProvider { @Override public Status removeNodeConnectorAllProps(NodeConnector nodeConnector) { if (nodeConnectorNames != null) { - Name name = ((Name) getNodeConnectorProp(nodeConnector, - Name.NamePropName)); + Name name = ((Name) getNodeConnectorProp(nodeConnector, Name.NamePropName)); if (name != null) { Node node = nodeConnector.getNode(); - Map map = nodeConnectorNames.get(node); - if (map != null) { + Map mapCurr = nodeConnectorNames.get(node); + if (mapCurr != null) { + Map map = new HashMap(); + for (String s : mapCurr.keySet()) { + try { + map.put(s, new NodeConnector(mapCurr.get(s))); + } catch (ConstructionException e) { + e.printStackTrace(); + } + } map.remove(name.getValue()); - nodeConnectorNames.put(node, map); + if (!nodeConnectorNames.replace(node, mapCurr, map)) { + log.warn("Cluster conflict: Unable remove Name property of nodeconnector {}, skip.", + nodeConnector.getID()); + } } + } } nodeConnectorProps.remove(nodeConnector); - return new Status(StatusCode.SUCCESS, null); + return new Status(StatusCode.SUCCESS); } /** @@ -1339,22 +1534,21 @@ CommandProvider { } Map> nodeProp = this.inventoryService.getNodeProps(); - for(Map.Entry> entry : nodeProp.entrySet()) { + for (Map.Entry> entry : nodeProp.entrySet()) { Node node = entry.getKey(); - log.debug("getInventories: {} added for container {}", - new Object[] { node, containerName }); + log.debug("getInventories: {} added for container {}", new Object[] { node, containerName }); Map propMap = entry.getValue(); Set props = new HashSet(); - for(Property property : propMap.values()) { + for (Property property : propMap.values()) { props.add(property); } addNode(node, props); } Map> nodeConnectorProp = this.inventoryService.getNodeConnectorProps(); - for(Map.Entry> entry : nodeConnectorProp.entrySet()) { + for (Map.Entry> entry : nodeConnectorProp.entrySet()) { Map propMap = entry.getValue(); - for(Property property : propMap.values()) { + for (Property property : propMap.values()) { addNodeConnectorProp(entry.getKey(), property); } } diff --git a/opendaylight/switchmanager/implementation/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImplTest.java b/opendaylight/switchmanager/implementation/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImplTest.java index 9bb231fb03..25d54906ed 100644 --- a/opendaylight/switchmanager/implementation/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImplTest.java +++ b/opendaylight/switchmanager/implementation/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerImplTest.java @@ -31,7 +31,7 @@ public class SwitchManagerImplTest { @Test public void testSwitchManagerAddRemoveSubnet() { SwitchManagerImpl switchmgr = new SwitchManagerImpl(); - switchmgr.nonClusterObjectCreate(); + switchmgr.startUp(); ArrayList portList = new ArrayList(); portList.add("1/1"); @@ -56,7 +56,7 @@ public class SwitchManagerImplTest { @Test public void testSwitchManagerNodeConnectors() { SwitchManagerImpl switchmgr = new SwitchManagerImpl(); - switchmgr.nonClusterObjectCreate(); + switchmgr.startUp(); State state; Bandwidth bw; diff --git a/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java b/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java index ea24ffcf88..39a9cceb08 100644 --- a/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java +++ b/opendaylight/switchmanager/integrationtest/src/test/java/org/opendaylight/controller/switchmanager/internal/SwitchManagerIT.java @@ -8,65 +8,48 @@ package org.opendaylight.controller.switchmanager.internal; -import java.net.InetAddress; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.ops4j.pax.exam.CoreOptions.junitBundles; +import static org.ops4j.pax.exam.CoreOptions.mavenBundle; +import static org.ops4j.pax.exam.CoreOptions.options; +import static org.ops4j.pax.exam.CoreOptions.systemPackages; +import static org.ops4j.pax.exam.CoreOptions.systemProperty; + import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.Bundle; import javax.inject.Inject; -import org.eclipse.osgi.framework.console.CommandProvider; import org.junit.Assert; -import org.junit.Test; import org.junit.Before; -import org.junit.After; +import org.junit.Test; import org.junit.runner.RunWith; import org.opendaylight.controller.sal.core.Actions; import org.opendaylight.controller.sal.core.Bandwidth; import org.opendaylight.controller.sal.core.Buffers; import org.opendaylight.controller.sal.core.Capabilities; +import org.opendaylight.controller.sal.core.Capabilities.CapabilitiesType; import org.opendaylight.controller.sal.core.ConstructionException; import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.core.Property; import org.opendaylight.controller.sal.core.State; import org.opendaylight.controller.sal.core.TimeStamp; -import org.opendaylight.controller.sal.core.UpdateType; -import org.opendaylight.controller.sal.core.Property; -import org.opendaylight.controller.sal.core.Capabilities.CapabilitiesType; -import org.opendaylight.controller.sal.utils.NodeConnectorCreator; -import org.opendaylight.controller.sal.utils.NodeCreator; -import org.opendaylight.controller.sal.utils.Status; -import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates; -import org.opendaylight.controller.switchmanager.*; -import org.opendaylight.controller.configuration.IConfigurationContainerAware; -import org.opendaylight.controller.clustering.services.ICacheUpdateAware; import org.opendaylight.controller.switchmanager.ISwitchManager; - -import org.ops4j.pax.exam.junit.PaxExam; -import org.ops4j.pax.exam.util.Filter; -import org.osgi.framework.BundleContext; -import static org.junit.Assert.*; -import org.ops4j.pax.exam.junit.Configuration; -import static org.ops4j.pax.exam.CoreOptions.*; - import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.Configuration; +import org.ops4j.pax.exam.junit.PaxExam; import org.ops4j.pax.exam.util.PathUtils; -import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; -import org.ops4j.pax.exam.spi.reactors.PerClass; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @RunWith(PaxExam.class) public class SwitchManagerIT { - private Logger log = LoggerFactory.getLogger(SwitchManagerIT.class); + private final Logger log = LoggerFactory.getLogger(SwitchManagerIT.class); // get the OSGI bundle context @Inject private BundleContext bc; @@ -107,29 +90,28 @@ public class SwitchManagerIT { .versionAsInProject(), mavenBundle("ch.qos.logback", "logback-classic") .versionAsInProject(), - - mavenBundle("org.opendaylight.controller", "switchmanager") - .versionAsInProject(), - mavenBundle("org.opendaylight.controller", - "switchmanager.implementation").versionAsInProject(), - mavenBundle("org.opendaylight.controller", "sal") + mavenBundle("org.opendaylight.controller", "clustering.stub") .versionAsInProject(), - mavenBundle("org.opendaylight.controller", "sal.implementation") + mavenBundle("org.opendaylight.controller", "configuration") .versionAsInProject(), + mavenBundle("org.opendaylight.controller", + "configuration.implementation").versionAsInProject(), mavenBundle("org.opendaylight.controller", "containermanager") .versionAsInProject(), mavenBundle("org.opendaylight.controller", "containermanager.implementation").versionAsInProject(), mavenBundle("org.opendaylight.controller", "clustering.services").versionAsInProject(), - mavenBundle("org.opendaylight.controller", "clustering.stub") + mavenBundle("org.opendaylight.controller", "sal") .versionAsInProject(), - mavenBundle("org.opendaylight.controller", "configuration") + mavenBundle("org.opendaylight.controller", "sal.implementation") .versionAsInProject(), mavenBundle("org.opendaylight.controller", - "configuration.implementation").versionAsInProject(), + "protocol_plugins.stub").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "switchmanager") + .versionAsInProject(), mavenBundle("org.opendaylight.controller", - "protocol_plugins.stub").versionAsInProject(), + "switchmanager.implementation").versionAsInProject(), mavenBundle("org.jboss.spec.javax.transaction", "jboss-transaction-api_1.1_spec").versionAsInProject(), mavenBundle("org.apache.commons", "commons-lang3") @@ -203,11 +185,11 @@ public class SwitchManagerIT { Assert.assertTrue(this.switchManager.getNodeProp(node, Capabilities.CapabilitiesPropName).equals( - new Capabilities((int) 3))); + new Capabilities(3))); Assert.assertTrue(this.switchManager.getNodeProp(node, - Actions.ActionsPropName).equals(new Actions((int) 2))); + Actions.ActionsPropName).equals(new Actions(2))); Assert.assertTrue(this.switchManager.getNodeProp(node, - Buffers.BuffersPropName).equals(new Buffers((int) 1))); + Buffers.BuffersPropName).equals(new Buffers(1))); Assert.assertTrue(this.switchManager.getNodeProp(node, TimeStamp.TimeStampPropName).equals( new TimeStamp(100000L, "connectedSince"))); diff --git a/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java index 534c2c293c..04f9863312 100644 --- a/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java +++ b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java @@ -16,17 +16,11 @@ import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentMap; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.codehaus.jackson.map.ObjectMapper; -import org.opendaylight.controller.web.DaylightWebUtil; -import org.opendaylight.controller.web.IDaylightWeb; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting; import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig; import org.opendaylight.controller.sal.authorization.Privilege; @@ -46,6 +40,13 @@ import org.opendaylight.controller.switchmanager.SpanConfig; import org.opendaylight.controller.switchmanager.SubnetConfig; import org.opendaylight.controller.switchmanager.Switch; import org.opendaylight.controller.switchmanager.SwitchConfig; +import org.opendaylight.controller.web.DaylightWebUtil; +import org.opendaylight.controller.web.IDaylightWeb; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; import com.google.gson.Gson; -- 2.36.6