<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
+ <argLine>${testvm.argLine}</argLine>
<systemProperties>
<property>
<name>logback.configurationFile</name>
<artifactId>maven-release-plugin</artifactId>
<version>${releaseplugin.version}</version>
</plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>${jacoco.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ </plugin>
+
<!-- Ignore/Execute plugin execution -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
--- /dev/null
+<snapshot>
+ <configuration>
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ prefix:toaster-provider-impl
+ </type>
+ <name>toaster-provider-impl</name>
+
+ <rpc-registry>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+
+ <notification-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl">
+ prefix:toaster-consumer-impl
+ </type>
+ <name>toaster-consumer-impl</name>
+
+ <rpc-registry>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+
+ <notification-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:toaster="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider">toaster:toaster-provider</type>
+ <instance>
+ <name>toaster-provider</name>
+ <provider>/modules/module[type='toaster-provider-impl'][name='toaster-provider-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:toaster="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer">toaster:toaster-consumer</type>
+ <instance>
+ <name>toaster-consumer</name>
+ <provider>/modules/module[type='toaster-consumer-impl'][name='toaster-consumer-impl']</provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+
+ </configuration>
+
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer?module=toaster-consumer&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl?module=toaster-consumer-impl&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider?module=toaster-provider&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31</capability>
+ </required-capabilities>
+
+</snapshot>
+
package org.opendaylight.controller.forwardingrulesmanager;
+import java.io.Serializable;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
import org.opendaylight.controller.configuration.ConfigurationObject;
import org.opendaylight.controller.sal.action.Action;
import org.opendaylight.controller.sal.action.ActionType;
import org.opendaylight.controller.sal.action.SetVlanId;
import org.opendaylight.controller.sal.action.SetVlanPcp;
import org.opendaylight.controller.sal.action.SwPath;
-import org.opendaylight.controller.sal.core.ContainerFlow;
-import org.opendaylight.controller.sal.core.IContainer;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.flowprogrammer.Flow;
import org.opendaylight.controller.sal.match.Match;
import org.opendaylight.controller.sal.match.MatchType;
-import org.opendaylight.controller.sal.utils.GlobalConstants;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.IPProtocols;
import org.opendaylight.controller.sal.utils.NetUtils;
-import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
-import org.opendaylight.controller.sal.utils.ServiceHelper;
import org.opendaylight.controller.sal.utils.Status;
import org.opendaylight.controller.sal.utils.StatusCode;
-import org.opendaylight.controller.switchmanager.ISwitchManager;
-import org.opendaylight.controller.switchmanager.Switch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import java.io.Serializable;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
/**
* Configuration Java Object which represents a flow configuration information
* for Forwarding Rules Manager.
return true;
}
- public boolean isPortValid(Switch sw, String port) {
- if (sw == null) {
- log.debug("switch info is not available. Skip checking if port is part of a switch or not.");
- return true;
- }
- NodeConnector nc = NodeConnectorCreator.createNodeConnector(port, sw.getNode());
- return sw.getNodeConnectors().contains(nc);
- }
-
public boolean isVlanIdValid(String vlanId) {
int vlan = Integer.decode(vlanId);
return ((vlan >= 0) && (vlan < 4096));
return (proto != null);
}
- private Status conflictWithContainerFlow(IContainer container) {
- // Return true if it's default container
- if (container.getName().equals(GlobalConstants.DEFAULT.toString())) {
- return new Status(StatusCode.SUCCESS);
- }
-
- // No container flow = no conflict
- List<ContainerFlow> cFlowList = container.getContainerFlows();
- if (((cFlowList == null)) || cFlowList.isEmpty()) {
- return new Status(StatusCode.SUCCESS);
- }
-
- // Check against each container's flow
- Flow flow = this.getFlow();
-
- // Configuration is rejected if it conflicts with _all_ the container
- // flows
- for (ContainerFlow cFlow : cFlowList) {
- if (cFlow.allowsFlow(flow)) {
- log.trace("Config is congruent with at least one container flow");
- return new Status(StatusCode.SUCCESS);
- }
- }
- String msg = "Flow Config conflicts with all existing container flows";
- log.trace(msg);
-
- return new Status(StatusCode.BADREQUEST, msg);
- }
-
- public Status validate(IContainer container) {
+ public Status validate() {
EtherIPType etype = EtherIPType.ANY;
EtherIPType ipsrctype = EtherIPType.ANY;
EtherIPType ipdsttype = EtherIPType.ANY;
- String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container.getName();
- ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName,
- this);
-
- Switch sw = null;
try {
// Flow name cannot be internal flow signature
if (!isValidResourceName(name) || isInternalFlow()) {
return new Status(StatusCode.BADREQUEST, "Node is null");
}
- if (switchManager != null) {
- for (Switch device : switchManager.getNetworkDevices()) {
- if (device.getNode().equals(node)) {
- sw = device;
- break;
- }
- }
- if (sw == null) {
- return new Status(StatusCode.BADREQUEST, String.format("Node %s not found", node));
- }
- } else {
- log.debug("switchmanager is not set yet");
- }
-
if (priority != null) {
if (Integer.decode(priority) < 0 || (Integer.decode(priority) > 65535)) {
return new Status(StatusCode.BADREQUEST, String.format("priority %s is not in the range 0 - 65535",
Long.decode(cookie);
}
- if (ingressPort != null) {
- if (!isPortValid(sw, ingressPort)) {
- String msg = String.format("Ingress port %s is not valid for the Switch", ingressPort);
- if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
- msg += " in Container " + containerName;
- }
- return new Status(StatusCode.BADREQUEST, msg);
- }
+ if (ingressPort != null && ingressPort.isEmpty()) {
+ return new Status(StatusCode.BADREQUEST, "Invalid ingress port");
}
if ((vlanId != null) && !isVlanIdValid(vlanId)) {
return new Status(StatusCode.BADREQUEST, "Actions value is null or empty");
}
for (String actiongrp : actions) {
- // check output ports
- sstr = Pattern.compile("OUTPUT=(.*)").matcher(actiongrp);
- if (sstr.matches()) {
- for (String t : sstr.group(1).split(",")) {
- if (t != null) {
- if (!isPortValid(sw, t)) {
- String msg = String.format("Output port %s is not valid for this switch", t);
- if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
- msg += " in Container " + containerName;
- }
- return new Status(StatusCode.BADREQUEST, msg);
- }
- }
- }
- continue;
- }
- // check enqueue
- sstr = Pattern.compile("ENQUEUE=(.*)").matcher(actiongrp);
- if (sstr.matches()) {
- for (String t : sstr.group(1).split(",")) {
- if (t != null) {
- String port = t.split(":")[0];
- if (!isPortValid(sw, port)) {
- String msg = String.format("Output port %d is not valid for this switch", port);
- if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
- msg += " in Container " + containerName;
- }
- return new Status(StatusCode.BADREQUEST, msg);
- }
- }
- }
- continue;
- }
- // Check src IP
- sstr = Pattern.compile(ActionType.FLOOD.toString()).matcher(actiongrp);
- if (sstr.matches()) {
- if (!containerName.equals(GlobalConstants.DEFAULT.toString())) {
- return new Status(StatusCode.BADREQUEST, String.format(
- "flood is not allowed in container %s", containerName));
- }
- continue;
- }
// Check src IP
sstr = Pattern.compile(ActionType.SET_NW_SRC.toString() + "=(.*)").matcher(actiongrp);
if (sstr.matches()) {
continue;
}
}
- // Check against the container flow
- Status status;
- if (!containerName.equals(GlobalConstants.DEFAULT.toString()) && !(status = conflictWithContainerFlow(container)).isSuccess()) {
- return status;
- }
} catch (NumberFormatException e) {
return new Status(StatusCode.BADREQUEST, String.format("Invalid number format %s", e.getMessage()));
}
FlowConfig flowConfig = new FlowConfig();
Assert.assertFalse(flowConfig.isInternalFlow());
flowConfig.setName("__Internal__");
- Status status = flowConfig.validate(null);
+ Status status = flowConfig.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("name"));
Assert.assertTrue(flowConfig.isInternalFlow());
@Test
public void testValid() throws UnknownHostException {
FlowConfig fc2 = createSampleFlowConfig();
- Assert.assertTrue(fc2.validate(null).isSuccess());
+ Assert.assertTrue(fc2.validate().isSuccess());
FlowConfig fc = new FlowConfig();
- Status status = fc.validate(null);
+ Status status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Invalid name"));
fc.setName("Config");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Node is null"));
fc.setNode(Node.fromString(Node.NodeIDType.OPENFLOW, "1"));
- Assert.assertFalse(fc.validate(null).isSuccess());
+ Assert.assertFalse(fc.validate().isSuccess());
List<String> actions = new ArrayList<String>();
fc.setActions(actions);
- Assert.assertFalse(fc.validate(null).isSuccess());
+ Assert.assertFalse(fc.validate().isSuccess());
actions.add("OUTPUT=2");
fc.setActions(actions);
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setPriority("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("is not in the range 0 - 65535"));
fc.setPriority("100000");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("is not in the range 0 - 65535"));
fc.setPriority("2000");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setCookie("100");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setIngressPort("100");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setVlanId(("-1"));
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("is not in the range 0 - 4095"));
fc.setVlanId("5000");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("is not in the range 0 - 4095"));
fc.setVlanId("100");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setVlanPriority("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("is not in the range 0 - 7"));
fc.setVlanPriority("9");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("is not in the range 0 - 7"));
fc.setVlanPriority("5");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setEtherType("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Ethernet type"));
fc.setEtherType("0xfffff");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Ethernet type"));
fc.setEtherType("0x800");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setTosBits("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("IP ToS bits"));
fc.setTosBits("65");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("IP ToS bits"));
fc.setTosBits("60");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setSrcPort("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Transport source port"));
fc.setSrcPort("0xfffff");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Transport source port"));
fc.setSrcPort("0");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setSrcPort("0x00ff");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setSrcPort("0xffff");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setDstPort("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Transport destination port"));
fc.setDstPort("0xfffff");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Transport destination port"));
fc.setDstPort("0");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setDstPort("0x00ff");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setDstPort("0xffff");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setSrcMac("abc");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Ethernet source address"));
fc.setSrcMac("00:A0:C9:14:C8:29");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setDstMac("abc");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Ethernet destination address"));
fc.setDstMac("00:A0:C9:22:AB:11");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setSrcIp("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("IP source address"));
fc.setSrcIp("2001:420:281:1004:407a:57f4:4d15:c355");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Type mismatch between Ethernet & Src IP"));
fc.setEtherType("0x86dd");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setSrcIp("1.1.1.1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Type mismatch between Ethernet & Src IP"));
fc.setEtherType("0x800");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setDstIp("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("IP destination address"));
fc.setDstIp("2001:420:281:1004:407a:57f4:4d15:c355");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Type mismatch between Ethernet & Dst IP"));
fc.setEtherType("0x86dd");
fc.setSrcIp("2001:420:281:1004:407a:57f4:4d15:c355");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setDstIp("2.2.2.2");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Type mismatch between Ethernet & Dst IP"));
fc.setEtherType("0x800");
fc.setSrcIp("1.1.1.1");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setEtherType(null);
fc.setSrcIp("2001:420:281:1004:407a:57f4:4d15:c355");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("IP Src Dest Type mismatch"));
fc.setSrcIp("1.1.1.1");
fc.setIdleTimeout("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Idle Timeout value"));
fc.setIdleTimeout("0xfffff");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Idle Timeout value"));
fc.setIdleTimeout("10");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
fc.setHardTimeout("-1");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Hard Timeout value"));
fc.setHardTimeout("0xfffff");
- status = fc.validate(null);
+ status = fc.validate();
Assert.assertFalse(status.isSuccess());
Assert.assertTrue(status.getDescription().contains("Hard Timeout value"));
fc.setHardTimeout("10");
- Assert.assertTrue(fc.validate(null).isSuccess());
+ Assert.assertTrue(fc.validate().isSuccess());
}
import org.opendaylight.controller.forwardingrulesmanager.implementation.data.FlowEntryDistributionOrder;
import org.opendaylight.controller.sal.action.Action;
import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.Enqueue;
+import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.FloodAll;
import org.opendaylight.controller.sal.action.Output;
import org.opendaylight.controller.sal.connection.ConnectionLocality;
import org.opendaylight.controller.sal.core.Config;
return null;
}
+ /**
+ * Checks if the FlowEntry targets are valid for this container
+ *
+ * @param flowEntry
+ * The flow entry to test
+ * @return a Status object representing the result of the validation
+ */
+ private Status validateEntry(FlowEntry flowEntry) {
+ // Node presence check
+ Node node = flowEntry.getNode();
+ if (!switchManager.getNodes().contains(node)) {
+ return new Status(StatusCode.BADREQUEST, String.format("Node %s is not present in this container", node));
+ }
+
+ // Ports and actions validation check
+ Flow flow = flowEntry.getFlow();
+ Match match = flow.getMatch();
+ if (match.isPresent(MatchType.IN_PORT)) {
+ NodeConnector inputPort = (NodeConnector)match.getField(MatchType.IN_PORT).getValue();
+ if (!switchManager.getNodeConnectors(node).contains(inputPort)) {
+ String msg = String.format("Ingress port %s is not present on this container", inputPort);
+ return new Status(StatusCode.BADREQUEST, msg);
+ }
+ }
+ for (Action action : flow.getActions()) {
+ if (action instanceof Flood && !GlobalConstants.DEFAULT.toString().equals(getContainerName())) {
+ return new Status(StatusCode.BADREQUEST, String.format("Flood is only allowed in default container"));
+ }
+ if (action instanceof FloodAll && !GlobalConstants.DEFAULT.toString().equals(getContainerName())) {
+ return new Status(StatusCode.BADREQUEST, String.format("FloodAll is only allowed in default container"));
+ }
+ if (action instanceof Output) {
+ Output out = (Output)action;
+ NodeConnector outputPort = out.getPort();
+ if (!switchManager.getNodeConnectors(node).contains(outputPort)) {
+ String msg = String.format("Output port %s is not present on this container", outputPort);
+ return new Status(StatusCode.BADREQUEST, msg);
+ }
+ }
+ if (action instanceof Enqueue) {
+ Enqueue out = (Enqueue)action;
+ NodeConnector outputPort = out.getPort();
+ if (!switchManager.getNodeConnectors(node).contains(outputPort)) {
+ String msg = String.format("Enqueue port %s is not present on this container", outputPort);
+ return new Status(StatusCode.BADREQUEST, msg);
+ }
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
+ }
+
/**
* Adds a flow entry onto the network node It runs various validity checks
* and derive the final container flows merged entries that will be
return new Status(StatusCode.NOTACCEPTABLE, INVALID_FLOW_ENTRY);
}
+ // Operational check: input, output and queue ports presence check and
+ // action validation for this container
+ Status status = validateEntry(flowEntry);
+ if (!status.isSuccess()) {
+ String msg = String.format("%s: %s", INVALID_FLOW_ENTRY, status.getDescription());
+ log.warn("{}: {}", msg, flowEntry);
+ return new Status(StatusCode.NOTACCEPTABLE, msg);
+ }
+
/*
* Redundant Check: Check if the request is a redundant one from the
* same application the flowEntry is equal to an existing one. Given we
return new Status(StatusCode.SUCCESS, msg);
}
+ // Operational check: input, output and queue ports presence check and
+ // action validation for this container
+ Status status = validateEntry(newFlowEntry);
+ if (!status.isSuccess()) {
+ String msg = String.format("Modify: %s: %s", INVALID_FLOW_ENTRY, status.getDescription());
+ log.warn("{}: {}", msg, newFlowEntry);
+ return new Status(StatusCode.NOTACCEPTABLE, msg);
+ }
+
/*
* Conflict Check: Verify the new entry would not conflict with an
* existing one. This is a loose check on the previous original flow
List<FlowEntryInstall> list = new ArrayList<FlowEntryInstall>(groupFlows.get(groupName));
toBeRemoved = list.size();
for (FlowEntryInstall entry : list) {
- Status status = this.removeEntry(entry.getOriginal(), false);
+ // since this is the entry that was stored in groupFlows
+ // it is already validated and merged
+ // so can call removeEntryInternal directly
+ Status status = this.removeEntryInternal(entry, false);
if (status.isSuccess()) {
toBeRemoved -= 1;
} else {
@Override
public Status addStaticFlow(FlowConfig config) {
// Configuration object validation
- Status status = config.validate(container);
+ Status status = config.validate();
if (!status.isSuccess()) {
log.warn("Invalid Configuration for flow {}. The failure is {}", config, status.getDescription());
String error = "Invalid Configuration (" + status.getDescription() + ")";
config.setStatus(StatusCode.SUCCESS.toString());
break;
default:
+ break;
}
}
}
}
// Validity Check
- Status status = newFlowConfig.validate(container);
+ Status status = newFlowConfig.validate();
if (!status.isSuccess()) {
String msg = "Invalid Configuration (" + status.getDescription() + ")";
newFlowConfig.setStatus(msg);
}
}
if (target != null) {
- Status status = target.validate(container);
+ Status status = target.validate();
if (!status.isSuccess()) {
log.warn(status.getDescription());
return status;
this.reinstallAllFlowEntries();
break;
default:
+ break;
}
// Update our configuration DB
<sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
</properties>
<build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
- </plugin>
- </plugins>
- </pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
</properties>
<build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
- </plugin>
- </plugins>
- </pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
package org.opendaylight.controller.sal.compatibility.adsal;
import java.math.BigInteger;
-import java.util.concurrent.Future;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.common.util.Futures;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.compatibility.InventoryMapping;
import org.opendaylight.controller.sal.compatibility.ToSalConversionsUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
public class FlowServiceAdapter implements SalFlowService, IFlowProgrammerListener {
private static final Logger LOG = LoggerFactory.getLogger(FlowServiceAdapter.class);
}
@Override
- public Future<RpcResult<AddFlowOutput>> addFlow(AddFlowInput input) {
+ public ListenableFuture<RpcResult<AddFlowOutput>> addFlow(AddFlowInput input) {
Flow flow = ToSalConversionsUtils.toFlow(input, null);
@SuppressWarnings("unchecked")
}
@Override
- public Future<RpcResult<RemoveFlowOutput>> removeFlow(RemoveFlowInput input) {
+ public ListenableFuture<RpcResult<RemoveFlowOutput>> removeFlow(RemoveFlowInput input) {
Flow flow = ToSalConversionsUtils.toFlow(input, null);
@SuppressWarnings("unchecked")
}
@Override
- public Future<RpcResult<UpdateFlowOutput>> updateFlow(UpdateFlowInput input) {
+ public ListenableFuture<RpcResult<UpdateFlowOutput>> updateFlow(UpdateFlowInput input) {
@SuppressWarnings("unchecked")
org.opendaylight.controller.sal.core.Node node = InventoryMapping.toAdNode((InstanceIdentifier<Node>) input
.getNode().getValue());
import java.util.concurrent.Future;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.common.util.Futures;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.compatibility.FromSalConversionsUtils;
import org.opendaylight.controller.sal.compatibility.InventoryMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
public class FlowStatisticsAdapter implements OpendaylightFlowStatisticsService, IReadServiceListener{
private static final Logger LOG = LoggerFactory.getLogger(FlowStatisticsAdapter.class);
@Override
public Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput>> getAggregateFlowStatisticsFromFlowTableForAllFlows(
GetAggregateFlowStatisticsFromFlowTableForAllFlowsInput input) {
- //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
+ //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
// generating aggregate flow statistics out of those individual flow stats.
return null;
}
@Override
public Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForGivenMatchOutput>> getAggregateFlowStatisticsFromFlowTableForGivenMatch(
GetAggregateFlowStatisticsFromFlowTableForGivenMatchInput input) {
- //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
+ //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
// generating aggregate flow statistics out of those individual flow stats.
return null;
}
@Override
- public Future<RpcResult<GetAllFlowStatisticsFromFlowTableOutput>> getAllFlowStatisticsFromFlowTable(
+ public ListenableFuture<RpcResult<GetAllFlowStatisticsFromFlowTableOutput>> getAllFlowStatisticsFromFlowTable(
GetAllFlowStatisticsFromFlowTableInput input) {
GetAllFlowStatisticsFromFlowTableOutput rpcResultType = null;
boolean rpcResultBool = false;
GetAllFlowStatisticsFromFlowTableOutputBuilder builder = new GetAllFlowStatisticsFromFlowTableOutputBuilder();
builder.setTransactionId(new TransactionId(new BigInteger("0")));
rpcResultType = builder.setFlowAndStatisticsMapList(flowsStatistics).build();
-
+
rpcResultBool = true;
} catch (ConstructionException e) {
LOG.error(e.getMessage());
* Essentially this API will return the same result as getAllFlowStatisticsFromFlowTable
*/
@Override
- public Future<RpcResult<GetAllFlowsStatisticsFromAllFlowTablesOutput>> getAllFlowsStatisticsFromAllFlowTables(
+ public ListenableFuture<RpcResult<GetAllFlowsStatisticsFromAllFlowTablesOutput>> getAllFlowsStatisticsFromAllFlowTables(
GetAllFlowsStatisticsFromAllFlowTablesInput input) {
-
+
GetAllFlowsStatisticsFromAllFlowTablesOutput rpcResultType = null;
boolean rpcResultBool = false;
GetAllFlowsStatisticsFromAllFlowTablesOutputBuilder builder = new GetAllFlowsStatisticsFromAllFlowTablesOutputBuilder();
builder.setTransactionId(new TransactionId(new BigInteger("0")));
rpcResultType = builder.setFlowAndStatisticsMapList(flowsStatistics).build();
-
+
rpcResultBool = true;
} catch (ConstructionException e) {
LOG.error(e.getMessage());
}
@Override
- public Future<RpcResult<GetFlowStatisticsFromFlowTableOutput>> getFlowStatisticsFromFlowTable(
+ public ListenableFuture<RpcResult<GetFlowStatisticsFromFlowTableOutput>> getFlowStatisticsFromFlowTable(
GetFlowStatisticsFromFlowTableInput input) {
GetFlowStatisticsFromFlowTableOutput rpcResultType = null;
boolean rpcResultBool = false;
public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
NodeConnectorStatisticsUpdateBuilder nodeConnectorStatisticsUpdateBuilder = new NodeConnectorStatisticsUpdateBuilder();
List<NodeConnectorStatisticsAndPortNumberMap> nodeConnectorStatistics = toOdNodeConnectorStatistics(ncStatsList);
-
+
nodeConnectorStatisticsUpdateBuilder.setNodeConnectorStatisticsAndPortNumberMap(nodeConnectorStatistics);
nodeConnectorStatisticsUpdateBuilder.setMoreReplies(false);
nodeConnectorStatisticsUpdateBuilder.setTransactionId(null);
@Override
public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
-
- FlowTableStatisticsUpdateBuilder flowTableStatisticsUpdateBuilder = new FlowTableStatisticsUpdateBuilder();
-
+
+ FlowTableStatisticsUpdateBuilder flowTableStatisticsUpdateBuilder = new FlowTableStatisticsUpdateBuilder();
+
List<FlowTableAndStatisticsMap> flowTableStatistics = toOdFlowTableStatistics(tableStatsList);
flowTableStatisticsUpdateBuilder.setFlowTableAndStatisticsMap(flowTableStatistics);
flowTableStatisticsUpdateBuilder.setMoreReplies(false);
@Override
public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
// TODO which *StatisticsUpdated interface should be used?
-
+
}
private List<FlowAndStatisticsMapList> toOdFlowsStatistics(List<FlowOnNode> flowsOnNode) {
}
private List<FlowTableAndStatisticsMap> toOdFlowTableStatistics(List<NodeTableStatistics> tableStatsList) {
-
+
List<FlowTableAndStatisticsMap> flowTableStatsMap = new ArrayList<FlowTableAndStatisticsMap>();
for (NodeTableStatistics nodeTableStatistics : tableStatsList) {
FlowTableAndStatisticsMapBuilder flowTableAndStatisticsMapBuilder = new FlowTableAndStatisticsMapBuilder();
flowTableAndStatisticsMapBuilder.setTableId(new TableId((short)nodeTableStatistics.getNodeTable().getID()));
flowTableStatsMap.add(flowTableAndStatisticsMapBuilder.build());
}
-
+
return flowTableStatsMap;
}
List<NodeConnectorStatisticsAndPortNumberMap> nodeConnectorStatisticsList = new ArrayList<NodeConnectorStatisticsAndPortNumberMap>();
for(NodeConnectorStatistics ofNodeConnectorStatistics : ncStatsList){
NodeConnectorStatisticsAndPortNumberMapBuilder nodeConnectorStatisticsAndPortNumberMapBuilder = new NodeConnectorStatisticsAndPortNumberMapBuilder();
-
+
nodeConnectorStatisticsAndPortNumberMapBuilder.setBytes(extractBytes(ofNodeConnectorStatistics));
nodeConnectorStatisticsAndPortNumberMapBuilder.setCollisionCount(toBI(ofNodeConnectorStatistics.getCollisionCount()));
nodeConnectorStatisticsAndPortNumberMapBuilder.setDuration(null);
nodeConnectorStatisticsAndPortNumberMapBuilder.setTransmitErrors(toBI(ofNodeConnectorStatistics.getTransmitErrorCount()));
nodeConnectorStatisticsList.add(nodeConnectorStatisticsAndPortNumberMapBuilder.build());
}
-
+
return nodeConnectorStatisticsList;
}
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
*/
package org.opendaylight.controller.sal.connector.remoterpc.api;
-import java.util.Map;
import java.util.Set;
public interface RoutingTable<I,R> {
-
-
/**
- * Adds a network address for the route. If address for route
- * exists, appends the address to the list
+ * Adds a network address for the route. If the route already exists,
+ * it throws <code>DuplicateRouteException</code>.
+ * This method would be used when registering a global service.
+ *
*
* @param routeId route identifier
* @param route network address
- * @throws RoutingTableException for any logical exception
+ * @throws DuplicateRouteException
+ * @throws RoutingTableException
+ */
+ public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException;
+
+ /**
+ * Remove the route.
+ * This method would be used when registering a global service.
+ * @param routeId
+ * @throws RoutingTableException
* @throws SystemException
*/
- public void addRoute(I routeId, R route) throws RoutingTableException,SystemException;
+ public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException;
- /**
+ /**
* Adds a network address for the route. If the route already exists,
* it throws <code>DuplicateRouteException</code>.
* This method would be used when registering a global service.
* @throws DuplicateRouteException
* @throws RoutingTableException
*/
- public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException;
-
+ public R getGlobalRoute(I routeId) throws RoutingTableException, SystemException;
+ /**
+ * Adds a network address for the route. If address for route
+ * exists, appends the address to the list
+ *
+ * @param routeId route identifier
+ * @param route network address
+ * @throws RoutingTableException for any logical exception
+ * @throws SystemException
+ */
+ public void addRoute(I routeId, R route) throws RoutingTableException,SystemException;
/**
* @param routeId
* @param route
*/
- public void removeRoute(I routeId, R route);
+ public void removeRoute(I routeId, R route) throws RoutingTableException,SystemException;
+ /**
+ * Adds address for a set of route identifiers. If address for route
+ * exists, appends the address to the set.
+ *
+ * @param routeIds a set of routeIds
+ * @param route network address
+ * @throws RoutingTableException for any logical exception
+ * @throws SystemException
+ */
+ public void addRoutes(Set<I> routeIds, R route) throws RoutingTableException,SystemException;
- /**
- * Remove the route.
- * This method would be used when registering a global service.
- * @param routeId
- * @throws RoutingTableException
- * @throws SystemException
- */
- public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException;
+ /**
+ * Removes address for a set of route identifiers.
+ *
+ * @param routeIds a set of routeIds
+ * @param route network address
+ * @throws RoutingTableException for any logical exception
+ * @throws SystemException
+ */
+ public void removeRoutes(Set<I> routeIds, R route) throws RoutingTableException,SystemException;
/**
* Returns a set of network addresses associated with this route
*/
public Set<R> getRoutes(I routeId);
- /**
- * Returns all network addresses stored in the table
- * @return
- */
- public Set<Map.Entry> getAllRoutes();
/**
- * Returns only one address from the list of network addresses
- * associated with the route. The algorithm to determine that
- * one address is upto the implementer
+ * Returns the last inserted address from the list of network addresses
+ * associated with the route.
* @param routeId
* @return
*/
- public R getARoute(I routeId);
-
- /**
- *
- * This will be removed after listeners
- * have made change on their end to use whiteboard pattern
- * @deprecated
- */
-
- public void registerRouteChangeListener(RouteChangeListener listener);
+ public R getLastAddedRoute(I routeId);
public class DuplicateRouteException extends RoutingTableException {
public DuplicateRouteException(String message) {
if (imp.equals(RoutingTableImpl.class)) {
Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
Set<String> propSet = new HashSet<String>();
- propSet.add(RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE);
+ propSet.add(RoutingTableImpl.GLOBALRPC_CACHE);
+ propSet.add(RoutingTableImpl.RPC_CACHE);
props.put(CACHE_UPDATE_AWARE_REGISTRY_KEY, propSet);
c.setInterface(new String[] { RoutingTable.class.getName(),ICacheUpdateAware.class.getName() }, props);
package org.opendaylight.controller.sal.connector.remoterpc.impl;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
import org.apache.felix.dm.Component;
-import org.opendaylight.controller.clustering.services.CacheConfigException;
-import org.opendaylight.controller.clustering.services.CacheExistException;
-import org.opendaylight.controller.clustering.services.CacheListenerAddException;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
-import org.opendaylight.controller.clustering.services.IClusterServices;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
+import org.opendaylight.controller.clustering.services.*;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.ConcurrentMap;
-/**
- * @author: syedbahm
- */
public class RoutingTableImpl<I, R> implements RoutingTable<I, R>, ICacheUpdateAware<I, R> {
- public static final String ROUTING_TABLE_GLOBAL_CACHE = "routing_table_global_cache";
- private Logger log = LoggerFactory.getLogger(RoutingTableImpl.class);
+ private Logger log = LoggerFactory.getLogger(RoutingTableImpl.class);
- private IClusterGlobalServices clusterGlobalServices = null;
- private RoutingTableImpl routingTableInstance = null;
- private ConcurrentMap routingTableCache = null;
- private Set<RouteChangeListener> routeChangeListeners = Collections
- .synchronizedSet(new HashSet<RouteChangeListener>());
+ private IClusterGlobalServices clusterGlobalServices = null;
- public RoutingTableImpl() {
- }
+ private ConcurrentMap<I,R> globalRpcCache = null;
+ private ConcurrentMap<I, LinkedHashSet<R>> rpcCache = null; //need routes to ordered by insert-order
- @Override
- public void addRoute(I routeId, R route) throws RoutingTableException {
- throw new UnsupportedOperationException(" Not implemented yet!");
- }
+ public static final String GLOBALRPC_CACHE = "remoterpc_routingtable.globalrpc_cache";
+ public static final String RPC_CACHE = "remoterpc_routingtable.rpc_cache";
- @Override
- public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException {
- Preconditions.checkNotNull(routeId, "addGlobalRoute: routeId cannot be null!");
- Preconditions.checkNotNull(route, "addGlobalRoute: route cannot be null!");
- try {
-
- Set<R> existingRoute = null;
- // ok does the global route is already registered ?
- if ((existingRoute = getRoutes(routeId)) == null) {
-
- if (log.isDebugEnabled()) {
- log.debug("addGlobalRoute: adding a new route with id" + routeId + " and value = "
- + route);
- }
- // lets start a transaction
- clusterGlobalServices.tbegin();
-
- routingTableCache.put(routeId, route);
- clusterGlobalServices.tcommit();
- } else {
- throw new DuplicateRouteException(" There is already existing route " + existingRoute);
- }
-
- } catch (NotSupportedException|HeuristicRollbackException|RollbackException|HeuristicMixedException e) {
- throw new RoutingTableException("Transaction error - while trying to create route id="
- + routeId + "with route" + route, e);
- } catch (javax.transaction.SystemException e) {
- throw new SystemException("System error occurred - while trying to create with value", e);
- }
+ public RoutingTableImpl() {
+ }
- }
+ @Override
+ public R getGlobalRoute(I routeId) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "getGlobalRoute: routeId cannot be null!");
+ return globalRpcCache.get(routeId);
+ }
- @Override
- public void removeRoute(I routeId, R route) {
- throw new UnsupportedOperationException("Not implemented yet!");
+ @Override
+ public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "addGlobalRoute: routeId cannot be null!");
+ Preconditions.checkNotNull(route, "addGlobalRoute: route cannot be null!");
+ try {
+
+ log.debug("addGlobalRoute: adding a new route with id[{}] and value [{}]", routeId, route);
+ clusterGlobalServices.tbegin();
+ if (globalRpcCache.putIfAbsent(routeId, route) != null) {
+ throw new DuplicateRouteException(" There is already existing route " + routeId);
+ }
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | HeuristicMixedException e) {
+ throw new RoutingTableException("Transaction error - while trying to create route id="
+ + routeId + "with route" + route, e);
+ } catch (javax.transaction.SystemException e) {
+ throw new SystemException("System error occurred - while trying to create with value", e);
}
- @Override
- public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException {
- Preconditions.checkNotNull(routeId, "removeGlobalRoute: routeId cannot be null!");
- try {
- if (log.isDebugEnabled()) {
- log.debug("removeGlobalRoute: removing a new route with id" + routeId);
- }
- // lets start a transaction
- clusterGlobalServices.tbegin();
-
- routingTableCache.remove(routeId);
- clusterGlobalServices.tcommit();
-
- } catch (NotSupportedException|HeuristicRollbackException|RollbackException|HeuristicMixedException e) {
- throw new RoutingTableException("Transaction error - while trying to remove route id="
- + routeId, e);
- } catch (javax.transaction.SystemException e) {
- throw new SystemException("System error occurred - while trying to remove with value", e);
- }
+ }
+
+ @Override
+ public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "removeGlobalRoute: routeId cannot be null!");
+ try {
+ log.debug("removeGlobalRoute: removing a new route with id [{}]", routeId);
+
+ clusterGlobalServices.tbegin();
+ globalRpcCache.remove(routeId);
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | HeuristicMixedException e) {
+ throw new RoutingTableException("Transaction error - while trying to remove route id="
+ + routeId, e);
+ } catch (javax.transaction.SystemException e) {
+ throw new SystemException("System error occurred - while trying to remove with value", e);
}
+ }
+
- @Override
- public Set<R> getRoutes(I routeId) {
+ @Override
+ public Set<R> getRoutes(I routeId) {
+ Preconditions.checkNotNull(routeId, "getRoutes: routeId cannot be null!");
+ Set<R> routes = rpcCache.get(routeId);
- // Note: currently works for global routes only wherein there is just single
- // route
- Preconditions.checkNotNull(routeId, "getARoute: routeId cannot be null!");
- R route = (R)routingTableCache.get(routeId);
- Set<R>routes = null;
- if(route !=null){
- routes = new HashSet<R>();
- routes.add(route);
- }
+ if (routes == null) return Collections.emptySet();
+
+ return ImmutableSet.copyOf(routes);
+ }
- return routes;
- }
+
+
+ public R getLastAddedRoute(I routeId) {
+
+ Set<R> routes = getRoutes(routeId);
+
+ if (routes.isEmpty()) return null;
+
+ R route = null;
+ Iterator<R> iter = routes.iterator();
+ while (iter.hasNext())
+ route = iter.next();
+
+ return route;
+ }
@Override
- public Set<Map.Entry> getAllRoutes() {
- return routingTableCache.entrySet();
+ public void addRoute(I routeId, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "addRoute: routeId cannot be null");
+ Preconditions.checkNotNull(route, "addRoute: route cannot be null");
+
+ try{
+ clusterGlobalServices.tbegin();
+ log.debug("addRoute: adding a route with k/v [{}/{}]", routeId, route);
+ threadSafeAdd(routeId, route);
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | HeuristicMixedException e) {
+ throw new RoutingTableException("Transaction error - while trying to remove route id="
+ + routeId, e);
+ } catch (javax.transaction.SystemException e) {
+ throw new SystemException("System error occurred - while trying to remove with value", e);
+ }
}
@Override
- public R getARoute(I routeId) {
- throw new UnsupportedOperationException("Not implemented yet!");
+ public void addRoutes(Set<I> routeIds, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeIds, "addRoutes: routeIds must not be null");
+ for (I routeId : routeIds){
+ addRoute(routeId, route);
}
+ }
- /**
- * @deprecated doesn't do anything will be removed once listeners used
- * whiteboard pattern Registers listener for sending any change
- * notification
- * @param listener
- */
- @Override
- public void registerRouteChangeListener(RouteChangeListener listener) {
+ @Override
+ public void removeRoute(I routeId, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "removeRoute: routeId cannot be null!");
+ Preconditions.checkNotNull(route, "removeRoute: route cannot be null!");
- }
+ LinkedHashSet<R> routes = rpcCache.get(routeId);
+ if (routes == null) return;
- public void setRouteChangeListener(RouteChangeListener rcl) {
- if(rcl != null){
- routeChangeListeners.add(rcl);
- }else{
- log.warn("setRouteChangeListener called with null listener");
- }
- }
+ try {
+ log.debug("removeRoute: removing a new route with k/v [{}/{}]", routeId, route);
- public void unSetRouteChangeListener(RouteChangeListener rcl) {
- if(rcl != null){
- routeChangeListeners.remove(rcl);
- }else{
- log.warn("unSetRouteChangeListener called with null listener");
- }
+ clusterGlobalServices.tbegin();
+ threadSafeRemove(routeId, route);
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | HeuristicMixedException e) {
+ throw new RoutingTableException("Transaction error - while trying to remove route id="
+ + routeId, e);
+ } catch (javax.transaction.SystemException e) {
+ throw new SystemException("System error occurred - while trying to remove with value", e);
}
+ }
- /**
- * Returning the set of route change listeners for Unit testing Note: the
- * package scope is default
- *
- * @return List of registered RouteChangeListener<I,R> listeners
- */
- Set<RouteChangeListener> getRegisteredRouteChangeListeners() {
- return routeChangeListeners;
+ @Override
+ public void removeRoutes(Set<I> routeIds, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeIds, "removeRoutes: routeIds must not be null");
+ for (I routeId : routeIds){
+ removeRoute(routeId, route);
}
+ }
- public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
- this.clusterGlobalServices = clusterGlobalServices;
+ /**
+ * This method guarantees that no 2 thread over write each other's changes.
+ * Just so that we dont end up in infinite loop, it tries for 100 times then throw
+ */
+ private void threadSafeAdd(I routeId, R route) {
+
+ for (int i=0;i<100;i++){
+
+ LinkedHashSet<R> updatedRoutes = new LinkedHashSet<>();
+ updatedRoutes.add(route);
+ LinkedHashSet<R> oldRoutes = rpcCache.putIfAbsent(routeId, updatedRoutes);
+ if (oldRoutes == null) return;
+
+ updatedRoutes = new LinkedHashSet<>(oldRoutes);
+ updatedRoutes.add(route);
+
+ if (rpcCache.replace(routeId, oldRoutes, updatedRoutes)) return;
}
+ //the method did not already return means it failed to add route in 10 attempts
+ throw new IllegalStateException("Failed to add route [" + routeId + "]");
+ }
+
+ /**
+ * This method guarantees that no 2 thread over write each other's changes.
+ * Just so that we dont end up in infinite loop, it tries for 10 times then throw
+ */
+ private void threadSafeRemove(I routeId, R route) {
+ LinkedHashSet<R> updatedRoutes = null;
+ for (int i=0;i<10;i++){
+ LinkedHashSet<R> oldRoutes = rpcCache.get(routeId);
+
+ // if route to be deleted is the only entry in the set then remove routeId from the cache
+ if ((oldRoutes.size() == 1) && oldRoutes.contains(route)){
+ rpcCache.remove(routeId);
+ return;
+ }
+
+ // if there are multiple routes for this routeId, remove the route to be deleted only from the set.
+ updatedRoutes = new LinkedHashSet<>(oldRoutes);
+ updatedRoutes.remove(route);
+ if (rpcCache.replace(routeId, oldRoutes, updatedRoutes)) return;
- public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
- if((clusterGlobalServices != null ) && (this.clusterGlobalServices.equals(clusterGlobalServices))){
- this.clusterGlobalServices = null;
- }
}
+ //the method did not already return means it failed to remove route in 10 attempts
+ throw new IllegalStateException("Failed to remove route [" + routeId + "]");
+ }
+
- /**
- * Creates the Routing Table clustered global services cache
- *
- * @throws CacheExistException
- * -- cluster global services exception when cache exist
- * @throws CacheConfigException
- * -- cluster global services exception during cache config
- * @throws CacheListenerAddException
- * -- cluster global services exception during adding of listener
- */
-
- void createRoutingTableCache() throws CacheExistException, CacheConfigException,
- CacheListenerAddException {
- // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it
- // should be caching?
-
- // let us check here if the cache already exists -- if so don't create
- if (!clusterGlobalServices.existCache(ROUTING_TABLE_GLOBAL_CACHE)) {
-
- if (log.isDebugEnabled()) {
- log.debug("createRoutingTableCache: creating a new routing table cache "
- + ROUTING_TABLE_GLOBAL_CACHE);
- }
- routingTableCache = clusterGlobalServices.createCache(ROUTING_TABLE_GLOBAL_CACHE,
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
- } else {
- if (log.isDebugEnabled()) {
- log.debug("createRoutingTableCache: found existing routing table cache "
- + ROUTING_TABLE_GLOBAL_CACHE);
- }
- routingTableCache = clusterGlobalServices.getCache(ROUTING_TABLE_GLOBAL_CACHE);
- }
+// /**
+// * @deprecated doesn't do anything will be removed once listeners used
+// * whiteboard pattern Registers listener for sending any change
+// * notification
+// * @param listener
+// */
+// @Override
+// public void registerRouteChangeListener(RouteChangeListener listener) {
+//
+// }
+
+// public void setRouteChangeListener(RouteChangeListener rcl) {
+// if(rcl != null){
+// routeChangeListeners.add(rcl);
+// }else{
+// log.warn("setRouteChangeListener called with null listener");
+// }
+// }
+//
+// public void unSetRouteChangeListener(RouteChangeListener rcl) {
+// if(rcl != null){
+// routeChangeListeners.remove(rcl);
+// }else{
+// log.warn("unSetRouteChangeListener called with null listener");
+// }
+// }
+
+ /**
+ * Returning the set of route change listeners for Unit testing Note: the
+ * package scope is default
+ *
+ * @return List of registered RouteChangeListener<I,R> listeners
+ */
+// Set<RouteChangeListener> getRegisteredRouteChangeListeners() {
+// return routeChangeListeners;
+// }
+ public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
+ this.clusterGlobalServices = clusterGlobalServices;
+ }
+
+ public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
+ if ((clusterGlobalServices != null) && (this.clusterGlobalServices.equals(clusterGlobalServices))) {
+ this.clusterGlobalServices = null;
+ }
+ }
+ /**
+ * Finds OR Creates clustered cache for Global RPCs
+ *
+ * @throws CacheExistException -- cluster global services exception when cache exist
+ * @throws CacheConfigException -- cluster global services exception during cache config
+ * @throws CacheListenerAddException -- cluster global services exception during adding of listener
+ */
+
+ void findOrCreateGlobalRpcCache() throws CacheExistException, CacheConfigException,
+ CacheListenerAddException {
+ // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it
+ // should be caching?
+
+ // let us check here if the cache already exists -- if so don't create
+ if (!clusterGlobalServices.existCache(GLOBALRPC_CACHE)) {
+
+ globalRpcCache = (ConcurrentMap<I,R>) clusterGlobalServices.createCache(GLOBALRPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+ log.debug("Cache created [{}] ", GLOBALRPC_CACHE);
+
+ } else {
+ globalRpcCache = (ConcurrentMap<I,R>) clusterGlobalServices.getCache(GLOBALRPC_CACHE);
+ log.debug("Cache exists [{}] ", GLOBALRPC_CACHE);
}
+ }
- /**
- * Function called by the dependency manager when all the required
- * dependencies are satisfied
- *
- */
- void init(Component c) {
- try {
-
- createRoutingTableCache();
- } catch (CacheExistException e) {
- throw new IllegalStateException("could not construct routing table cache");
- } catch (CacheConfigException e) {
- throw new IllegalStateException("could not construct routing table cache");
- } catch (CacheListenerAddException e) {
- throw new IllegalStateException("could not construct routing table cache");
- }
+ /**
+ * Finds OR Creates clustered cache for Routed RPCs
+ *
+ * @throws CacheExistException -- cluster global services exception when cache exist
+ * @throws CacheConfigException -- cluster global services exception during cache config
+ * @throws CacheListenerAddException -- cluster global services exception during adding of listener
+ */
+
+ void findOrCreateRpcCache() throws CacheExistException, CacheConfigException,
+ CacheListenerAddException {
+ // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it
+ // should be caching?
+
+ if (clusterGlobalServices.existCache(RPC_CACHE)){
+ rpcCache = (ConcurrentMap<I,LinkedHashSet<R>>) clusterGlobalServices.getCache(RPC_CACHE);
+ log.debug("Cache exists [{}] ", RPC_CACHE);
+ return;
}
- /**
- * Get routing table method is useful for unit testing <note>It has package
- * scope</note>
- */
- ConcurrentMap getRoutingTableCache() {
- return this.routingTableCache;
+ //cache doesnt exist, create one
+ rpcCache = (ConcurrentMap<I,LinkedHashSet<R>>) clusterGlobalServices.createCache(RPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+ log.debug("Cache created [{}] ", RPC_CACHE);
+ }
+
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ */
+ void init(Component c) {
+ try {
+
+ findOrCreateGlobalRpcCache();
+ findOrCreateRpcCache();
+
+ } catch (CacheExistException|CacheConfigException|CacheListenerAddException e) {
+ throw new IllegalStateException("could not construct routing table cache");
}
+ }
+
+ /**
+ * Useful for unit testing <note>It has package
+ * scope</note>
+ */
+ ConcurrentMap getGlobalRpcCache() {
+ return this.globalRpcCache;
+ }
+
+ /**
+ * Useful for unit testing <note>It has package
+ * scope</note>
+ */
+ ConcurrentMap getRpcCache() {
+ return this.rpcCache;
+ }
- /**
- * This is used from integration test NP rest API to check out the result of the
- * cache population
- * <Note> For testing purpose only-- use it wisely</Note>
- * @return
- */
- public String dumpRoutingTableCache(){
- Set<Map.Entry<I, R>> cacheEntrySet = this.routingTableCache.entrySet();
- StringBuilder sb = new StringBuilder();
- for(Map.Entry<I,R> entry:cacheEntrySet){
- sb.append("Key:").append(entry.getKey()).append("---->Value:")
- .append((entry.getValue() != null)?entry.getValue():"null")
- .append("\n");
- }
- return sb.toString();
+ /**
+ * This is used from integration test NP rest API to check out the result of the
+ * cache population
+ * <Note> For testing purpose only-- use it wisely</Note>
+ *
+ * @return
+ */
+ public String dumpGlobalRpcCache() {
+ Set<Map.Entry<I, R>> cacheEntrySet = this.globalRpcCache.entrySet();
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<I, R> entry : cacheEntrySet) {
+ sb.append("Key:").append(entry.getKey()).append("---->Value:")
+ .append((entry.getValue() != null) ? entry.getValue() : "null")
+ .append("\n");
}
+ return sb.toString();
+ }
- /**
- * Invoked when a new entry is available in the cache, the key is only
- * provided, the value will come as an entryUpdate invocation
- *
- * @param key
- * Key for the entry just created
- * @param cacheName
- * name of the cache for which update has been received
- * @param originLocal
- * true if the event is generated from this node
- */
- @Override
- public void entryCreated(I key, String cacheName, boolean originLocal) {
- // TBD: do we require this.
- if (log.isDebugEnabled()) {
- log.debug("RoutingTableUpdates: entryCreated routeId = " + key + " cacheName=" + cacheName);
- }
+ public String dumpRpcCache() {
+ Set<Map.Entry<I, LinkedHashSet<R>>> cacheEntrySet = this.rpcCache.entrySet();
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<I, LinkedHashSet<R>> entry : cacheEntrySet) {
+ sb.append("Key:").append(entry.getKey()).append("---->Value:")
+ .append((entry.getValue() != null) ? entry.getValue() : "null")
+ .append("\n");
}
+ return sb.toString();
+ }
+ /**
+ * Invoked when a new entry is available in the cache, the key is only
+ * provided, the value will come as an entryUpdate invocation
+ *
+ * @param key Key for the entry just created
+ * @param cacheName name of the cache for which update has been received
+ * @param originLocal true if the event is generated from this node
+ */
+ @Override
+ public void entryCreated(I key, String cacheName, boolean originLocal) {
+ // TBD: do we require this.
+ if (log.isDebugEnabled()) {
+ log.debug("RoutingTableUpdates: entryCreated routeId = " + key + " cacheName=" + cacheName);
+ }
+ }
- /**
- * Called anytime a given entry is updated
- *
- * @param key
- * Key for the entry modified
- * @param new_value
- * the new value the key will have
- * @param cacheName
- * name of the cache for which update has been received
- * @param originLocal
- * true if the event is generated from this node
- */
- @Override
- public void entryUpdated(I key, R new_value, String cacheName, boolean originLocal) {
- if (log.isDebugEnabled()) {
- log.debug("RoutingTableUpdates: entryUpdated routeId = " + key + ",value = " + new_value
- + " ,cacheName=" + cacheName + " originLocal="+originLocal);
- }
- if (!originLocal) {
- for (RouteChangeListener rcl : routeChangeListeners) {
- rcl.onRouteUpdated(key, new_value);
- }
- }
+ /**
+ * Called anytime a given entry is updated
+ *
+ * @param key Key for the entry modified
+ * @param new_value the new value the key will have
+ * @param cacheName name of the cache for which update has been received
+ * @param originLocal true if the event is generated from this node
+ */
+ @Override
+ public void entryUpdated(I key, R new_value, String cacheName, boolean originLocal) {
+ if (log.isDebugEnabled()) {
+ log.debug("RoutingTableUpdates: entryUpdated routeId = " + key + ",value = " + new_value
+ + " ,cacheName=" + cacheName + " originLocal=" + originLocal);
}
+// if (!originLocal) {
+// for (RouteChangeListener rcl : routeChangeListeners) {
+// rcl.onRouteUpdated(key, new_value);
+// }
+// }
+ }
- /**
- * Called anytime a given key is removed from the ConcurrentHashMap we are
- * listening to.
- *
- * @param key
- * Key of the entry removed
- * @param cacheName
- * name of the cache for which update has been received
- * @param originLocal
- * true if the event is generated from this node
- */
- @Override
- public void entryDeleted(I key, String cacheName, boolean originLocal) {
- if (log.isDebugEnabled()) {
- log.debug("RoutingTableUpdates: entryUpdated routeId = " + key + " local = " + originLocal
- + " cacheName=" + cacheName + " originLocal="+originLocal);
- }
- if (!originLocal) {
- for (RouteChangeListener rcl : routeChangeListeners) {
- rcl.onRouteDeleted(key);
- }
- }
+ /**
+ * Called anytime a given key is removed from the ConcurrentHashMap we are
+ * listening to.
+ *
+ * @param key Key of the entry removed
+ * @param cacheName name of the cache for which update has been received
+ * @param originLocal true if the event is generated from this node
+ */
+ @Override
+ public void entryDeleted(I key, String cacheName, boolean originLocal) {
+ if (log.isDebugEnabled()) {
+ log.debug("RoutingTableUpdates: entryUpdated routeId = " + key + " local = " + originLocal
+ + " cacheName=" + cacheName + " originLocal=" + originLocal);
}
+// if (!originLocal) {
+// for (RouteChangeListener rcl : routeChangeListeners) {
+// rcl.onRouteDeleted(key);
+// }
+// }
+ }
}
\ No newline at end of file
import junit.framework.Assert;
import org.apache.felix.dm.Component;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
+import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import java.net.URI;
import java.util.EnumSet;
import java.util.HashSet;
-import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
-/**
- * @author: syedbahm
- */
public class RoutingTableImplTest {
- private IClusterGlobalServices ics = mock(IClusterGlobalServices.class);
- private RoutingTableImpl rti = new RoutingTableImpl();
+ private final URI namespace = URI.create("http://cisco.com/example");
+ private final QName QNAME = new QName(namespace, "global");
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace,"global");
+ private IClusterGlobalServices clusterService;
+ private RoutingTableImpl<RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>, String> routingTable;
+ ConcurrentMap mockGlobalRpcCache;
+ ConcurrentMap mockRpcCache;
- ConcurrentMap concurrentMapMock = mock(ConcurrentMap.class);
+ @Before
+ public void setUp() throws Exception{
+ clusterService = mock(IClusterGlobalServices.class);
+ routingTable = new RoutingTableImpl<RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>, String>();
+ mockGlobalRpcCache = new ConcurrentHashMap<>();
+ mockRpcCache = new ConcurrentHashMap<>();
+ createRoutingTableCache();
+ }
+ @After
+ public void tearDown(){
+ reset(clusterService);
+ mockGlobalRpcCache = null;
+ mockRpcCache = null;
+ }
- @Test
- public void testAddGlobalRoute() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ @Test
+ public void addGlobalRoute_ValidArguments_ShouldAdd() throws Exception {
- Assert.assertNotNull(concurrentMap);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
- InstanceIdentifier identifier = mock(InstanceIdentifier.class);
- when(routeIdentifier.getType()).thenReturn(QNAME);
- when(routeIdentifier.getRoute()).thenReturn(identifier);
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ final String expectedRoute = "172.27.12.1:5000";
+ routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
- Set<String> globalService = new HashSet<String>();
- globalService.add("172.27.12.1:5000");
+ ConcurrentMap latestCache = routingTable.getGlobalRpcCache();
+ Assert.assertEquals(mockGlobalRpcCache, latestCache);
+ Assert.assertEquals(expectedRoute, latestCache.get(routeIdentifier));
+ }
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ @Test (expected = RoutingTable.DuplicateRouteException.class)
+ public void addGlobalRoute_DuplicateRoute_ShouldThrow() throws Exception{
- Assert.assertEquals(concurrentMap,latestCache);
+ Assert.assertNotNull(mockGlobalRpcCache);
- Set<String> servicesGlobal = (Set<String>)latestCache.get(routeIdentifier);
- Assert.assertEquals(servicesGlobal.size(),1);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ routingTable.addGlobalRoute(routeIdentifier, new String());
+ routingTable.addGlobalRoute(routeIdentifier, new String());
+ }
- Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+ @Test
+ public void getGlobalRoute_ExistingRouteId_ShouldReturnRoute() throws Exception {
- }
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ String expectedRoute = "172.27.12.1:5000";
- @Test
- public void testGetRoutes() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
- Assert.assertNotNull(concurrentMap);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
- InstanceIdentifier identifier = mock(InstanceIdentifier.class);
- when(routeIdentifier.getContext()).thenReturn(QNAME);
- when(routeIdentifier.getRoute()).thenReturn(identifier);
+ String actualRoute = (String) routingTable.getGlobalRoute(routeIdentifier);
+ Assert.assertEquals(expectedRoute, actualRoute);
+ }
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ @Test
+ public void getGlobalRoute_NonExistentRouteId_ShouldReturnNull() throws Exception {
- String globalService = "172.27.12.1:5000";
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ String actualRoute = (String) routingTable.getGlobalRoute(routeIdentifier);
+ Assert.assertNull(actualRoute);
+ }
- Assert.assertEquals(concurrentMap,latestCache);
+ @Test
+ public void removeGlobalRoute_ExistingRouteId_ShouldRemove() throws Exception {
- Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ ConcurrentMap cache = routingTable.getGlobalRpcCache();
+ Assert.assertTrue(cache.size() == 0);
+ routingTable.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ Assert.assertTrue(cache.size() == 1);
- Assert.assertEquals(servicesGlobal.size(),1);
- Iterator<String> iterator = servicesGlobal.iterator();
- while(iterator.hasNext()){
- Assert.assertEquals(iterator.next(),"172.27.12.1:5000");
- }
+ routingTable.removeGlobalRoute(routeIdentifier);
+ Assert.assertTrue(cache.size() == 0);
+ }
- }
- @Test
- public void testRegisterRouteChangeListener() throws Exception {
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0);
- rti.registerRouteChangeListener(new RouteChangeListenerImpl());
+ @Test
+ public void removeGlobalRoute_NonExistentRouteId_ShouldDoNothing() throws Exception {
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0); //old should not work
- //what about the new approach - using whiteboard pattern
- rti.setRouteChangeListener(new RouteChangeListenerImpl());
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),1); //should not work
+ ConcurrentMap cache = routingTable.getGlobalRpcCache();
+ Assert.assertTrue(cache.size() == 0);
+ routingTable.removeGlobalRoute(routeIdentifier);
+ Assert.assertTrue(cache.size() == 0);
- }
- @Test
- public void testRemoveGlobalRoute()throws Exception {
+ }
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ @Test
+ public void addRoute_ForNewRouteId_ShouldAddRoute() throws Exception {
+ Assert.assertTrue(mockRpcCache.size() == 0);
- Assert.assertNotNull(concurrentMap);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
- InstanceIdentifier identifier = mock(InstanceIdentifier.class);
- when(routeIdentifier.getContext()).thenReturn(QNAME);
- when(routeIdentifier.getRoute()).thenReturn(identifier);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ routingTable.addRoute(routeId, new String());
+ Assert.assertTrue(mockRpcCache.size() == 1);
- String globalService = "172.27.12.1:5000";
+ Set<String> routes = routingTable.getRoutes(routeId);
+ Assert.assertEquals(1, routes.size());
+ }
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ @Test
+ public void addRoute_ForExistingRouteId_ShouldAppendRoute() throws Exception {
- Assert.assertEquals(concurrentMap,latestCache);
+ Assert.assertTrue(mockRpcCache.size() == 0);
- Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5955";
+ String route_2 = "10.0.0.2:5955";
- Assert.assertEquals(servicesGlobal.size(),1);
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
- Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+ Assert.assertTrue(mockRpcCache.size() == 1);
- rti.removeGlobalRoute(routeIdentifier);
+ Set<String> routes = routingTable.getRoutes(routeId);
+ Assert.assertEquals(2, routes.size());
+ Assert.assertTrue(routes.contains(route_1));
+ Assert.assertTrue(routes.contains(route_2));
+ }
- Assert.assertNotNull(rti.getRoutes(routeIdentifier));
+ @Test
+ public void addRoute_UsingMultipleThreads_ShouldNotOverwrite(){
+ ExecutorService threadPool = Executors.newCachedThreadPool();
+ int numOfRoutesToAdd = 100;
+ String routePrefix_1 = "10.0.0.1:555";
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ threadPool.submit(addRoutes(numOfRoutesToAdd, routePrefix_1, routeId));
+ String routePrefix_2 = "10.0.0.1:556";
+ threadPool.submit(addRoutes(numOfRoutesToAdd, routePrefix_2, routeId));
+ // wait for all tasks to complete; timeout in 10 sec
+ threadPool.shutdown();
+ try {
+ threadPool.awaitTermination(10, TimeUnit.SECONDS); //
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
- private ConcurrentMap createRoutingTableCache() throws Exception {
+ Assert.assertEquals(2*numOfRoutesToAdd, routingTable.getRoutes(routeId).size());
+ }
- //here init
- Component c = mock(Component.class);
+ @Test(expected = NullPointerException.class)
+ public void addRoute_NullRouteId_shouldThrowNpe() throws Exception {
- when(ics.existCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(false);
+ routingTable.addRoute(null, new String());
+ }
- when(ics.createCache(RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(concurrentMapMock);
- rti.setClusterGlobalServices(this.ics);
- rti.init(c);
+ @Test(expected = NullPointerException.class)
+ public void addRoute_NullRoute_shouldThrowNpe() throws Exception{
- Assert.assertEquals(concurrentMapMock,rti.getRoutingTableCache() );
- return concurrentMapMock;
+ routingTable.addRoute(getRouteIdentifier(), null);
+ }
- }
+ @Test (expected = UnsupportedOperationException.class)
+ public void getRoutes_Call_ShouldReturnImmutableCopy() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ routingTable.addRoute(routeId, new String());
+ Set<String> routes = routingTable.getRoutes(routeId); //returns Immutable Set
- @Test
- public void testCreateRoutingTableCacheReturnExistingCache() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ routes.add(new String()); //can not be modified; should throw
+ }
- //OK here we should try creating again the cache but this time it should return the existing one
- when(ics.existCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(true);
+ @Test
+ public void getRoutes_With2RoutesFor1RouteId_ShouldReturnASetWithSize2() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ routingTable.addRoute(routeId, "10.0.0.1:5555");
+ routingTable.addRoute(routeId, "10.0.0.2:5555");
- when(ics.getCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(concurrentMap);
+ Set<String> routes = routingTable.getRoutes(routeId); //returns Immutable Set
+ Assert.assertEquals(2, routes.size());
+ }
- //here init
- Component c = mock(Component.class);
+ @Test
+ public void getLastAddedRoute_WhenMultipleRoutesExists_ShouldReturnLatestRoute()
+ throws Exception {
- rti.init(c);
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
+ String route_2 = "10.0.0.2:5555";
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
- Assert.assertEquals(concurrentMap,rti.getRoutingTableCache());
+ Assert.assertEquals(route_2, routingTable.getLastAddedRoute(routeId));
+ }
+ @Test
+ public void removeRoute_WhenMultipleRoutesExist_RemovesGivenRoute() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
+ String route_2 = "10.0.0.2:5555";
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
+ Assert.assertEquals(2, routingTable.getRoutes(routeId).size());
+ routingTable.removeRoute(routeId, route_1);
+ Assert.assertEquals(1, routingTable.getRoutes(routeId).size());
- }
+ }
- private class RouteChangeListenerImpl<I,R> implements RouteChangeListener<I,R>{
+ @Test
+ public void removeRoute_WhenOnlyOneRouteExists_RemovesRouteId() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
- @Override
- public void onRouteUpdated(I key, R new_value) {
- //To change body of implemented methods use File | Settings | File Templates.
- }
+ routingTable.addRoute(routeId, route_1);
+ Assert.assertEquals(1, routingTable.getRoutes(routeId).size());
- @Override
- public void onRouteDeleted(I key) {
- //To change body of implemented methods use File | Settings | File Templates.
- }
- }
+ routingTable.removeRoute(routeId, route_1);
+ ConcurrentMap cache = routingTable.getRpcCache();
+ Assert.assertFalse(cache.containsKey(routeId));
+
+ }
+ /*
+ * Private helper methods
+ */
+ private void createRoutingTableCache() throws Exception {
+
+ //here init
+ Component c = mock(Component.class);
+
+ when(clusterService.existCache(
+ RoutingTableImpl.GLOBALRPC_CACHE)).thenReturn(false);
+
+ when(clusterService.createCache(RoutingTableImpl.GLOBALRPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).
+ thenReturn(mockGlobalRpcCache);
+
+ when(clusterService.existCache(
+ RoutingTableImpl.RPC_CACHE)).thenReturn(false);
+
+ when(clusterService.createCache(RoutingTableImpl.RPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).
+ thenReturn(mockRpcCache);
+
+ doNothing().when(clusterService).tbegin();
+ doNothing().when(clusterService).tcommit();
+
+ routingTable.setClusterGlobalServices(this.clusterService);
+ routingTable.init(c);
+
+ Assert.assertEquals(mockGlobalRpcCache, routingTable.getGlobalRpcCache());
+ Assert.assertEquals(mockRpcCache, routingTable.getRpcCache());
+ }
+
+ private RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> getRouteIdentifier(){
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
+ InstanceIdentifier identifier = mock(InstanceIdentifier.class);
+ when(routeIdentifier.getType()).thenReturn(QNAME);
+ when(routeIdentifier.getRoute()).thenReturn(identifier);
+
+ return routeIdentifier;
+ }
+
+ private Runnable addRoutes(final int numRoutes, final String routePrefix, final RpcRouter.RouteIdentifier routeId){
+ return new Runnable() {
+ @Override
+ public void run() {
+ for (int i=0;i<numRoutes;i++){
+ String route = routePrefix + i;
+ try {
+ routingTable.addRoute(routeId, route);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+ }
}
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
DataModificationTransaction target = biDataService.beginTransaction();
LOG.debug("Created DOM Transaction {} for {},", target.getIdentifier(),source.getIdentifier());
+ for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
+ target.removeConfigurationData(biEntry);
+ LOG.debug("Delete of Binding Configuration Data {} is translated to {}",entry,biEntry);
+ }
+ for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedOperationalData()) {
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
+ target.removeOperationalData(biEntry);
+ LOG.debug("Delete of Binding Operational Data {} is translated to {}",entry,biEntry);
+ }
for (Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedConfigurationData()
.entrySet()) {
Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
target.putOperationalData(biEntry.getKey(), biEntry.getValue());
LOG.debug("Update of Binding Operational Data {} is translated to {}",entry,biEntry);
}
- for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
- target.removeConfigurationData(biEntry);
- LOG.debug("Delete of Binding Configuration Data {} is translated to {}",entry,biEntry);
- }
- for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedOperationalData()) {
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
- target.removeOperationalData(biEntry);
- LOG.debug("Delete of Binding Operational Data {} is translated to {}",entry,biEntry);
- }
+
return target;
}
DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction target = baDataService
.beginTransaction();
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
+ try {
+
+ InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
+ target.removeConfigurationData(baEntry);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry, e);
+ }
+ }
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) {
+ try {
+
+ InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
+ target.removeOperationalData(baEntry);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry, e);
+ }
+ }
for (Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
.getUpdatedConfigurationData().entrySet()) {
try {
LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
}
}
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
- try {
-
- InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
- target.removeConfigurationData(baEntry);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry, e);
- }
- }
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) {
- try {
-
- InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
- target.removeOperationalData(baEntry);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry, e);
- }
- }
return target;
}
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+/**
+ * @deprecated Use {@link com.google.common.util.concurrent.Futures} instead.
+ */
+@Deprecated
public class Futures {
private Futures() {
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
--- /dev/null
+package org.opendaylight.controller.sal.core.api;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public interface RoutedRpcDefaultImplementation {
+
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input);
+
+}
ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation);
+
+ /**
+ * Sets this RoutedRpc Implementation as a delegate rpc provider and will be asked to invoke rpc if the
+ * current provider can't service the rpc request
+ *
+ * @param defaultImplementation
+ * Provider's implementation of RPC functionality
+ */
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation);
}
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
broker.setRouter(new SchemaAwareRpcBroker("/", SchemaContextProviders.fromSchemaService(schemaService)));
dataService = new DataBrokerImpl();
- dataService.setExecutor(broker.getExecutor());
+ //dataService.setExecutor(broker.getExecutor());
dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener
import org.opendaylight.controller.sal.core.api.Broker
import org.opendaylight.controller.sal.core.api.Consumer
import org.opendaylight.controller.sal.core.api.Provider
-import org.opendaylight.controller.sal.core.api.RpcImplementation
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
-import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
-import org.opendaylight.controller.sal.core.api.RpcRoutingContext
-import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.common.RpcResult
import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
import org.osgi.framework.BundleContext
import org.slf4j.LoggerFactory
+import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
+import org.opendaylight.controller.sal.core.api.RpcImplementation
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener
+import org.opendaylight.controller.sal.core.api.RpcRoutingContext
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation
public class BrokerImpl implements Broker, RpcProvisionRegistry, AutoCloseable {
private static val log = LoggerFactory.getLogger(BrokerImpl);
override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
router.addRoutedRpcImplementation(rpcType,implementation);
}
-
+
+ override setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
+ router.setRoutedRpcDefaultDelegate(defaultImplementation);
+ }
+
override addRpcRegistrationListener(RpcRegistrationListener listener) {
return router.addRpcRegistrationListener(listener);
}
import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
}
@Override
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
+ rpcs.setRoutedRpcDefaultDelegate(defaultImplementation);
+ }
+
+ @Override
public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
throws IllegalArgumentException {
return rpcs.addRpcImplementation(rpcType, implementation);
import static com.google.common.base.Preconditions.checkState;
-import java.io.Console;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Set;
import java.util.concurrent.Future;
-import javax.activation.UnsupportedDataTypeException;
-
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.DataModification;
import org.opendaylight.controller.md.sal.common.api.data.DataReader;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
private NormalizedDataModification prepareMergedTransaction(
DataModification<InstanceIdentifier, CompositeNode> original) {
NormalizedDataModification normalized = new NormalizedDataModification(original);
- for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
- normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
- }
- for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
- normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
- }
+ LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(),
+ original.getRemovedConfigurationData(), original.getRemovedConfigurationData());
+ LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(),
+ original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet());
+ LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(),
+ original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet());
+
for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
normalized.deepRemoveConfigurationData(entry);
}
for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
normalized.deepRemoveOperationalData(entry);
}
+ for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
+ normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
+ }
+ for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
+ normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
+ }
return normalized;
}
public void putDeepConfigurationData(InstanceIdentifier entryKey, CompositeNode entryData) {
this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
}
-
+
public void putDeepOperationalData(InstanceIdentifier entryKey, CompositeNode entryData) {
this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
}
this.putOperationalData(entryKey, entryData);
break;
- default :
+ default:
LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
}
}
}
- private void putCompositeNodeData(InstanceIdentifier entryKey, CompositeNode entryData, String dataStoreIdentifier) {
+ private void putCompositeNodeData(InstanceIdentifier entryKey, CompositeNode entryData,
+ String dataStoreIdentifier) {
this.putData(entryKey, entryData, dataStoreIdentifier);
-
+
for (Node<?> child : entryData.getChildren()) {
- InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType()).toInstance();
+ InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType())
+ .toInstance();
if (child instanceof CompositeNode) {
DataSchemaNode subSchema = schemaNodeFor(subEntryId);
CompositeNode compNode = (CompositeNode) child;
if (subSchema instanceof ListSchemaNode) {
ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
- Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema, (CompositeNode) child);
+ Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema,
+ (CompositeNode) child);
if (mapOfSubValues != null) {
- instanceId = InstanceIdentifier.builder(entryKey).nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
+ instanceId = InstanceIdentifier.builder(entryKey)
+ .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
}
- }
- else if (subSchema instanceof ContainerSchemaNode) {
+ } else if (subSchema instanceof ContainerSchemaNode) {
ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
instanceId = InstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
}
}
}
- private Map<QName, Object> getValuesFromListSchema (ListSchemaNode listSchema, CompositeNode entryData) {
+ private Map<QName, Object> getValuesFromListSchema(ListSchemaNode listSchema, CompositeNode entryData) {
List<QName> keyDef = listSchema.getKeyDefinition();
- if (keyDef != null && ! keyDef.isEmpty()) {
+ if (keyDef != null && !keyDef.isEmpty()) {
Map<QName, Object> map = new HashMap<QName, Object>();
for (QName key : keyDef) {
List<Node<?>> data = entryData.get(key);
- if (data != null && ! data.isEmpty()) {
+ if (data != null && !data.isEmpty()) {
for (Node<?> nodeData : data) {
if (nodeData instanceof SimpleNode<?>) {
map.put(key, data.get(0).getValue());
import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
-public class SchemaAwareRpcBroker implements RpcRouter, Identifiable<String> {
+public class SchemaAwareRpcBroker implements RpcRouter, Identifiable<String>, RoutedRpcDefaultImplementation {
private static final Logger LOG = LoggerFactory.getLogger(SchemaAwareRpcBroker.class);
private final ConcurrentMap<QName, RpcImplementation> implementations = new ConcurrentHashMap<>();
private RpcImplementation defaultImplementation;
private SchemaContextProvider schemaProvider;
+ private RoutedRpcDefaultImplementation defaultDelegate;
public SchemaAwareRpcBroker(String identifier, SchemaContextProvider schemaProvider) {
super();
this.schemaProvider = schemaProvider;
}
+ public RoutedRpcDefaultImplementation getRoutedRpcDefaultDelegate() {
+ return defaultDelegate;
+ }
+
@Override
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultDelegate) {
+ this.defaultDelegate = defaultDelegate;
+ }
+
+ @Override
public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
checkArgument(rpcType != null, "RPC Type should not be null");
checkArgument(implementation != null, "RPC Implementatoin should not be null");
return ret;
}
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
+ checkState(defaultDelegate != null);
+ return defaultDelegate.invokeRpc(rpc, identifier, input);
+ }
+
private static abstract class RoutingStrategy implements Identifiable<QName> {
private final QName identifier;
SimpleNode<?> routeContainer = inputContainer.getFirstSimpleByName(strategy.getLeaf());
checkArgument(routeContainer != null, "Leaf %s must be set with value", strategy.getLeaf());
Object route = routeContainer.getValue();
+ checkArgument(route instanceof InstanceIdentifier);
RpcImplementation potential = null;
if (route != null) {
RoutedRpcRegImpl potentialReg = implementations.get(route);
}
}
if (potential == null) {
- potential = defaultDelegate;
+ return router.invokeRpc(rpc, (InstanceIdentifier) route, input);
}
checkState(potential != null, "No implementation is available for rpc:%s path:%s", rpc, route);
return potential.invokeRpc(rpc, input);
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
-import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
-import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
+import org.opendaylight.controller.sal.core.api.*;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
return getDelegate().addRoutedRpcImplementation(rpcType, implementation);
}
- @Override
+ @Override
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
+ getDelegate().setRoutedRpcDefaultDelegate(defaultImplementation);
+ }
+
+ @Override
public <L extends RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> ListenerRegistration<L> registerRouteChangeListener(L listener) {
return getDelegate().registerRouteChangeListener(listener);
}
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-broker-impl</artifactId>
- <version>1.1-SNAPSHOT</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<id>sal-remote</id>
import org.opendaylight.controller.sal.connector.remoterpc.*;
import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.osgi.framework.BundleContext;
/**
ClientImpl clientImpl = new ClientImpl();
- RoutingTableProvider provider = new RoutingTableProvider(bundleContext,serverImpl);
+ RoutingTableProvider provider = new RoutingTableProvider(bundleContext);//,serverImpl);
- RemoteRpcProvider facade = new RemoteRpcProvider(serverImpl, clientImpl);
-
- facade.setRoutingTableProvider(provider );
-
- broker.registerProvider(facade, bundleContext);
- return facade;
- }
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
+ facade.setRoutingTableProvider(provider );
+ facade.setContext(bundleContext);
+ facade.setRpcProvisionRegistry((RpcProvisionRegistry) broker);
+
+ broker.registerProvider(facade, bundleContext);
+ return facade;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
}
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
+import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zeromq.ZMQ;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
this.routingTableProvider = routingTableProvider;
}
- @Override
- public Set<QName> getSupportedRpcs(){
- //TODO: Find the entries from routing table
- return Collections.emptySet();
- }
-
@Override
public void start() {/*NOOPS*/}
* @param input payload for the remote service
* @return
*/
- @Override
public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
+ RouteIdentifierImpl routeId = new RouteIdentifierImpl();
+ routeId.setType(rpc);
+
+ String address = lookupRemoteAddressForGlobalRpc(routeId);
+ return sendMessage(input, routeId, address);
+ }
+
+ /**
+ * Finds remote server that can execute this routed rpc and sends a message to it
+ * requesting execution.
+ * The call blocks until a response from remote server is received. Its upto
+ * the client of this API to implement a timeout functionality.
+ *
+ * @param rpc
+ * rpc to be called
+ * @param identifier
+ * instance identifier on which rpc is to be executed
+ * @param input
+ * payload
+ * @return
+ */
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
RouteIdentifierImpl routeId = new RouteIdentifierImpl();
routeId.setType(rpc);
+ routeId.setRoute(identifier);
+
+ String address = lookupRemoteAddressForRpc(routeId);
- String address = lookupRemoteAddress(routeId);
+ return sendMessage(input, routeId, address);
+ }
+ private RpcResult<CompositeNode> sendMessage(CompositeNode input, RouteIdentifierImpl routeId, String address) {
Message request = new Message.MessageBuilder()
.type(Message.MessageType.REQUEST)
.sender(Context.getInstance().getLocalUri())
collectErrors(e, errors);
return Rpcs.getRpcResult(false, null, errors);
}
-
}
/**
* @param routeId route identifier
* @return remote network address
*/
- private String lookupRemoteAddress(RpcRouter.RouteIdentifier routeId){
+ private String lookupRemoteAddressForGlobalRpc(RpcRouter.RouteIdentifier routeId){
checkNotNull(routeId, "route must not be null");
- Optional<RoutingTable<String, String>> routingTable = routingTableProvider.getRoutingTable();
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> routingTable = routingTableProvider.getRoutingTable();
checkNotNull(routingTable.isPresent(), "Routing table is null");
- Set<String> addresses = routingTable.get().getRoutes(routeId.toString());
- checkNotNull(addresses, "Address not found for route [%s]", routeId);
- checkState(addresses.size() == 1,
- "Multiple remote addresses found for route [%s], \nonly 1 expected", routeId); //its a global service.
+ String address = null;
+ try {
+ address = routingTable.get().getGlobalRoute(routeId);
+ } catch (RoutingTableException|SystemException e) {
+ _logger.error("Exception caught while looking up remote address " + e);
+ }
+ checkState(address != null, "Address not found for route [%s]", routeId);
+
+ return address;
+ }
+
+ /**
+ * Find address for the given route identifier in routing table
+ * @param routeId route identifier
+ * @return remote network address
+ */
+ private String lookupRemoteAddressForRpc(RpcRouter.RouteIdentifier routeId){
+ checkNotNull(routeId, "route must not be null");
+
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> routingTable = routingTableProvider.getRoutingTable();
+ checkNotNull(routingTable.isPresent(), "Routing table is null");
- String address = addresses.iterator().next();
- checkNotNull(address, "Address not found for route [%s]", routeId);
+ String address = routingTable.get().getLastAddedRoute(routeId);
+ checkState(address != null, "Address not found for route [%s]", routeId);
return address;
}
package org.opendaylight.controller.sal.connector.remoterpc;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
-public interface RemoteRpcClient extends RpcImplementation,AutoCloseable{
-
+public interface RemoteRpcClient extends AutoCloseable{
void setRoutingTableProvider(RoutingTableProvider provider);
package org.opendaylight.controller.sal.connector.remoterpc;
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
+import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
+import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
+import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
-public class RemoteRpcProvider implements
- RemoteRpcServer,
- RemoteRpcClient,
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class RemoteRpcProvider implements
+ RpcImplementation,
+ RoutedRpcDefaultImplementation,
+ AutoCloseable,
Provider {
- private final ServerImpl server;
- private final ClientImpl client;
- private RoutingTableProvider provider;
+ private Logger _logger = LoggerFactory.getLogger(RemoteRpcProvider.class);
- @Override
- public void setRoutingTableProvider(RoutingTableProvider provider) {
- this.provider = provider;
- server.setRoutingTableProvider(provider);
- client.setRoutingTableProvider(provider);
+ private final ServerImpl server;
+ private final ClientImpl client;
+ private RoutingTableProvider routingTableProvider;
+ private final RpcListener listener = new RpcListener();
+ private final RoutedRpcListener routeChangeListener = new RoutedRpcListener();
+ private ProviderSession brokerSession;
+ private RpcProvisionRegistry rpcProvisionRegistry;
+ private BundleContext context;
+ private ServiceTracker clusterTracker;
+
+ public RemoteRpcProvider(ServerImpl server, ClientImpl client) {
+ this.server = server;
+ this.client = client;
+ }
+
+ public void setRoutingTableProvider(RoutingTableProvider provider) {
+ this.routingTableProvider = provider;
+ client.setRoutingTableProvider(provider);
+ }
+
+ public void setContext(BundleContext context){
+ this.context = context;
+ }
+
+ public void setRpcProvisionRegistry(RpcProvisionRegistry rpcProvisionRegistry){
+ this.rpcProvisionRegistry = rpcProvisionRegistry;
+ }
+
+ @Override
+ public void onSessionInitiated(ProviderSession session) {
+ brokerSession = session;
+ server.setBrokerSession(session);
+ start();
+ }
+
+ @Override
+ public Set<QName> getSupportedRpcs() {
+ //TODO: Ask Tony if we need to get this from routing table
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
+ return client.invokeRpc(rpc, input);
+ }
+
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
+ return client.invokeRpc(rpc, identifier, input);
+ }
+
+ public void start() {
+ server.start();
+ client.start();
+ brokerSession.addRpcRegistrationListener(listener);
+ rpcProvisionRegistry.setRoutedRpcDefaultDelegate(this);
+ rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener);
+
+ announceSupportedRpcs();
+ announceSupportedRoutedRpcs();
+ }
+
+ @Override
+ public void close() throws Exception {
+ unregisterSupportedRpcs();
+ unregisterSupportedRoutedRpcs();
+ server.close();
+ client.close();
+ }
+
+ public void stop() {
+ server.stop();
+ client.stop();
+ }
+
+ /**
+ * Add all the locally registered RPCs in the clustered routing table
+ */
+ private void announceSupportedRpcs(){
+ Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
+ for (QName rpc : currentlySupported) {
+ listener.onRpcImplementationAdded(rpc);
}
-
- @Override
- public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
- return client.invokeRpc(rpc, input);
+ }
+
+ /**
+ * Add all the locally registered Routed RPCs in the clustered routing table
+ */
+ private void announceSupportedRoutedRpcs(){
+
+ //TODO: announce all routed RPCs as well
+
+ }
+
+ /**
+ * Un-Register all the supported RPCs from clustered routing table
+ */
+ private void unregisterSupportedRpcs(){
+ Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
+ //TODO: remove all routed RPCs as well
+ for (QName rpc : currentlySupported) {
+ listener.onRpcImplementationRemoved(rpc);
}
-
+ }
+
+ /**
+ * Un-Register all the locally supported Routed RPCs from clustered routing table
+ */
+ private void unregisterSupportedRoutedRpcs(){
+
+ //TODO: remove all routed RPCs as well
+
+ }
+
+ private RoutingTable<RpcRouter.RouteIdentifier, String> getRoutingTable(){
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> routingTable =
+ routingTableProvider.getRoutingTable();
+
+ checkNotNull(routingTable.isPresent(), "Routing table is null");
+
+ return routingTable.get();
+ }
+
+ /**
+ * Listener for rpc registrations in broker
+ */
+ private class RpcListener implements RpcRegistrationListener {
+
@Override
- public Set<QName> getSupportedRpcs() {
- return client.getSupportedRpcs();
- }
-
-
- public RemoteRpcProvider(ServerImpl server, ClientImpl client) {
- this.server = server;
- this.client = client;
- }
-
- public void setBrokerSession(ProviderSession session) {
- server.setBrokerSession(session);
- }
-// public void setServerPool(ExecutorService serverPool) {
-// server.setServerPool(serverPool);
-// }
- public void start() {
- //when listener was being invoked and addRPCImplementation was being
- //called the client was null.
- server.setClient(client);
- server.start();
- client.start();
+ public void onRpcImplementationAdded(QName rpc) {
+
+ _logger.debug("Adding registration for [{}]", rpc);
+ RouteIdentifierImpl routeId = new RouteIdentifierImpl();
+ routeId.setType(rpc);
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+ try {
+ routingTable.addGlobalRoute(routeId, server.getServerAddress());
+ _logger.debug("Route added [{}-{}]", routeId, server.getServerAddress());
+
+ } catch (RoutingTableException | SystemException e) {
+ //TODO: This can be thrown when route already exists in the table. Broker
+ //needs to handle this.
+ _logger.error("Unhandled exception while adding global route to routing table [{}]", e);
+
+ }
}
-
@Override
- public Collection<ProviderFunctionality> getProviderFunctionality() {
- // TODO Auto-generated method stub
- return null;
+ public void onRpcImplementationRemoved(QName rpc) {
+
+ _logger.debug("Removing registration for [{}]", rpc);
+ RouteIdentifierImpl routeId = new RouteIdentifierImpl();
+ routeId.setType(rpc);
+
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+
+ try {
+ routingTable.removeGlobalRoute(routeId);
+ } catch (RoutingTableException | SystemException e) {
+ _logger.error("Route delete failed {}", e);
+ }
}
-
-
+ }
+
+ /**
+ * Listener for Routed Rpc registrations in broker
+ */
+ private class RoutedRpcListener
+ implements RouteChangeListener<RpcRoutingContext, InstanceIdentifier> {
+
+ /**
+ *
+ * @param routeChange
+ */
@Override
- public void onSessionInitiated(ProviderSession session) {
- server.setBrokerSession(session);
- start();
+ public void onRouteChange(RouteChange<RpcRoutingContext, InstanceIdentifier> routeChange) {
+ Map<RpcRoutingContext, Set<InstanceIdentifier>> announcements = routeChange.getAnnouncements();
+ announce(getRouteIdentifiers(announcements));
+
+ Map<RpcRoutingContext, Set<InstanceIdentifier>> removals = routeChange.getRemovals();
+ remove(getRouteIdentifiers(removals));
+ }
+
+ /**
+ *
+ * @param announcements
+ */
+ private void announce(Set<RpcRouter.RouteIdentifier> announcements) {
+ _logger.debug("Announcing [{}]", announcements);
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+ try {
+ routingTable.addRoutes(announcements, server.getServerAddress());
+ } catch (RoutingTableException | SystemException e) {
+ _logger.error("Route announcement failed {}", e);
+ }
}
-
-
- public void close() throws Exception {
- server.close();
- client.close();
+
+ /**
+ *
+ * @param removals
+ */
+ private void remove(Set<RpcRouter.RouteIdentifier> removals){
+ _logger.debug("Removing [{}]", removals);
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+ try {
+ routingTable.removeRoutes(removals, server.getServerAddress());
+ } catch (RoutingTableException | SystemException e) {
+ _logger.error("Route removal failed {}", e);
+ }
}
- @Override
- public void stop() {
- server.stop();
- client.stop();
+ /**
+ *
+ * @param changes
+ * @return
+ */
+ private Set<RpcRouter.RouteIdentifier> getRouteIdentifiers(Map<RpcRoutingContext, Set<InstanceIdentifier>> changes) {
+ RouteIdentifierImpl routeId = null;
+ Set<RpcRouter.RouteIdentifier> routeIdSet = new HashSet<RpcRouter.RouteIdentifier>();
+
+ for (RpcRoutingContext context : changes.keySet()){
+ routeId = new RouteIdentifierImpl();
+ routeId.setType(context.getRpc());
+ routeId.setContext(context.getContext());
+
+ for (InstanceIdentifier instanceId : changes.get(context)){
+ routeId.setRoute(instanceId);
+ routeIdSet.add(routeId);
+ }
+ }
+ return routeIdSet;
}
+
+
+
+ }
+
}
package org.opendaylight.controller.sal.connector.remoterpc;
import com.google.common.base.Optional;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
+import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.connector.remoterpc.impl.RoutingTableImpl;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
private RoutingTableImpl routingTableImpl = null;
- final private RouteChangeListener routeChangeListener;
+ //final private RouteChangeListener routeChangeListener;
- public RoutingTableProvider(BundleContext ctx,RouteChangeListener rcl) {
+ public RoutingTableProvider(BundleContext ctx){//,RouteChangeListener rcl) {
@SuppressWarnings("rawtypes")
ServiceTracker<RoutingTable, RoutingTable> rawTracker = new ServiceTracker<>(ctx, RoutingTable.class, null);
tracker = rawTracker;
tracker.open();
- routeChangeListener = rcl;
+ //routeChangeListener = rcl;
}
- public Optional<RoutingTable<String, String>> getRoutingTable() {
+ public Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> getRoutingTable() {
@SuppressWarnings("unchecked")
- RoutingTable<String,String> tracked = tracker.getService();
+ RoutingTable<RpcRouter.RouteIdentifier,String> tracked = tracker.getService();
if(tracked instanceof RoutingTableImpl){
if(routingTableImpl != tracked){
routingTableImpl= (RoutingTableImpl)tracked;
- routingTableImpl.setRouteChangeListener(routeChangeListener);
+ //routingTableImpl.setRouteChangeListener(routeChangeListener);
}
}
import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import org.opendaylight.yangtools.yang.common.QName;
import static com.google.common.base.Preconditions.checkState;
/**
- * ZeroMq based implementation of RpcRouter. It implements RouteChangeListener of RoutingTable
- * so that it gets route change notifications from routing table.
+ * ZeroMq based implementation of RpcRouter.
*/
-public class ServerImpl implements RemoteRpcServer, RouteChangeListener<String, String> {
+public class ServerImpl implements RemoteRpcServer {
private Logger _logger = LoggerFactory.getLogger(ServerImpl.class);
private ProviderSession brokerSession;
private ZMQ.Context context;
- private final RpcListener listener = new RpcListener();
-
private final String HANDLER_INPROC_ADDRESS = "inproc://rpc-request-handler";
private final int HANDLER_WORKER_COUNT = 2;
private final int HWM = 200;//high water mark on sockets
private String serverAddress;
private int port;
- private ClientImpl client;
-
- private RoutingTableProvider routingTableProvider;
-
public static enum State {
STARTING, STARTED, STOPPED;
}
this.port = port;
}
- public RoutingTableProvider getRoutingTableProvider() {
- return routingTableProvider;
- }
-
- public void setRoutingTableProvider(RoutingTableProvider routingTableProvider) {
- this.routingTableProvider = routingTableProvider;
- }
-
- public ClientImpl getClient(){
- return this.client;
- }
-
- public void setClient(ClientImpl client) {
- this.client = client;
- }
-
public State getStatus() {
return this.status;
}
remoteServices = new HashSet<QName>();//
serverPool = Executors.newSingleThreadExecutor();//main server thread
serverPool.execute(receive()); // Start listening rpc requests
- brokerSession.addRpcRegistrationListener(listener);
-
- announceLocalRpcs();
-
- registerRemoteRpcs();
status = State.STARTED;
_logger.info("Remote RPC Server started [{}]", getServerAddress());
if (State.STOPPED == this.getStatus()) return; //do nothing
- unregisterLocalRpcs();
-
if (serverPool != null)
serverPool.shutdown();
/**
* Closes ZMQ Context. It tries to gracefully terminate the context. If
- * termination takes more than a second, its forcefully shutdown.
+ * termination takes more than 5 seconds, its forcefully shutdown.
*/
private void closeZmqContext() {
ExecutorService exec = Executors.newSingleThreadExecutor();
};
}
- /**
- * Register the remote RPCs from the routing table into broker
- */
- private void registerRemoteRpcs(){
- Optional<RoutingTable<String, String>> routingTableOptional = routingTableProvider.getRoutingTable();
-
- Preconditions.checkState(routingTableOptional.isPresent(), "Routing table is absent");
-
- Set<Map.Entry> remoteRoutes =
- routingTableProvider.getRoutingTable().get().getAllRoutes();
-
- //filter out all entries that contains local address
- //we dont want to register local RPCs as remote
- Predicate<Map.Entry> notLocalAddressFilter = new Predicate<Map.Entry>(){
- public boolean apply(Map.Entry remoteRoute){
- return !getServerAddress().equalsIgnoreCase((String)remoteRoute.getValue());
- }
- };
-
- //filter the entries created by current node
- Set<Map.Entry> filteredRemoteRoutes = Sets.filter(remoteRoutes, notLocalAddressFilter);
-
- for (Map.Entry route : filteredRemoteRoutes){
- onRouteUpdated((String) route.getKey(), "");//value is not needed by broker
- }
- }
-
- /**
- * Un-Register the local RPCs from the routing table
- */
- private void unregisterLocalRpcs(){
- Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
- for (QName rpc : currentlySupported) {
- listener.onRpcImplementationRemoved(rpc);
- }
- }
-
- /**
- * Publish all the locally registered RPCs in the routing table
- */
- private void announceLocalRpcs(){
- Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
- for (QName rpc : currentlySupported) {
- listener.onRpcImplementationAdded(rpc);
- }
- }
-
- /**
- * @param key
- * @param value
- */
- @Override
- public void onRouteUpdated(String key, String value) {
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- try {
- _logger.debug("Updating key/value {}-{}", key, value);
- brokerSession.addRpcImplementation(
- (QName) rId.fromString(key).getType(), client);
-
- //TODO: Check with Tony for routed rpc
- //brokerSession.addRoutedRpcImplementation((QName) rId.fromString(key).getRoute(), client);
- } catch (Exception e) {
- _logger.info("Route update failed {}", e);
- }
- }
-
- /**
- * @param key
- */
- @Override
- public void onRouteDeleted(String key) {
- //TODO: Broker session needs to be updated to support this
- throw new UnsupportedOperationException();
- }
-
/**
* Finds IPv4 address of the local VM
* TODO: This method is non-deterministic. There may be more than one IPv4 address. Cant say which
return null;
}
- /**
- * Listener for rpc registrations
- */
- private class RpcListener implements RpcRegistrationListener {
-
- @Override
- public void onRpcImplementationAdded(QName name) {
-
- //if the service name exists in the set, this notice
- //has bounced back from the broker. It should be ignored
- if (remoteServices.contains(name))
- return;
-
- _logger.debug("Adding registration for [{}]", name);
- RouteIdentifierImpl routeId = new RouteIdentifierImpl();
- routeId.setType(name);
-
- RoutingTable<String, String> routingTable = getRoutingTable();
-
- try {
- routingTable.addGlobalRoute(routeId.toString(), getServerAddress());
- _logger.debug("Route added [{}-{}]", name, getServerAddress());
-
- } catch (RoutingTableException | SystemException e) {
- //TODO: This can be thrown when route already exists in the table. Broker
- //needs to handle this.
- _logger.error("Unhandled exception while adding global route to routing table [{}]", e);
-
- }
- }
-
- @Override
- public void onRpcImplementationRemoved(QName name) {
-
- _logger.debug("Removing registration for [{}]", name);
- RouteIdentifierImpl routeId = new RouteIdentifierImpl();
- routeId.setType(name);
-
- RoutingTable<String, String> routingTable = getRoutingTable();
-
- try {
- routingTable.removeGlobalRoute(routeId.toString());
- } catch (RoutingTableException | SystemException e) {
- _logger.error("Route delete failed {}", e);
- }
- }
-
- private RoutingTable<String, String> getRoutingTable(){
- Optional<RoutingTable<String, String>> routingTable =
- routingTableProvider.getRoutingTable();
-
- checkNotNull(routingTable.isPresent(), "Routing table is null");
-
- return routingTable.get();
- }
- }
-
- /*
- * Listener for Route changes in broker. Broker notifies this listener in the event
- * of any change (add/delete). Listener then updates the routing table.
- */
- private class BrokerRouteChangeListener
- implements org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener<RpcRoutingContext, InstanceIdentifier>{
-
- @Override
- public void onRouteChange(RouteChange<RpcRoutingContext, InstanceIdentifier> routeChange) {
-
- }
- }
-
}
public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>,Serializable {
- transient ObjectMapper mapper = new ObjectMapper();
-
private QName context;
private QName type;
private InstanceIdentifier route;
public void setRoute(InstanceIdentifier route) {
this.route = route;
}
-
- @Override
- public String toString() {
- try {
- return mapper.writeValueAsString(this);
- } catch (Throwable e) {
- //do nothing
- }
-
- return super.toString();
- }
-
- public RpcRouter.RouteIdentifier fromString(String input)
- throws Exception {
-
- JsonNode root = mapper.readTree(input);
- this.context = parseQName(root.get("context"));
- this.type = parseQName(root.get("type"));
-
- return this;
- }
-
- private QName parseQName(JsonNode node){
- if (node == null) return null;
-
- String namespace = (node.get("namespace") != null) ?
- node.get("namespace").asText() : "";
-
- String localName = (node.get("localName") != null) ?
- node.get("localName").asText() : "";
-
- URI uri = URI.create(namespace);
- return new QName(uri, localName);
- }
}
identity remote-zeromq-rpc-server {
base config:module-type;
- config:provided-service remote-rpc-server;
- config:provided-service remote-rpc-client;
config:java-name-prefix ZeroMQServer;
}
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
//mock routing table
routingTableProvider = mock(RoutingTableProvider.class);
- RoutingTable<String, String> mockRoutingTable = new MockRoutingTable<String, String>();
- Optional<RoutingTable<String, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
+ RoutingTable<RpcRouter.RouteIdentifier, String> mockRoutingTable = new MockRoutingTable<String, String>();
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
when(routingTableProvider.getRoutingTable()).thenReturn(optionalRoutingTable);
//mock ClientRequestHandler
}
- @Test
+ //@Test
public void invokeRpc_NormalCall_ShouldReturnSuccess() throws Exception {
when(mockHandler.handle(any(Message.class))).
Assert.assertNull(result.getResult());
}
- @Test
+ //@Test
public void invokeRpc_HandlerThrowsException_ShouldReturnError() throws Exception {
when(mockHandler.handle(any(Message.class))).
}
+ @Override
+ public void addRoutes(Set set, Object o) throws RoutingTableException, SystemException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void removeRoutes(Set set, Object o) throws RoutingTableException, SystemException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
@Override
public void removeGlobalRoute(Object o) throws RoutingTableException, SystemException {
}
+ @Override
+ public Object getGlobalRoute(Object o) throws RoutingTableException, SystemException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
@Override
public Set getRoutes(Object o) {
Set<String> routes = new HashSet<String>();
}
@Override
- public Set<Map.Entry> getAllRoutes() {
- return Collections.emptySet();
+ public Object getLastAddedRoute(Object o) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
}
- @Override
- public Object getARoute(Object o) {
- return null;
- }
+// @Override
+// public Set<Map.Entry> getAllRoutes() {
+// return Collections.emptySet();
+// }
- @Override
- public void registerRouteChangeListener(RouteChangeListener routeChangeListener) {
-
- }
+// @Override
+// public Object getARoute(Object o) {
+// return null;
+// }
}
--- /dev/null
+package org.opendaylight.controller.sal.connector.remoterpc;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RemoteRpcProviderTest {
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testSetRoutingTableProvider() throws Exception {
+
+ }
+
+ @Test
+ public void testOnSessionInitiated() throws Exception {
+
+ }
+
+ @Test
+ public void testGetSupportedRpcs() throws Exception {
+
+ }
+
+ @Test
+ public void testGetProviderFunctionality() throws Exception {
+
+ }
+
+ @Test
+ public void testInvokeRpc() throws Exception {
+
+ }
+
+ @Test
+ public void testInvokeRoutedRpc() throws Exception {
+
+ }
+
+ @Test
+ public void testStart() throws Exception {
+
+ }
+
+ @Test
+ public void testClose() throws Exception {
+
+ }
+
+ @Test
+ public void testStop() throws Exception {
+
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.sal.connector.remoterpc;
-
-import java.net.URI;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import org.junit.Assert;
-import org.junit.Test;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RouteIdentifierImplTest {
-
- Logger _logger = LoggerFactory.getLogger(RouteIdentifierImplTest.class);
-
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "heartbeat");
-
- @Test
- public void testToString() throws Exception {
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- rId.setType(QNAME);
-
- _logger.debug(rId.toString());
-
- Assert.assertTrue(true);
-
- }
-
- @Test
- public void testFromString() throws Exception {
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- rId.setType(QNAME);
-
- String s = rId.toString();
- _logger.debug("serialized route: {}", s);
-
- RpcRouter.RouteIdentifier ref = new RouteIdentifierImpl().fromString(s);
- _logger.debug("deserialized route: {}", ref);
-
- Assert.assertTrue(true);
- }
-
- @Test(expected = JsonParseException.class)
- public void testFromInvalidString() throws Exception {
- String invalidInput = "aklhdgadfa;;;;;;;]]]]=]ag" ;
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- rId.fromString(invalidInput);
-
- _logger.debug("" + rId);
- Assert.assertTrue(true);
- }
-}
import com.google.common.base.Optional;
import junit.framework.Assert;
import org.junit.*;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
import org.opendaylight.controller.sal.connector.remoterpc.utils.MessagingUtil;
import org.opendaylight.controller.sal.core.api.Broker;
server = new ServerImpl(port);
server.setBrokerSession(brokerSession);
- server.setRoutingTableProvider(routingTableProvider);
- RoutingTable<String, String> mockRoutingTable = new MockRoutingTable<String, String>();
- Optional<RoutingTable<String, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
+ RoutingTable<RpcRouter.RouteIdentifier, String> mockRoutingTable = new MockRoutingTable<String, String>();
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
when(routingTableProvider.getRoutingTable()).thenReturn(optionalRoutingTable);
when(brokerSession.addRpcRegistrationListener(listener)).thenReturn(null);
Assert.assertEquals(ServerImpl.State.STOPPED, server.getStatus());
}
- @Test
- public void getRoutingTableProvider_Call_ShouldReturnRoutingTable() throws Exception {
- Assert.assertNotNull(server.getRoutingTableProvider());
- }
-
@Test
public void getBrokerSession_Call_ShouldReturnBrokerSession() throws Exception {
Optional<Broker.ProviderSession> mayBeBroker = server.getBrokerSession();
</Export-Package>
<Import-Package>
com.sun.jersey.spi.container.servlet,
- org.codehaus.jackson.annotate,
+ !org.codehaus.jackson.annotate,
javax.ws.rs,
javax.ws.rs.core,
javax.xml.bind,
javax.xml.bind.annotation,
org.slf4j,
org.apache.catalina.filters,
- org.codehaus.jackson.jaxrs,
+ !org.codehaus.jackson.jaxrs,
org.opendaylight.controller.sample.zeromq.provider,
org.opendaylight.controller.sample.zeromq.consumer,
org.opendaylight.controller.sal.utils,
@Consumes({Draft02.MediaTypes.OPERATION+JSON, Draft02.MediaTypes.OPERATION+XML,
Draft02.MediaTypes.DATA+JSON, Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public StructuredData invokeRpc(@PathParam("identifier") String identifier, CompositeNode payload);
+ public StructuredData invokeRpc(@Encoded @PathParam("identifier") String identifier, CompositeNode payload);
@POST
@Path("/operations/{identifier:.+}")
@Produces({Draft02.MediaTypes.OPERATION+JSON, Draft02.MediaTypes.OPERATION+XML,
Draft02.MediaTypes.DATA+JSON, Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public StructuredData invokeRpc(@PathParam("identifier") String identifier, @DefaultValue("") String noPayload);
+ public StructuredData invokeRpc(@Encoded @PathParam("identifier") String identifier, @DefaultValue("") String noPayload);
@GET
@Path("/config/{identifier:.+}")
import org.opendaylight.yangtools.yang.model.api.Module
import org.opendaylight.yangtools.yang.model.api.RpcDefinition
import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
import org.slf4j.LoggerFactory
import static com.google.common.base.Preconditions.*
import static javax.ws.rs.core.Response.Status.*
-class ControllerContext implements SchemaServiceListener {
+class ControllerContext implements SchemaContextListener {
val static LOG = LoggerFactory.getLogger(ControllerContext)
val static ControllerContext INSTANCE = new ControllerContext
val static NULL_VALUE = "null"
private def toUriString(Object object) {
if(object === null) return "";
-// return object.toString.replace("/",URI_SLASH_PLACEHOLDER)
return URLEncoder.encode(object.toString,URI_ENCODING_CHAR_SET)
}
decodedPathArgs.add(URLDecoder.decode(pathArg, URI_ENCODING_CHAR_SET))
}
return decodedPathArgs
+ }
+
+ def urlPathArgDecode(String pathArg) {
+ if (pathArg !== null) {
+ return URLDecoder.decode(pathArg, URI_ENCODING_CHAR_SET)
+ }
+ return null
}
}
}
override invokeRpc(String identifier, CompositeNode payload) {
- val rpc = identifier.rpcDefinition
- if (rpc === null) {
- throw new ResponseException(NOT_FOUND, "RPC does not exist.");
- }
+ val rpc = resolveIdentifierInInvokeRpc(identifier)
if (rpc.QName.namespace.toString == SAL_REMOTE_NAMESPACE && rpc.QName.localName == SAL_REMOTE_RPC_SUBSRCIBE) {
val value = normalizeNode(payload, rpc.input, null)
val pathNode = value?.getFirstSimpleByName(QName.create(rpc.QName, "path"))
if (!noPayload.nullOrEmpty) {
throw new ResponseException(UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
}
- return callRpc(identifier.rpcDefinition, null)
+ val rpc = resolveIdentifierInInvokeRpc(identifier)
+ return callRpc(rpc, null)
+ }
+
+ def resolveIdentifierInInvokeRpc(String identifier) {
+ if (identifier.indexOf("/") === -1) {
+ val identifierDecoded = identifier.urlPathArgDecode
+ val rpc = identifierDecoded.rpcDefinition
+ if (rpc !== null) {
+ return rpc
+ }
+ throw new ResponseException(NOT_FOUND, "RPC does not exist.");
+ }
+ val slashErrorMsg = String.format("Identifier %n%s%ncan't contain slash character (/). +
+ If slash is part of identifier name then use %2F placeholder.",identifier)
+ throw new ResponseException(NOT_FOUND, slashErrorMsg);
}
private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
+<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<module>toaster</module>
<module>toaster-consumer</module>
<module>toaster-provider</module>
- </modules>
+ </modules>
<profiles>
<profile>
<activeByDefault>false</activeByDefault>
</activation>
<modules>
- <!--module>toaster-it</module -->
+ <module>toaster-it</module>
</modules>
</profile>
</profiles>
<groupId>org.opendaylight.controller.samples</groupId>
-</project>
+</project>
\ No newline at end of file
<tag>HEAD</tag>
</scm>
+ <properties>
+ <sal-binding-api.version>1.1-SNAPSHOT</sal-binding-api.version>
+ <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+
+ </properties>
+
<build>
<plugins>
<plugin>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Export-Package>org.opendaylight.controller.sample.toaster.provider.api</Export-Package>
- <Private-Package>org.opendaylight.controller.sample.toaster.provider.impl</Private-Package>
- <Bundle-Activator>org.opendaylight.controller.sample.toaster.provider.impl.ToastConsumerImpl</Bundle-Activator>
+ <Export-Package>
+ org.opendaylight.controller.sample.toaster.provider.api,
+ org.opendaylight.controller.config.yang.toaster-consumer,
+ </Export-Package>
+ <Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ </codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>
+ urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang
+ </namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${jmxGeneratorPath}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-consumer-impl yang module local name: toaster-consumer-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:31:30 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_consumer.impl;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
+import org.opendaylight.controller.sample.toaster.provider.impl.ToastConsumerImpl;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastDone;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+*
+*/
+public final class ToasterConsumerModule extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModule
+ {
+
+ public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ ToasterConsumerModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation(){
+ // No need to validate dependencies, since all dependencies have mandatory true flag in yang
+ // config-subsystem will perform the validation
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ ToasterService toasterService = getRpcRegistryDependency().getRpcService(ToasterService.class);
+
+ final ToastConsumerImpl consumer = new ToastConsumerImpl(toasterService);
+ final Registration<NotificationListener<ToastDone>> notificationRegistration = getNotificationServiceDependency()
+ .registerNotificationListener(ToastDone.class, consumer);
+
+ final ToasterConsumerRuntimeRegistration runtimeRegistration = getRootRuntimeBeanRegistratorWrapper().register(consumer);
+
+ final class AutoCloseableToastConsumer implements AutoCloseable, ToastConsumer {
+
+ @Override
+ public void close() throws Exception {
+ runtimeRegistration.close();
+ notificationRegistration.close();
+ }
+
+ @Override
+ public boolean createToast(Class<? extends ToastType> type, int doneness) {
+ return consumer.createToast(type, doneness);
+ }
+ }
+
+ return new AutoCloseableToastConsumer();
+ }
+}
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-consumer-impl yang module local name: toaster-consumer-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:31:30 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_consumer.impl;
+
+/**
+*
+*/
+public class ToasterConsumerModuleFactory extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModuleFactory
+{
+
+
+}
*/
package org.opendaylight.controller.sample.toaster.provider.impl;
-import java.util.Hashtable;
import java.util.concurrent.ExecutionException;
-import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
-import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.config.yang.config.toaster_consumer.impl.ToasterConsumerRuntimeMXBean;
import org.opendaylight.controller.sal.binding.api.NotificationListener;
-import org.opendaylight.controller.sal.binding.api.NotificationService;
import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInputBuilder;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastDone;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.*;
import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ToastConsumerImpl extends AbstractBindingAwareConsumer implements BundleActivator, BindingAwareConsumer, ToastConsumer,
- NotificationListener<ToastDone> {
+public class ToastConsumerImpl implements
+ ToastConsumer,
+ NotificationListener<ToastDone>,ToasterConsumerRuntimeMXBean {
private static final Logger log = LoggerFactory.getLogger(ToastConsumerImpl.class);
private ToasterService toaster;
- private ConsumerContext session;
+ public ToastConsumerImpl(ToasterService toaster) {
+ this.toaster = toaster;
+ }
@Override
public boolean createToast(Class<? extends ToastType> type, int doneness) {
toastInput.setToasterToastType(type);
try {
- RpcResult<Void> result = getToastService().makeToast(toastInput.build()).get();
+ RpcResult<Void> result = toaster.makeToast(toastInput.build()).get();
if (result.isSuccessful()) {
- log.trace("Toast was successfuly finished");
+ log.trace("Toast was successfully finished");
} else {
- log.warn("Toast was not successfuly finished");
+ log.warn("Toast was not successfully finished");
}
return result.isSuccessful();
} catch (InterruptedException | ExecutionException e) {
- log.warn("Error occured during toast creation");
+ log.warn("Error occurred during toast creation");
}
return false;
}
- @Override
- @Deprecated
- protected void startImpl(BundleContext context) {
- context.registerService(ToastConsumer.class, this, new Hashtable<String,String>());
- }
-
- @Override
- public void onSessionInitialized(ConsumerContext session) {
- this.session = session;
- NotificationService notificationService = session.getSALService(NotificationService.class);
- notificationService.registerNotificationListener(ToastDone.class, this);
- }
-
@Override
public void onNotification(ToastDone notification) {
log.trace("ToastDone Notification Received: {} ",notification.getToastStatus());
-
}
- private ToasterService getToastService() {
- if (toaster == null) {
- toaster = session.getRpcService(ToasterService.class);
- }
- return toaster;
+ @Override
+ public Boolean makeHashBrownToast(Integer doneness) {
+ return createToast(HashBrown.class, doneness);
}
-
}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-consumer-impl {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl";
+ prefix "toaster-consumer-impl";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+
+ import toaster-consumer { prefix toaster-consumer; revision-date 2014-01-31; }
+ import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-consumer impl implementation.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service implementation
+ identity toaster-consumer-impl {
+ base config:module-type;
+ config:provided-service toaster-consumer:toaster-consumer;
+ config:java-name-prefix ToasterConsumer;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case toaster-consumer-impl {
+ when "/config:modules/config:module/config:type = 'toaster-consumer-impl'";
+
+ container rpc-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-rpc-registry;
+ }
+ }
+ }
+
+ container notification-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-notification-service;
+ }
+ }
+ }
+
+ }
+ }
+
+ augment "/config:modules/config:module/config:state" {
+ case toaster-consumer-impl {
+ when "/config:modules/config:module/config:type = 'toaster-consumer-impl'";
+ rpcx:rpc-context-instance "make-hash-brown-toast-rpc";
+ }
+ }
+
+ identity make-hash-brown-toast-rpc;
+
+ rpc make-hash-brown-toast {
+ input {
+ uses rpcx:rpc-context-ref {
+ refine context-instance {
+ rpcx:rpc-context-instance make-hash-brown-toast-rpc;
+ }
+ }
+ leaf doneness {
+ type uint16;
+ }
+ }
+ output {
+ leaf result {
+ type boolean;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-consumer {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer";
+ prefix "toaster-consumer";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-consumer services.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service
+ identity toaster-consumer {
+
+ base "config:service-type";
+
+ config:java-class "org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer";
+ }
+}
\ No newline at end of file
<parent>
<artifactId>sal-samples</artifactId>
<groupId>org.opendaylight.controller.samples</groupId>
- <version>1.0-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</parent>
<artifactId>sample-toaster-it</artifactId>
<scm>
<url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
</scm>
- <properties>
- <exam.version>3.0.0</exam.version>
- <url.version>1.5.0</url.version>
- </properties>
<build>
<plugins>
</execution>
</executions>
</plugin>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <configuration>
- <includes>org.opendaylight.controller.*</includes>
- </configuration>
- <executions>
- <execution>
- <id>pre-test</id>
- <goals>
- <goal>prepare-agent</goal>
- </goals>
- </execution>
- <execution>
- <id>post-test</id>
- <phase>test</phase>
- <goals>
- <goal>report</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
</plugins>
- <pluginManagement>
- <plugins>
- <!--This plugin's configuration is used to store Eclipse
- m2e settings only. It has no influence on the Maven build itself. -->
- <plugin>
- <groupId>org.eclipse.m2e</groupId>
- <artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
- <configuration>
- <lifecycleMappingMetadata>
- <pluginExecutions>
- <pluginExecution>
- <pluginExecutionFilter>
- <groupId>
- org.ops4j.pax.exam
- </groupId>
- <artifactId>
- maven-paxexam-plugin
- </artifactId>
- <versionRange>
- [1.2.4,)
- </versionRange>
- <goals>
- <goal>
- generate-depends-file
- </goal>
- </goals>
- </pluginExecutionFilter>
- <action>
- <ignore></ignore>
- </action>
- </pluginExecution>
- </pluginExecutions>
- </lifecycleMappingMetadata>
- </configuration>
- </plugin>
- </plugins>
- </pluginManagement>
</build>
<dependencies>
<dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>xtend-lib-osgi</artifactId>
- <version>2.4.3</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.samples</groupId>
- <artifactId>sample-toaster</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-it</artifactId>
+ <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster-consumer</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster-provider</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>1.1-SNAPSHOT</version>
</dependency>
+
<dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-container-native</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
<version>${exam.version}</version>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
-
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-manager</artifactId>
- <version>0.2.3-SNAPSHOT</version>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-native</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<version>3.8.1.v20120830-144521</version>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- <version>1.7.2</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- <version>1.0.9</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>1.0.9</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>antlr4-runtime-osgi-nohead</artifactId>
- <version>4.0</version>
- </dependency>
</dependencies>
</project>
*/
package org.opendaylight.controller.sample.toaster.it;
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.*;
+import static org.ops4j.pax.exam.CoreOptions.*;
+
+import javax.inject.Inject;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.config.yang.config.toaster_consumer.impl.ToasterConsumerRuntimeMXBean;
+import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.HashBrown;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.WhiteBread;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
-import org.osgi.framework.BundleContext;
-
-import javax.inject.Inject;
-
-import static org.junit.Assert.assertTrue;
-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.systemProperty;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+import org.ops4j.pax.exam.util.Filter;
import org.ops4j.pax.exam.util.PathUtils;
+import java.lang.management.ManagementFactory;
+
@RunWith(PaxExam.class)
public class ToasterTest {
- public static final String ODL = "org.opendaylight.controller";
- public static final String YANG = "org.opendaylight.yangtools";
- public static final String CONTROLLER = "org.opendaylight.controller";
- public static final String YANGTOOLS = "org.opendaylight.yangtools";
-
-
- public static final String SAMPLE = "org.opendaylight.controller.samples";
+ @Inject
+ @Filter(timeout=60*1000)
+ ToastConsumer toastConsumer;
- @Test
- public void properInitialized() throws Exception {
+ @Configuration
+ public Option[] config() {
+ return options(systemProperty("osgi.console").value("2401"), mavenBundle("org.slf4j", "slf4j-api")
+ .versionAsInProject(), //
+ mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), //
+
+ systemProperty("logback.configurationFile").value(
+ "file:" + PathUtils.getBaseDir()
+ + "/src/test/resources/logback.xml"),
+ mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
+ mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
+ systemProperty("osgi.bundles.defaultStartLevel").value("4"),
- Thread.sleep(500); // Waiting for services to get wired.
+ toasterBundles(),
+ mdSalCoreBundles(),
- assertTrue(consumer.createToast(WhiteBread.class, 5));
+ bindingAwareSalBundles(),
+ configMinumumBundles(),
+ // BASE Models
+ baseModelBundles(),
+ flowCapableModelBundles(),
+ // Set fail if unresolved bundle present
+ systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
+ junitAndMockitoBundles());
}
- @Inject
- BindingAwareBroker broker;
+ private Option toasterBundles() {
+ return new DefaultCompositeOption(
+ mavenBundle("org.opendaylight.controller.samples", "sample-toaster-provider").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.samples", "sample-toaster-consumer").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.samples", "sample-toaster").versionAsInProject()
+ );
+ }
- @Inject
- ToastConsumer consumer;
+ @Test
+ public void testToaster() throws Exception {
- @Inject
- BundleContext ctx;
+ MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
+ ObjectName consumerOn = new ObjectName("org.opendaylight.controller:instanceName=toaster-consumer-impl,type=RuntimeBean,moduleFactoryName=toaster-consumer-impl");
+ ObjectName providerOn = new ObjectName("org.opendaylight.controller:instanceName=toaster-provider-impl,type=RuntimeBean,moduleFactoryName=toaster-provider-impl");
- @Configuration
- public Option[] config() {
- return options(systemProperty("osgi.console").value("2401"),
- systemProperty("logback.configurationFile").value(
- "file:" + PathUtils.getBaseDir()
- + "/src/test/resources/logback.xml"),
- mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), //
- mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), //
- mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
- mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
-
- mavenBundle(ODL, "sal-common").versionAsInProject(), //
- mavenBundle(ODL, "sal-common-api").versionAsInProject(),//
- mavenBundle(ODL, "sal-common-impl").versionAsInProject(), //
- mavenBundle(ODL, "sal-common-util").versionAsInProject(), //
-
- mavenBundle(ODL, "config-api").versionAsInProject(), //
- mavenBundle(ODL, "config-manager").versionAsInProject(), //
- mavenBundle("commons-io", "commons-io").versionAsInProject(),
- mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(),
-
- mavenBundle(CONTROLLER, "sal-binding-api").versionAsInProject(), //
- mavenBundle(CONTROLLER, "sal-binding-config").versionAsInProject(),
- mavenBundle(CONTROLLER, "sal-binding-broker-impl").versionAsInProject(), //
- mavenBundle("org.javassist", "javassist").versionAsInProject(), //
- mavenBundle(CONTROLLER, "sal-common-util").versionAsInProject(), //
-
- mavenBundle(YANGTOOLS, "yang-data-api").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-data-impl").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-model-api").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-model-util").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-parser-api").versionAsInProject(),
- mavenBundle(YANGTOOLS, "yang-parser-impl").versionAsInProject(),
-
-
- mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "binding-model-api").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "binding-generator-util").versionAsInProject(),
- mavenBundle(YANGTOOLS, "yang-parser-impl").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-type-provider").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-generator-api").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-generator-impl").versionAsInProject(),
-
-
- mavenBundle(CONTROLLER, "sal-core-api").versionAsInProject().update(), //
- mavenBundle(CONTROLLER, "sal-broker-impl").versionAsInProject(), //
- mavenBundle(CONTROLLER, "sal-core-spi").versionAsInProject().update(), //
-
- mavenBundle(YANGTOOLS + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), //
-
- mavenBundle(SAMPLE, "sample-toaster").versionAsInProject(), //
- mavenBundle(SAMPLE, "sample-toaster-consumer").versionAsInProject(), //
- mavenBundle(SAMPLE, "sample-toaster-provider").versionAsInProject(), //
- mavenBundle(YANG, "concepts").versionAsInProject(),
- mavenBundle(YANG, "yang-binding").versionAsInProject(), //
- mavenBundle(YANG, "yang-common").versionAsInProject(), //
- mavenBundle(YANG+".thirdparty", "xtend-lib-osgi").versionAsInProject(),
- mavenBundle("com.google.guava", "guava").versionAsInProject(), //
- mavenBundle("org.javassist", "javassist").versionAsInProject(),
- junitBundles()
- );
+ long toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
+ assertEquals(0, toastsMade);
+
+ boolean toasts = true;
+
+ // Make toasts using OSGi service
+ toasts &= toastConsumer.createToast(HashBrown.class, 4);
+ toasts &= toastConsumer.createToast(WhiteBread.class, 8);
+
+ // Make toast using JMX/config-subsystem
+ toasts &= (Boolean)platformMBeanServer.invoke(consumerOn, "makeHashBrownToast", new Object[]{4}, new String[]{Integer.class.getName()});
+
+ Assert.assertTrue("Not all toasts done by " + toastConsumer, toasts);
+
+ // Verify toasts made count on provider via JMX/config-subsystem
+ toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
+ assertEquals(3, toastsMade);
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<persisted-snapshots>
+ <snapshots>
+ <snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27</capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
+ </capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28</capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
+ </capability>
+ <capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>
+ <capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04</capability>
+ <capability>urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
+ </capability>
+ <capability>urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
+ </capability>
+ <capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>
+ <capability>urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
+ </capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16</capability>
+ <capability>urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
+ </capability>
+ <capability>http://netconfcentral.org/ns/toaster?module=toaster&revision=2009-11-20</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer?module=toaster-consumer&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl?module=toaster-consumer-impl&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider?module=toaster-provider&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31</capability>
+
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ prefix:toaster-provider-impl
+ </type>
+ <name>toaster-provider-impl</name>
+
+ <rpc-registry>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+
+ <notification-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl">
+ prefix:toaster-consumer-impl
+ </type>
+ <name>toaster-consumer-impl</name>
+
+ <rpc-registry>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+
+ <notification-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:schema-service-singleton
+ </type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:hash-map-data-store
+ </type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:dom-broker-impl
+ </type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-data-store
+ </type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-broker-impl
+ </type>
+ <name>binding-broker-impl</name>
+ <notification-service
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-data-broker
+ </type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:runtime-generated-mapping
+ </type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-notification-broker
+ </type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-data-broker
+ </type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-broker-osgi-registry
+ </type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ binding:binding-dom-mapping-service
+ </type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:schema-service
+ </type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>
+ /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>
+ /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-data-store
+ </type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>
+ /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <instance>
+ <name>binding-rpc-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-broker-osgi-registry
+ </type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>
+ /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ binding-impl:binding-dom-mapping-service
+ </type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>
+ /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-broker-osgi-registry
+ </type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-data-broker
+ </type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>
+ /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+
+ </configuration>
+ </snapshot>
+
+ </snapshots>
+</persisted-snapshots>
</encoder>
</appender>
- <root level="error">
+
+ <logger name="org.opendaylight.yangtools.yang.parser" level="ERROR"/>
+
+ <root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
</scm>
+ <properties>
+ <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+ <sal-binding-api.version>1.1-SNAPSHOT</sal-binding-api.version>
+ </properties>
+
<build>
<plugins>
<plugin>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Bundle-Activator>org.opendaylight.controller.sample.toaster.provider.ToasterProvider</Bundle-Activator>
+ <Export-Package>
+ org.opendaylight.controller.config.yang.toaster_provider,
+ </Export-Package>
+ <Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ </codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>
+ urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang
+ </namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${jmxGeneratorPath}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-provider-impl yang module local name: toaster-provider-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:05:32 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_provider.impl;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sample.toaster.provider.OpendaylightToaster;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+
+/**
+*
+*/
+public final class ToasterProviderModule extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModule
+ {
+
+ public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ ToasterProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation() {
+ // No need to validate dependencies, since all dependencies have mandatory true flag in yang
+ // config-subsystem will perform the validation for dependencies
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ final OpendaylightToaster opendaylightToaster = new OpendaylightToaster();
+
+ // Register to md-sal
+ opendaylightToaster.setNotificationProvider(getNotificationServiceDependency());
+ final BindingAwareBroker.RpcRegistration<ToasterService> rpcRegistration = getRpcRegistryDependency()
+ .addRpcImplementation(ToasterService.class, opendaylightToaster);
+
+ // Register runtimeBean for toaster statistics via JMX
+ final ToasterProviderRuntimeRegistration runtimeReg = getRootRuntimeBeanRegistratorWrapper().register(
+ opendaylightToaster);
+
+ // Wrap toaster as AutoCloseable and close registrations to md-sal at
+ // close()
+ final class AutoCloseableToaster implements AutoCloseable, ToasterData {
+
+ @Override
+ public void close() throws Exception {
+ rpcRegistration.close();
+ runtimeReg.close();
+ }
+
+ @Override
+ public Toaster getToaster() {
+ return opendaylightToaster.getToaster();
+ }
+ }
+
+ return new AutoCloseableToaster();
+ }
+
+}
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-provider-impl yang module local name: toaster-provider-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:05:32 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_provider.impl;
+
+/**
+*
+*/
+public class ToasterProviderModuleFactory extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModuleFactory
+{
+
+
+}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
import org.opendaylight.controller.sal.common.util.Futures;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class OpendaylightToaster implements ToasterData, ToasterService {
+public class OpendaylightToaster implements ToasterData, ToasterService, ToasterProviderRuntimeMXBean {
private static final Logger log = LoggerFactory.getLogger(OpendaylightToaster.class);
log.trace("Toast: {} doneness: {}", toastType, toastDoneness);
}
+ private final AtomicLong toastsMade = new AtomicLong(0);
+
+ @Override
+ public Long getToastsMade() {
+ return toastsMade.get();
+ }
+
private class MakeToastTask implements Callable<RpcResult<Void>> {
final MakeToastInput toastRequest;
log.trace("Toast Done");
logToastInput(toastRequest);
currentTask = null;
+
+ toastsMade.incrementAndGet();
+
return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
}
}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sample.toaster.provider;
-
-public class ToasterActivator {
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sample.toaster.provider;
-
-import java.util.Collection;
-import java.util.Collections;
-
-import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
-import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ToasterProvider extends AbstractBindingAwareProvider {
- private static final Logger log = LoggerFactory.getLogger(ToasterProvider.class);
-
- private ProviderContext providerContext;
- private final OpendaylightToaster toaster;
-
- public ToasterProvider() {
- toaster = new OpendaylightToaster();
- }
-
- @Override
- public void onSessionInitiated(ProviderContext session) {
- log.info("Provider Session initialized");
-
- this.providerContext = session;
- toaster.setNotificationProvider(session.getSALService(NotificationProviderService.class));
- providerContext.addRpcImplementation(ToasterService.class, toaster);
- }
-
- @Override
- public Collection<? extends RpcService> getImplementations() {
- return Collections.emptySet();
- }
-
- @Override
- public Collection<? extends ProviderFunctionality> getFunctionality() {
- return Collections.emptySet();
- }
-}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-provider-impl {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl";
+ prefix "toaster-provider-impl";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import toaster-provider { prefix toaster-provider; revision-date 2014-01-31; }
+ import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-provider impl implementation.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service implementation
+ identity toaster-provider-impl {
+ base config:module-type;
+ config:provided-service toaster-provider:toaster-provider;
+ config:java-name-prefix ToasterProvider;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case toaster-provider-impl {
+ when "/config:modules/config:module/config:type = 'toaster-provider-impl'";
+
+ container rpc-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-rpc-registry;
+ }
+ }
+ }
+
+ container notification-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-notification-service;
+ }
+ }
+ }
+
+ }
+ }
+
+ augment "/config:modules/config:module/config:state" {
+ case toaster-provider-impl {
+ when "/config:modules/config:module/config:type = 'toaster-provider-impl'";
+
+ leaf toasts-made {
+ type uint32;
+ }
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-provider {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider";
+ prefix "toaster-provider";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-provider services.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service
+ identity toaster-provider {
+
+ base "config:service-type";
+
+ config:java-class "org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData";
+ }
+}
\ No newline at end of file
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-file-xml-adapter</artifactId>
<scope>test</scope>
- <version>${config.version}</version>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-directory-xml-adapter</artifactId>
- <version>${config.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
import org.junit.Test;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageHeader;
+@Deprecated
public class MessageHeaderTest {
@Test
public void testFromBytes() {
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</dependency>
- <!--dependency>
- <groupId>com.siemens.ct.exi</groupId>
- <artifactId>exificient</artifactId>
- </dependency-->
<dependency>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
.build();
}
handleResourceCongruence(name, flowConfig.getName());
- handleResourceCongruence(nodeId, flowConfig.getNode().getNodeIDString());
+ handleResourceCongruence(nodeType, flowConfig.getNode().getType());
+ handleResourceCongruence(nodeId, flowConfig.getNode().getID() == null ? null : flowConfig.getNode().getNodeIDString());
handleDefaultDisabled(containerName);
IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
c.add(createServiceDependency()
.setService(IFlowProgrammerNotifier.class)
.setCallbacks("setFlowProgrammerNotifier",
- "unsetsetFlowProgrammerNotifier")
+ "unsetFlowProgrammerNotifier")
.setRequired(false));
c.add(createServiceDependency()
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
for (int i = 0; i < edges.size() - 1; i++) {
Edge current = edges.get(i);
Edge next = edges.get(i + 1);
- if (!current.getHeadNodeConnector().getNode()
- .equals(
- next.getTailNodeConnector()
- .getNode())) {
+ if (!current.getHeadNodeConnector().getNode().equals(next.getTailNodeConnector().getNode())) {
sequential = false;
+ break;
}
}
} else if (edges.size() == 0) {
* getter method for the Path
*
*
- * @return Return the list of edges that constitue the Path
+ * @return Return the list of edges that constitute the Path
*/
public List<Edge> getEdges() {
- return this.edges;
+ return (edges == null) ? Collections.<Edge>emptyList() : new ArrayList<Edge>(edges);
}
@Override
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
Path other = (Path) obj;
if (edges == null) {
- if (other.edges != null)
+ if (other.edges != null) {
return false;
- } else if (!edges.equals(other.edges))
+ }
+ } else if (!edges.equals(other.edges)) {
return false;
+ }
return true;
}
<sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
</properties>
<build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
- </plugin>
- </plugins>
- </pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
<sonar.jacoco.itReportPath>../implementaiton/target/jacoco-it.exec</sonar.jacoco.itReportPath>
</properties>
<build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
- </plugin>
- </plugins>
- </pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <!-- Get some common settings for the project we are using it in -->
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.thirdparty</artifactId>
- <version>1.1.1-SNAPSHOT</version>
- <relativePath>../commons/thirdparty</relativePath>
- </parent>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
- <tag>HEAD</tag>
- </scm>
-
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.opendaylight.controller.thirdparty</groupId>
- <artifactId>exificient</artifactId>
- <version>0.9.2-SNAPSHOT</version>
- <packaging>bundle</packaging>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>2.3.6</version>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Embed-Dependency>*;scope=!provided;type=!pom;inline=false</Embed-Dependency>
- <Embed-Transitive>false</Embed-Transitive>
- <Export-Package>
- com.siemens.ct.exi.*,
- </Export-Package>
- <Import-Package>
- javax.xml.namespace,
- javax.xml.parsers,
- javax.xml.stream,
- javax.xml.stream.events,
- javax.xml.transform.sax,
- org.apache.xerces.impl.xs,
- org.apache.xerces.impl.xs.models,
- org.apache.xerces.xni,
- org.apache.xerces.xni.grammars,
- org.apache.xerces.xni.parser,
- org.apache.xerces.xs,
- org.w3c.dom,
- org.xml.sax,
- org.xml.sax.ext,
- org.xml.sax.helpers
- </Import-Package>
- </instructions>
- <manifestLocation>${project.basedir}/META-INF</manifestLocation>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-
- <dependencies>
- <dependency>
- <groupId>com.siemens.ct.exi</groupId>
- <artifactId>exificient</artifactId>
- <version>0.9.2</version>
- </dependency>
- </dependencies>
-
-</project>