import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
import org.opendaylight.controller.sal.packet.IDataPacketService;
import org.opendaylight.controller.sal.packet.IListenDataPacket;
-import org.opendaylight.controller.sal.routing.IRouting;
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.controller.topologymanager.ITopologyManager;
import org.slf4j.Logger;
* instantiated in order to get an fully working implementation
* Object
*/
+ @Override
public Object[] getImplementations() {
Object[] res = { ArpHandler.class };
return res;
* also optional per-container different behavior if needed, usually
* should not be the case though.
*/
+ @Override
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(ArpHandler.class)) {
// export the service
"setClusterContainerService", "unsetClusterContainerService")
.setRequired(true));
- c.add(createContainerServiceDependency(containerName).setService(
- IRouting.class).setCallbacks("setRouting","unsetRouting")
- .setRequired(false));
-
// the Host Listener is optional
c.add(createContainerServiceDependency(containerName).setService(
IfHostListener.class).setCallbacks("setHostListener",
}
}
- void setRouting(IRouting r) {
- this.routing = r;
- }
-
- void unsetRouting(IRouting r) {
- if (this.routing == r) {
- this.routing = null;
- }
- }
-
void setHostListener(IfHostListener s) {
if (this.hostListeners != null) {
this.hostListeners.add(s);
// see if we know about the host
// Hosttracker hosts db key implementation
- IHostId id = HostIdFactory.create(dIP, null);
- HostNodeConnector host = hostTracker.hostFind(id);
+ HostNodeConnector host = hostTracker.hostFind(dIP);
if (host == null) {
- // if we don't, know about the host, try to find it
+ // if we don't know about the host, try to find it
log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
/*
* unknown destination host, initiate bcast ARP request
*/
arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
- } else if (routing == null || routing.getRoute(p.getNode(), host.getnodeconnectorNode()) != null) {
- /*
- * if IRouting is available, make sure that this packet can get it's
- * destination normally before teleporting it there. If it's not
- * available, then assume it's reachable.
- *
- * TODO: come up with a way to do this in the absence of IRouting
- */
-
- log.trace("forwarding punted IP pkt to {} received at {}", dIP, p);
-
- /*
- * if we know where the host is and there's a path from where this
- * packet was punted to where the host is, then deliver it to the
- * host for now
- */
- NodeConnector nc = host.getnodeConnector();
-
- // re-encode the Ethernet packet (the parent of the IPv4 packet)
- RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
- rp.setOutgoingNodeConnector(nc);
- this.dataPacketService.transmitDataPacket(rp);
} else {
- log.trace("ignoring punted IP pkt to {} because there is no route from {}", dIP, p);
+ log.trace("Ignoring punted IP pkt to known host: {} (received on: {})", dIP, p);
}
}
<commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
<concepts.version>0.5.2-SNAPSHOT</concepts.version>
<protocol-framework.version>0.5.0-SNAPSHOT</protocol-framework.version>
- <netty.version>4.0.10.Final</netty.version>
+ <netty.version>4.0.17.Final</netty.version>
<commons.io.version>2.4</commons.io.version>
<bundlescanner.version>0.4.2-SNAPSHOT</bundlescanner.version>
<usermanager.version>0.4.2-SNAPSHOT</usermanager.version>
import org.opendaylight.controller.config.manager.impl.osgi.mapping.RefreshingSCPModuleInfoRegistry;
import org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil;
import org.opendaylight.controller.config.spi.ModuleFactory;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import javax.management.MBeanServer;
import java.lang.management.ManagementFactory;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
import static org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil.registerService;
// track bundles containing factories
BlankTransactionServiceTracker blankTransactionServiceTracker = new BlankTransactionServiceTracker(
configRegistry);
- ModuleFactoryBundleTracker moduleFactoryBundleTracker = new ModuleFactoryBundleTracker(
+ ModuleFactoryBundleTracker primaryModuleFactoryBundleTracker = new ModuleFactoryBundleTracker(
blankTransactionServiceTracker);
// start extensible tracker
- ExtensibleBundleTracker<Collection<ObjectRegistration<YangModuleInfo>>> bundleTracker = new ExtensibleBundleTracker<>(context, moduleInfoBundleTracker, moduleFactoryBundleTracker);
+ ExtensibleBundleTracker<?> bundleTracker = new ExtensibleBundleTracker<>(context,
+ primaryModuleFactoryBundleTracker, moduleInfoBundleTracker);
bundleTracker.open();
// register config registry to OSGi
*/
package org.opendaylight.controller.config.manager.impl.osgi.mapping;
+import static java.lang.String.format;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
import org.apache.commons.io.IOUtils;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.sal.binding.generator.api.ModuleInfoRegistry;
import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-import static java.lang.String.format;
-
/**
* Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry
*/
return;
}
- for (Registration<YangModuleInfo> reg : regs) {
+ for (ObjectRegistration<YangModuleInfo> reg : regs) {
try {
reg.close();
} catch (Exception e) {
# Set Default start level for framework
osgi.bundles.defaultStartLevel=4
# Extra packages to import from the boot class loader
-org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc
+org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc,sun.nio.ch
# This is not Eclipse App
eclipse.ignoreApp=true
# Don't shutdown equinox if the eclipse App has ended,
import org.opendaylight.controller.sal.action.Drop;
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.HwPath;
import org.opendaylight.controller.sal.action.Loopback;
import org.opendaylight.controller.sal.action.Output;
continue;
}
+ sstr = Pattern.compile(ActionType.ENQUEUE + "=(.*)").matcher(actiongrp);
+ if (sstr.matches()) {
+ for (String t : sstr.group(1).split(",")) {
+ if (t != null) {
+ String parts[] = t.split(":");
+ String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString());
+ if (NodeConnector.fromString(nc) == null) {
+ return new Status(StatusCode.BADREQUEST, String.format("Enqueue port is not valid"));
+ }
+ if (parts.length > 1) {
+ try {
+ Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ return new Status(StatusCode.BADREQUEST, String.format("Enqueue %s is not in the range 0 - 2147483647", parts[1]));
+ }
+ }
+ }
+ }
+ continue;
+ }
+
sstr = Pattern.compile(ActionType.SET_VLAN_PCP.toString() + "=(.*)").matcher(actiongrp);
if (sstr.matches()) {
if ((sstr.group(1) != null) && !isVlanPriorityValid(sstr.group(1))) {
continue;
}
+ sstr = Pattern.compile(ActionType.FLOOD_ALL.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new FloodAll());
+ continue;
+ }
+
sstr = Pattern.compile(ActionType.SW_PATH.toString()).matcher(actiongrp);
if (sstr.matches()) {
actionList.add(new SwPath());
static final String ACTIVE_HOST_CACHE = "hosttracker.ActiveHosts";
static final String INACTIVE_HOST_CACHE = "hosttracker.InactiveHosts";
private static final Logger logger = LoggerFactory.getLogger(HostTracker.class);
- protected final Set<IHostFinder> hostFinder = new CopyOnWriteArraySet<IHostFinder>();;
+ protected final Set<IHostFinder> hostFinders = new CopyOnWriteArraySet<IHostFinder>();
protected ConcurrentMap<IHostId, HostNodeConnector> hostsDB;
/*
* Following is a list of hosts which have been requested by NB APIs to be
}
public void setArpHandler(IHostFinder hostFinder) {
- if (this.hostFinder != null) {
- this.hostFinder.add(hostFinder);
- }
+ this.hostFinders.add(hostFinder);
}
public void unsetArpHandler(IHostFinder hostFinder) {
- if (this.hostFinder != null) {
- logger.debug("Arp Handler Service removed!");
- this.hostFinder.remove(hostFinder);
- }
+ logger.debug("Arp Handler Service removed!");
+ this.hostFinders.remove(hostFinder);
}
public void setTopologyManager(ITopologyManager s) {
* already handles the null return
*/
- if (hostFinder == null) {
- logger.debug("Exiting hostFind, null hostFinder");
+ if (hostFinders.isEmpty()) {
+ logger.debug("No available host finders, exiting hostFind()");
return null;
}
logger.debug("hostFind(): Host Not Found for IP: {}, Inititated Host Discovery ...", id);
/* host is not found, initiate a discovery */
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
InetAddress addr = decodeIPFromId(id);
hf.find(addr);
}
for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
ARPPending arphost;
arphost = entry.getValue();
- if (hostFinder == null) {
- logger.warn("ARPHandler Services are not available on subnet addition");
+ if (hostFinders.isEmpty()) {
+ logger.debug("ARPHandler Services are not available on subnet addition");
continue;
}
logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", decodeIPFromId(arphost.getHostId()));
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.find(decodeIPFromId(arphost.getHostId()));
}
}
* next one. Before sending the ARP, check if ARPHandler
* is available or not
*/
- if (hostFinder == null) {
+ if (hostFinders.isEmpty()) {
logger.warn("ARPHandler Services are not available for Outstanding ARPs");
continue;
}
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.find(decodeIPFromId(arphost.getHostId()));
}
arphost.sent_count++;
HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
}
host.setArpSendCountDown(arp_cntdown);
- if (hostFinder == null) {
+ if (hostFinders.isEmpty()) {
/*
* If hostfinder is not available, then can't send
* the probe. However, continue the age out the
logger.trace("ARPHandler is not avaialable, can't send the probe");
continue;
}
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.probe(host);
}
}
for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
arphost = entry.getValue();
logger.trace("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostId());
- if (hostFinder == null) {
+ if (hostFinders.isEmpty()) {
logger.warn("ARPHandler is not available at interface up");
logger.warn("Since this event is missed, host(s) connected to interface {} may not be discovered",
nodeConnector);
byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr();
host = new HostNodeConnector(dataLayerAddress, decodeIPFromId(arphost.getHostId()), nodeConnector,
(short) 0);
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.probe(host);
}
} catch (ConstructionException e) {
class FRMRuntimeDataProvider implements RuntimeDataProvider, DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> {
- static val FLOWS_PATH = InstanceIdentifier.builder().node(Flows).toInstance;
+ static val FLOWS_PATH = InstanceIdentifier.builder(Flows).toInstance;
@Property
var DataProviderService dataService;
DataModificationTransaction transaction = dataService.beginTransaction();
Flow flow = createSampleFlow("foo", null);
- InstanceIdentifier<Flow> path = InstanceIdentifier.builder().node(Flows.class).node(Flow.class, flow.getKey())
+ InstanceIdentifier<Flow> path = InstanceIdentifier.builder(Flows.class).child(Flow.class, flow.getKey())
.toInstance();
transaction.putConfigurationData(path, flow);
*/
package org.opendaylight.controller.md.compatibility.switchmanager
-import org.opendaylight.controller.switchmanager.ISwitchManager
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.core.Property
-import java.util.List
-import org.opendaylight.controller.sal.core.Node
import java.net.InetAddress
+import java.net.NetworkInterface
+import java.net.SocketException
+import java.util.ArrayList
+import java.util.Collections
+import java.util.HashSet
+import java.util.List
+import java.util.Map
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
-import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
-import org.opendaylight.controller.sal.core.Description
-import org.opendaylight.controller.sal.core.Tier
import org.opendaylight.controller.sal.core.Bandwidth
+import org.opendaylight.controller.sal.core.Description
import org.opendaylight.controller.sal.core.ForwardingMode
import org.opendaylight.controller.sal.core.MacAddress
-
-import org.slf4j.LoggerFactory
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import java.net.NetworkInterface
-import java.net.SocketException
-import java.util.Collections
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
-import java.util.ArrayList
+import org.opendaylight.controller.sal.core.Node
+import org.opendaylight.controller.sal.core.NodeConnector
+import org.opendaylight.controller.sal.core.Property
+import org.opendaylight.controller.sal.core.Tier
+import org.opendaylight.controller.switchmanager.ISwitchManager
import org.opendaylight.controller.switchmanager.Switch
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
-import java.util.Map
-import java.util.HashSet
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import org.slf4j.LoggerFactory
+
+import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
class CompatibleSwitchManager extends ConfigurableSwitchManager implements ISwitchManager {
}
override getNetworkDevices() {
- val path = InstanceIdentifier.builder().node(Nodes).toInstance;
+ val path = InstanceIdentifier.builder(Nodes).toInstance;
val data = dataService.readOperationalData(path) as Nodes;
val ret = new ArrayList<Switch>();
for (node : data.node) {
}
override getNodes() {
- val path = InstanceIdentifier.builder().node(Nodes).toInstance;
+ val path = InstanceIdentifier.builder(Nodes).toInstance;
val data = dataService.readOperationalData(path) as Nodes;
val ret = new HashSet<Node>();
for (node : data.node) {
*/
package org.opendaylight.controller.md.compatibility.topology
+import java.util.ArrayList
+import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+import org.opendaylight.controller.sal.core.Edge
+import org.opendaylight.controller.sal.core.NodeConnector
import org.opendaylight.controller.switchmanager.ISwitchManager
import org.opendaylight.controller.topologymanager.ITopologyManager
-import org.opendaylight.controller.md.sal.common.api.data.DataReader
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology
-import org.opendaylight.controller.md.compatibility.topology.TopologyMapping
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder
-
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder
-import java.util.ArrayList
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.topology.TopoEdgeUpdate
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder
-import org.opendaylight.controller.sal.core.Edge
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
class TopologyReader implements RuntimeDataProvider {
new() {
_topologyKey = new TopologyKey(new TopologyId("compatibility:ad-sal"));
- _topologyPath = InstanceIdentifier.builder().node(NetworkTopology).child(Topology, topologyKey).toInstance;
+ _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topologyKey).toInstance;
_mapping = new TopologyMapping(topologyKey, topologyPath);
}
import java.util.Map
import org.opendaylight.controller.sal.core.Edge
import java.util.Set
-import java.util.List
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
-import java.util.Collections
-import com.google.common.collect.FluentIterable
import java.util.HashSet
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId
import org.opendaylight.controller.sal.compatibility.NodeMapping
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey
import java.util.HashMap
new(TopologyKey topology) {
topologyMapping = topology;
- _topologyPath = InstanceIdentifier.builder.node(NetworkTopology).child(Topology, topology).toInstance;
+ _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topology).toInstance;
}
def InstanceIdentifier<TerminationPoint> toTerminationPoint(NodeConnector connector) {
- InstanceIdentifier.builder(topologyPath).node(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance;
+ InstanceIdentifier.builder(topologyPath).child(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance;
}
def Map<Edge, Set<org.opendaylight.controller.sal.core.Property>> toEdgePropertiesMap(Iterable<Link> links) {
topology.dataService = session.getSALService(DataProviderService)
tpProvider.dataService = session.getSALService(DataProviderService)
- inventory.start();
+ inventory.startAdapter();
- tpProvider.start();
+ tpProvider.startAdapter();
subscribe.registerNotificationListener(dataPacket)
}
private final Lock nodeToNodeConnectorsLock = new ReentrantLock();
- def start(){
+ def startAdapter(){
inventoryNotificationProvider.dataProviderService = dataProviderService;
inventoryNotificationProvider.inventoryPublisher = inventoryPublisher;
// inventoryNotificationProvider.start();
}
+ def start(){
+ }
+
def setInventoryPublisher(IPluginOutInventoryService listener){
inventoryPublisher.add(listener);
}
public static NodeRef toNodeRef(final Node node) {
final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node));
final NodeKey nodeKey = new NodeKey(nodeId);
- final InstanceIdentifierBuilder<? extends Object> builder = InstanceIdentifier.builder();
- final InstanceIdentifierBuilder<Nodes> nodes = builder.<Nodes>node(Nodes.class);
+ final InstanceIdentifierBuilder<Nodes> nodes = InstanceIdentifier.builder(Nodes.class);
final InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> child =
nodes.<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, NodeKey>child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nodeKey);
final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> path = child.toInstance();
Preconditions.checkArgument(MD_SAL_TYPE.equals(node.getType()));
final String nodeId = Arguments.<String>checkInstanceOf(node.getID(), String.class);
final NodeKey nodeKey = new NodeKey(new NodeId(nodeId));
- final InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder().node(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
+ final InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
return new NodeRef(nodePath);
}
DataProviderService dataService;
Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>,DataObject>> commitHandlerRegistration;
-
+
def void start() {
+
+ }
+ def void startAdapter() {
+ if(dataService == null){
+ LOG.error("dataService not set");
+ return;
+ }
commitHandler = new TopologyCommitHandler(dataService)
commitHandler.setTopologyPublisher(topologyPublisher)
val InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(NetworkTopology)
def setTopologyPublisher(IPluginOutTopologyService topologyPublisher) {
_topologyPublisher = topologyPublisher;
- commitHandler.setTopologyPublisher(topologyPublisher);
+ if(commitHandler != null){
+ commitHandler.setTopologyPublisher(topologyPublisher);
+ }
}
}
<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
package org.opendaylight.controller.md.inventory.manager
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowNodeConnector
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode
uses tr:transaction-aware;
}
}
-
- rpc get-port {
- output {
- uses port-type:flow-capable-port;
- }
- }
notification port-updated {
uses port-update;
namespace "urn:opendaylight:model:statistics:types";
prefix stat-types;
- import ietf-yang-types {prefix yang;}
+ import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
revision "2013-09-25" {
description "Initial revision of flow service";
</scm>
<dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-common</artifactId>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common</artifactId>
+ <artifactId>sal-common-api</artifactId>
</dependency>
-
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-api</artifactId>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
--- /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.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>{
+ @Override
+ BindingDataReadTransaction newReadOnlyTransaction();
+
+ @Override
+ BindingDataReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ BindingDataWriteTransaction newWriteOnlyTransaction();
+
+ @Override
+ ListenerRegistration<BindingDataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
+ InstanceIdentifier<?> path, BindingDataChangeListener listener, DataChangeScope triggeringScope);
+}
--- /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.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataChangeListener extends AsyncDataChangeListener<InstanceIdentifier<?>, DataObject> {
+ @Override
+ void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change);
+}
--- /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.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface BindingDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, DataObject> {
+ @Override
+ ListenableFuture<Optional<DataObject>> read(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
--- /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.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Logical capture of a combination of both {@link BindingDataReadTransaction} and
+ * {@link BindingDataWriteTransaction}.
+ */
+public interface BindingDataReadWriteTransaction extends BindingDataReadTransaction, BindingDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
--- /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.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, DataObject> {
+ @Override
+ void put(LogicalDatastoreType store, InstanceIdentifier<?> path, DataObject data);
+
+ @Override
+ void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.RpcService;
/**
* Binding-aware core of the SAL layer responsible for wiring the SAL consumers.
- *
+ *
* The responsibility of the broker is to maintain registration of SAL
* functionality {@link Consumer}s and {@link Provider}s, store provider and
* consumer specific context and functionality registration via
* {@link ConsumerContext} and provide access to infrastructure services, which
* removes direct dependencies between providers and consumers.
- *
+ *
* The Binding-aware broker is also responsible for translation from Java
* classes modeling the functionality and data to binding-independent form which
* is used in SAL Core.
- *
- *
+ *
+ *
* <h3>Infrastructure services</h3> Some examples of infrastructure services:
- *
+ *
* <ul>
* <li>YANG Module service - see {@link ConsumerContext#getRpcService(Class)},
* {@link ProviderContext}
* <li>Data Store access and modification - see {@link DataBrokerService} and
* {@link DataProviderService}
* </ul>
- *
+ *
* The services are exposed via session.
- *
+ *
* <h3>Session-based access</h3>
- *
+ *
* The providers and consumers needs to register in order to use the
* binding-independent SAL layer and to expose functionality via SAL layer.
- *
+ *
* For more information about session-based access see {@link ConsumerContext}
* and {@link ProviderContext}
- *
- *
- *
+ *
+ *
+ *
*/
public interface BindingAwareBroker {
/**
* Registers the {@link BindingAwareConsumer}, which will use the SAL layer.
- *
+ *
* <p>
* Note that consumer could register additional functionality at later point
* by using service and functionality specific APIs.
- *
+ *
* <p>
* The consumer is required to use returned session for all communication
* with broker or one of the broker services. The session is announced to
* the consumer by invoking
* {@link Consumer#onSessionInitiated(ConsumerContext)}.
- *
+ *
* @param cons
* Consumer to be registered.
* @return a session specific to consumer registration
/**
* Registers the {@link BindingAwareProvider}, which will use the SAL layer.
- *
+ *
* <p>
* During the registration, the broker obtains the initial functionality
* from consumer, using the
* {@link BindingAwareProvider#getImplementations()}, and register that
* functionality into system and concrete infrastructure services.
- *
+ *
* <p>
* Note that provider could register additional functionality at later point
* by using service and functionality specific APIs.
- *
+ *
* <p>
* The consumer is <b>required to use</b> returned session for all
* communication with broker or one of the broker services. The session is
* announced to the consumer by invoking
* {@link BindingAwareProvider#onSessionInitiated(ProviderContext)}.
- *
- *
+ *
+ *
* @param prov
* Provider to be registered.
* @return a session unique to the provider registration.
/**
* {@link BindingAwareConsumer} specific access to the SAL functionality.
- *
+ *
* <p>
* ConsumerSession is {@link BindingAwareConsumer}-specific access to the
* SAL functionality and infrastructure services.
- *
+ *
* <p>
* The session serves to store SAL context (e.g. registration of
* functionality) for the consumer and provides access to the SAL
* infrastructure services and other functionality provided by
* {@link Provider}s.
- *
- *
- *
+ *
+ *
+ *
*/
public interface ConsumerContext extends RpcConsumerRegistry {
/**
* Returns a session specific instance (implementation) of requested
* binding-aware infrastructural service
- *
+ *
* @param service
* Broker service
* @return Session specific implementation of service
/**
* {@link BindingAwareProvider} specific access to the SAL functionality.
- *
+ *
* <p>
* ProviderSession is {@link BindingAwareProvider}-specific access to the
* SAL functionality and infrastructure services, which also allows for
* exposing the provider's functionality to the other
* {@link BindingAwareConsumer}s.
- *
+ *
* <p>
* The session serves to store SAL context (e.g. registration of
* functionality) for the providers and exposes access to the SAL
* infrastructure services, dynamic functionality registration and any other
* functionality provided by other {@link BindingAwareConsumer}s.
- *
+ *
*/
public interface ProviderContext extends ConsumerContext, RpcProviderRegistry {
void unregisterFunctionality(ProviderFunctionality functionality);
}
- public interface RpcRegistration<T extends RpcService> extends Registration<T> {
+ public interface RpcRegistration<T extends RpcService> extends ObjectRegistration<T> {
Class<T> getServiceType();
}
/**
* Register particular instance identifier to be processed by this
* RpcService
- *
- * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
- *
+ *
+ * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
+ *
* @param context
* @param instance
*/
/**
* Unregister particular instance identifier to be processed by this
* RpcService
- *
- * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
- *
+ *
+ * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
+ *
* @param context
* @param instance
*/
public RpcProxyRegistration(Class<T> type, T service, RpcProviderRegistryImpl registry) {
super(service);
- serviceType = type;
+ this.serviceType = type;
+ this.registry = registry;
}
@Override
*/
package org.opendaylight.controller.sal.binding.test.bugfix;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
import org.junit.Test;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.Prefix;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.PrefixBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-
-
-
-
-
-
-
-import static org.junit.Assert.*;
-
public class UnionSerializationTest extends AbstractDataServiceTest {
public static final String PREFIX_STRING = "192.168.0.1/32";
assertNotNull(serialized.getFirstSimpleByName(Prefix.QNAME));
assertEquals(PREFIX_STRING, serialized.getFirstSimpleByName(Prefix.QNAME).getValue());
- Prefix deserialized = (Prefix) testContext.getBindingToDomMappingService().dataObjectFromDataDom(InstanceIdentifier.builder().node(Prefix.class).build(), serialized);
+ Prefix deserialized = (Prefix) testContext.getBindingToDomMappingService().dataObjectFromDataDom(Prefix.class, serialized);
assertNotNull(deserialized);
assertNotNull(deserialized.getPrefix());
assertNotNull(deserialized.getPrefix().getIpv4Prefix());
bindingAwareSalBundles(),
mavenBundle("commons-codec", "commons-codec").versionAsInProject(),
+ systemProperty("org.osgi.framework.system.packages.extra").value("sun.nio.ch"),
+ mavenBundle("io.netty", "netty-common").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
+
mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), //
mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // //
mavenBundle("commons-io", "commons-io").versionAsInProject(), //
mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), //
- mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
- mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
- mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
- mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
- mavenBundle("io.netty", "netty-common").versionAsInProject(), //
-
mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"),
mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"),
import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
@RunWith(PaxExam.class)
mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+ systemPackages("sun.nio.ch"),
mdSalCoreBundles(),
-<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">
+<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>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-parent</artifactId>
<version>1.1-SNAPSHOT</version>
</parent>
+
<artifactId>sal-common-api</artifactId>
+ <packaging>bundle</packaging>
+
<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:MD-SAL</url>
- <tag>HEAD</tag>
- </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:MD-SAL</url>
+ <tag>HEAD</tag>
+ </scm>
<dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-common</artifactId>
<artifactId>concepts</artifactId>
</dependency>
<dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
</dependency>
</dependencies>
- <packaging>bundle</packaging>
</project>
--- /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.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
+ AsyncDataTransactionFactory<P, D> {
+
+ /**
+ *
+ * Scope of Data Change
+ *
+ * Represents scope of data change (addition, replacement, deletion).
+ *
+ * The terminology for types is reused from LDAP
+ *
+ * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/SEARCH_Setting_the_SCOPE_Parameter.shtml
+ */
+ public enum DataChangeScope {
+
+ /**
+ * Represents only a direct change of the node, such as replacement of node,
+ * addition or deletion.
+ *
+ */
+ BASE,
+ /**
+ * Represent a change (addition,replacement,deletion)
+ * of the node or one of it's direct childs.
+ *
+ */
+ ONE,
+ /**
+ * Represents a change of the node or any of it's child nodes.
+ *
+ */
+ SUBTREE
+ }
+
+ @Override
+ public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+ @Override
+ public AsyncReadWriteTransaction<P,D> newReadWriteTransaction();
+
+ @Override
+ public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+ /**
+ * Registers {@link DataChangeListener} for Data Change callbacks
+ * which will be triggered on which will be triggered on the store
+ *
+ *Â @param store Logical store in which listener is registered.
+ * @param path Path (subtree identifier) on which client listener will be invoked.
+ * @param listener Instance of listener which should be invoked on
+ * @param triggeringScope Scope of change which triggers callback.
+ * @return Listener registration of the listener, call {@link ListenerRegistration#close()}
+ * to stop delivery of change events.
+ */
+ ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener, DataChangeScope triggeringScope);
+}
--- /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.md.sal.common.api.data;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataChangeEvent<P extends Path<P>,D> extends Immutable {
+ /**
+ * Returns a immutable map of paths and newly created objects
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getCreatedData();
+
+ /**
+ * Returns a immutable map of paths and respective updated objects after update.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalData()}
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getUpdatedData();
+
+ /**
+ * Returns a immutable set of removed paths.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalData()}
+ *
+ * @return set of removed paths
+ */
+ Set<P> getRemovedPaths();
+
+ /**
+ * Return a immutable map of paths and original state of updated and removed objects.
+ *
+ * This map is populated if at changed path was previous object, and captures
+ * state of previous object.
+ *
+ * @return map of paths and original state of updated and removed objects.
+ */
+ Map<P, ? extends D> getOriginalData();
+
+ /**
+ * Returns a immutable stable view of data state, which
+ * captures state of data store before the reported change.
+ *
+ *
+ * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+ *
+ * @return Stable view of data before the change happened, rooted at the listener registration path.
+ *
+ */
+ D getOriginalSubtree();
+
+ /**
+ * Returns a immutable stable view of data, which captures state of data store
+ * after the reported change.
+ *
+ * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+ *
+ * @return Stable view of data after the change happened, rooted at the listener registration path.
+ */
+ D getUpdatedSubtree();
+}
--- /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.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataChangeListener<P extends Path<P>, D> extends EventListener {
+ /**
+ * Note that this method may be invoked from a shared thread pool, so
+ * implementations SHOULD NOT perform CPU-intensive operations and they
+ * definitely MUST NOT invoke any potentially blocking operations.
+ *
+ * @param change Data Change Event being delivered.
+ */
+ void onDataChanged(AsyncDataChangeEvent<P, D> change);
+}
--- /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.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
+
+ AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+ AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+ AsyncWriteTransaction<P,D> newWriteOnlyTransaction();
+
+}
--- /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.md.sal.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+
+ /**
+ *
+ * Reads data from provided logical data store located at provided path
+ *
+ *
+ * @param store
+ * Logical data store from which read should occur.
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return Listenable Future which contains read result
+ * <ul>
+ * <li>If data at supplied path exists the {@link Future#get()}
+ * returns Optional object containing data
+ * <li>If data at supplied path does not exists the
+ * {@link Future#get()} returns {@link Optional#absent()}.
+ * </ul>
+ */
+ ListenableFuture<Optional<D>> read(LogicalDatastoreType store, P path);
+
+}
--- /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.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Transaction enabling client to have combined transaction,
+ * which provides read and write capabilities.
+ *
+ *
+ * @param <P> Path Type
+ * @param <D> Data Type
+ */
+public interface AsyncReadWriteTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>,
+ AsyncWriteTransaction<P, D> {
+
+}
--- /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.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Path;
+
+
+/**
+ *
+ * @author
+ *
+ * @param <P> Type of path (subtree identifier), which represents location in tree
+ * @param <D> Type of data (payload), which represents data payload
+ */
+public interface AsyncTransaction<P extends Path<P>,D> extends //
+ Identifiable<Object>,
+ AutoCloseable {
+
+ @Override
+ public Object getIdentifier();
+
+ /**
+ * Closes transaction and releases all resources associated with it.
+ */
+ @Override
+ public void close();
+}
--- /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.md.sal.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+ /**
+ * Cancels transaction.
+ *
+ * Transaction could be only cancelled if it's status
+ * is {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
+ *
+ * Invoking cancel() on {@link TransactionStatus#FAILED} or {@link TransactionStatus#CANCELED}
+ * will have no effect.
+ *
+ * @throws IllegalStateException If transaction status is {@link TransactionStatus#COMMITED}
+ *
+ */
+ public void cancel();
+
+ /**
+ * Store a piece of data at specified path. This acts as a add / replace operation,
+ * which is to say that whole subtree will be replaced by specified path.
+ *
+ * If you need add or merge of current object with specified use {@link #merge(LogicalDatastoreType, Path, Object)}
+ *
+ * @param store Logical data store which should be modified
+ * @param path Data object path
+ * @param data Data object to be written to specified path
+ * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ */
+ public void put(LogicalDatastoreType store, P path, D data);
+
+ /**
+ * Store a piece of data at specified path. This acts as a merge operation,
+ * which is to say that any pre-existing data which is not explicitly
+ * overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged. Performing the following put operations:
+ *
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ *
+ * will result in the following data being present:
+ *
+ * container { list [ a, b ] }
+ *
+ * This also means that storing the container will preserve any augmentations
+ * which have been attached to it.
+ *
+ * If you require an explicit replace operation, use {@link #put(LogicalDatastoreType, Path, Object)} instead.
+ *
+ * @param store Logical data store which should be modified
+ * @param path Data object path
+ * @param data Data object to be written to specified path
+ * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ */
+ public void merge(LogicalDatastoreType store, P path, D data);
+
+ /**
+ * Remove a piece of data from specified path. This operation does not fail
+ * if the specified path does not exist.
+ *
+ * @param store Logical data store which should be modified
+ * @param path Data object path
+ * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ */
+ public void delete(LogicalDatastoreType store, P path);
+
+ /**
+ *
+ * Closes transaction and resources allocated to the transaction.
+ *
+ * This call does not change Transaction status. Client SHOULD
+ * explicitly {@link #commit()} or {@link #cancel()} transaction.
+ *
+ * @throws IllegalStateException if the transaction has not been
+ * updated by invoking {@link #commit()} or {@link #cancel()}.
+ */
+ @Override
+ public void close();
+
+ /**
+ * Initiates a commit of modification. This call logically seals the
+ * transaction, preventing any the client from interacting with the
+ * data stores. The transaction is marked as {@link TransactionStatus#SUBMITED}
+ * and enqueued into the data store backed for processing.
+ *
+ * <p>
+ * The successful commit changes the state of the system and may affect
+ * several components.
+ *
+ * <p>
+ * The effects of successful commit of data are described in the
+ * specifications and YANG models describing the Provider components of
+ * controller. It is assumed that Consumer has an understanding of this
+ * changes.
+ *
+ * @see DataCommitHandler for further information how two-phase commit is
+ * processed.
+ * @param store Identifier of the store, where commit should occur.
+ * @return Result of the Commit, containing success information or list of
+ * encountered errors, if commit was not successful. The Future
+ * blocks until {@link TransactionStatus#COMMITED} or
+ * {@link TransactionStatus#FAILED} is reached.
+ * @throws IllegalStateException if the transaction is not {@link TransactionStatus#NEW}
+ */
+ public Future<RpcResult<TransactionStatus>> commit();
+
+}
import org.opendaylight.yangtools.concepts.Path;
public interface DataChangeListener<P extends Path<P>, D> extends EventListener {
-
+ /**
+ * Note that this method may be invoked from a shared thread pool, so
+ * implementations SHOULD NOT perform CPU-intensive operations and they
+ * definitely MUST NOT invoke any potentially blocking operations.
+ *
+ * @param change Data Change Event being delivered.
+ **/
void onDataChanged(DataChangeEvent<P, D> change);
}
*/
package org.opendaylight.controller.md.sal.common.api.data;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.yang.common.RpcResult;
/**
--- /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.md.sal.common.api.data;
+
+public enum LogicalDatastoreType {
+
+ /**
+ * Logical atastore representing operational state of the system
+ * and it's components
+ *
+ * This datastore is used to describe operational state of
+ * the system and it's operation related data.
+ *
+ */
+ OPERATIONAL,
+ /**
+ * Logical Datastore representing configuration state of the system
+ * and it's components.
+ *
+ * This datastore is used to describe intended state of
+ * the system and intended operation mode.
+ *
+ */
+ CONFIGURATION
+
+}
* A chain of transactions. Transactions in a chain need to be committed in sequence and each
* transaction should see the effects of previous transactions as if they happened. A chain
* makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ *
*/
-public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable {
+public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable, AsyncDataTransactionFactory<P, D> {
+
/**
- * Create a new transaction which will continue the chain. The previous transaction
- * has to be either COMMITTED or CANCELLED.
+ * Create a new read only transaction which will continue the chain.
+ * The previous read-write transaction has to be either COMMITED or CANCELLED.
*
* @return New transaction in the chain.
- * @throws IllegalStateException if the previous transaction was not COMMITTED or CANCELLED.
+ * @throws IllegalStateException if the previous transaction was not COMMITED
+ * or CANCELLED.
* @throws TransactionChainClosedException if the chain has been closed.
*/
- DataModification<P, D> newTransaction();
+ @Override
+ public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+
+ /**
+ * Create a new read write transaction which will continue the chain.
+ * The previous read-write transaction has to be either COMMITED or CANCELLED.
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException if the previous transaction was not COMMITTED
+ * or CANCELLED.
+ * @throws TransactionChainClosedException if the chain has been closed.
+ */
+ @Override
+ public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
@Override
void close();
* @param transaction Transaction which caused the chain to fail
* @param cause The cause of transaction failure
*/
- void onTransactionChainFailed(TransactionChain<?, ?> chain, DataModification<?, ?> transaction, Throwable cause);
+ void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause);
/**
* Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
+++ /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.md.sal.common.impl;
-
-import org.opendaylight.yangtools.concepts.Registration;
-
-public abstract class AbstractRegistration<T> implements Registration<T> {
-
-
- private final T instance;
-
- public AbstractRegistration(T instance) {
- super();
- this.instance = instance;
- }
-
- @Override
- public final T getInstance() {
- return instance;
- }
-
-}
package org.opendaylight.controller.md.sal.common.impl;
import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Path;
-public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractRegistration<S> implements
+public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractObjectRegistration<S> implements
RoutedRegistration<C, P, S> {
public AbstractRoutedRegistration(S instance) {
--- /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.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataBroker extends AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>{
+ @Override
+ DOMDataReadTransaction newReadOnlyTransaction();
+
+ @Override
+ DOMDataReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ DOMDataWriteTransaction newWriteOnlyTransaction();
+}
--- /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.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataChangeListener extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /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.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /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.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadWriteTransaction extends DOMDataReadTransaction, DOMDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /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.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
import org.opendaylight.controller.sal.core.api.notify.NotificationService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
}
- public interface RpcRegistration extends Registration<RpcImplementation> {
+ public interface RpcRegistration extends ObjectRegistration<RpcImplementation> {
QName getType();
@Override
package org.opendaylight.controller.sal.core.api.mount;
-import java.util.concurrent.Future;
-
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
import org.opendaylight.controller.sal.core.api.notify.NotificationService;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface representing a single mount instance and represents a way for
+ * clients to access underlying data, RPCs and notifications.
+ */
public interface MountInstance extends //
NotificationService, //
DataBrokerService {
- Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+ /**
+ * Invoke an RPC on the system underlying the mount instance.
+ *
+ * @param type RPC type
+ * @param input RPC input arguments
+ * @return Future representing execution of the RPC.
+ */
+ ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+ /**
+ * Get {@link SchemaContext} of the system underlying the mount instance.
+ *
+ * @return A schema context.
+ */
SchemaContext getSchemaContext();
}
@Override
public MountProvisionInstance getMountPoint(InstanceIdentifier path);
-
+
MountProvisionInstance createMountPoint(InstanceIdentifier path);
-
+
MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path);
-
+
ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
-
- public interface MountProvisionListener extends EventListener {
-
+
+ public interface MountProvisionListener extends EventListener {
+
void onMountPointCreated(InstanceIdentifier path);
-
+
void onMountPointRemoved(InstanceIdentifier path);
-
+
}
}
import org.opendaylight.controller.sal.core.api.BrokerService;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-
+/**
+ * Client-level interface for interacting with mount points. It provides access
+ * to {@link MountInstance} instances based on their path.
+ */
public interface MountService extends BrokerService {
-
+ /**
+ * Obtain access to a mount instance registered at the specified path.
+ *
+ * @param path Path at which the instance is registered
+ * @return Reference to the instance, or null if no such instance exists.
+ */
MountInstance getMountPoint(InstanceIdentifier path);
}
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import com.google.common.util.concurrent.ListenableFuture;
+
public class MountPointImpl implements MountProvisionInstance, SchemaContextProvider {
private final SchemaAwareRpcBroker rpcs;
@Override
- public Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
+ public ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
return null;
}
import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
public class NotificationRouterImpl implements NotificationRouter {
private static Logger log = LoggerFactory.getLogger(NotificationRouterImpl.class);
- private final Multimap<QName, Registration<NotificationListener>> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, Registration<NotificationListener>>create());
+ private final Multimap<QName, ListenerRegistration> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, ListenerRegistration>create());
// private Registration<NotificationListener> defaultListener;
private void sendNotification(CompositeNode notification) {
final QName type = notification.getNodeType();
- final Collection<Registration<NotificationListener>> toNotify = listeners.get(type);
+ final Collection<ListenerRegistration> toNotify = listeners.get(type);
log.trace("Publishing notification " + type);
if ((toNotify == null) || toNotify.isEmpty()) {
return;
}
- for (Registration<NotificationListener> listener : toNotify) {
+ for (ListenerRegistration listener : toNotify) {
try {
// FIXME: ensure that notification is immutable
listener.getInstance().onNotification(notification);
return ret;
}
- private class ListenerRegistration extends AbstractObjectRegistration<NotificationListener> {
+ private class ListenerRegistration extends AbstractListenerRegistration<NotificationListener> {
final QName type;
--- /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.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStore {
+
+ /**
+ *
+ * Creates a read only transaction
+ *
+ * @return
+ */
+ DOMStoreReadTransaction newReadOnlyTransaction();
+
+ /**
+ * Creates write only transaction
+ *
+ * @return
+ */
+ DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * Creates Read-Write transaction
+ *
+ * @return
+ */
+ DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+ /**
+ * Registers {@link DataChangeListener} for Data Change callbacks
+ * which will be triggered on the change of provided subpath. What
+ * constitutes a change depends on the @scope parameter.
+ *
+ * Listener upon registration receives an initial callback
+ * {@link AsyncDataChangeListener#onDataChanged(org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent)}
+ * which contains stable view of data tree at the time of registration.
+ *
+ *Â @param path Path (subtree identifier) on which client listener will be invoked.
+ * @param listener Instance of listener which should be invoked on
+ * @param scope Scope of change which triggers callback.
+ * @return Listener Registration object, which client may use to close registration
+ * / interest on receiving data changes.
+ *
+ */
+ <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+ InstanceIdentifier path, L listener, DataChangeScope scope);
+
+}
--- /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.sal.core.spi.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface DOMStoreReadTransaction extends DOMStoreTransaction {
+
+ /**
+ *
+ * Reads data from provided logical data store located at provided path
+ *
+ *
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return Listenable Future which contains read result
+ * <ul>
+ * <li>If data at supplied path exists the {@link Future#get()}
+ * returns Optional object containing data
+ * <li>If data at supplied path does not exists the
+ * {@link Future#get()} returns {@link Optional#absent()}.
+ * </ul>
+ */
+ ListenableFuture<Optional<NormalizedNode<?,?>>> read(InstanceIdentifier path);
+}
--- /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.sal.core.spi.data;
+
+/**
+ * Combination of a {@link DOMStoreReadTransaction} and {@link DOMStoreWriteTransaction}.
+ */
+public interface DOMStoreReadWriteTransaction extends DOMStoreReadTransaction, DOMStoreWriteTransaction {
+
+}
--- /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.sal.core.spi.data;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface implemented by the {@link DOMStore} and exposed for each {@link DOMStoreWriteTransaction}
+ * upon its transition to Ready state. The frontend (DOMStore user) uses this interface to drive the
+ * commit procedure across potentially multiple DOMStores using the Three-Phase-Commit (3PC) Protocol,
+ * as described in {@link https://en.wikipedia.org/wiki/Three-phase_commit}.
+ */
+public interface DOMStoreThreePhaseCommitCohort {
+
+ /**
+ * Sends transaction associated with this three phase commit instance to the
+ * participant, participant votes on the transaction, if the transaction
+ * should be committed or aborted.
+ *
+ * @return ListenableFuture with vote of the participant. Vote
+ * {@link ListenableFuture#get()} is following:
+ * <ul>
+ * <li>
+ * true if transaction is approved by data store.
+ * <li>false if the transaction is not approved by data store and
+ * should be aborted.
+ */
+ ListenableFuture<Boolean> canCommit();
+
+ /**
+ * Initiates a pre-commit phase of associated transaction on datastore.
+ *
+ * This message is valid only and only if and only if the participant responded
+ * on {@link #canCommit()} call with positive response.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that pre-commit message was received and processed.
+ */
+ ListenableFuture<Void> preCommit();
+
+ /**
+ * Initiates a abort phase of associated transaction on data store.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that abort message was received.
+ */
+ ListenableFuture<Void> abort();
+
+ /**
+ * Initiates a commit phase on of associated transaction on data store.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that commit message was received and commit of transaction was
+ * processed.
+ */
+ ListenableFuture<Void> commit();
+}
--- /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.sal.core.spi.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+/**
+ * DOM Data Store transaction
+ *
+ * See {@link DOMStoreReadTransaction}, {@link DOMStoreWriteTransaction} and {@link DOMStoreReadWriteTransaction}
+ * for specific transaction types.
+ *
+ */
+public interface DOMStoreTransaction extends AutoCloseable, Identifiable<Object> {
+ /**
+ * Unique identifier of the transaction
+ */
+ @Override
+ public Object getIdentifier();
+
+ @Override
+ void close();
+}
--- /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.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStoreWriteTransaction extends DOMStoreTransaction {
+
+ /**
+ * Store a provided data at specified path. This acts as a add / replace
+ * operation, which is to say that whole subtree will be replaced by
+ * specified path.
+ *
+ * If you need add or merge of current object with specified use
+ * {@link #merge(LogicalDatastoreType, Path, Object)}
+ *
+ *
+ * @param path
+ * @param data
+ * Data object to be written
+ *
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ *
+ * Deletes data and whole subtree located at provided path.
+ *
+ * @param path
+ * Path to delete
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void delete(InstanceIdentifier path);
+
+ /**
+ *
+ * Seals transaction, and returns three-phase commit cohort associated
+ * with this transaction and DOM Store to be coordinated by coordinator.
+ *
+ * @return Three Phase Commit Cohort instance for this transaction.
+ */
+ DOMStoreThreePhaseCommitCohort ready();
+
+}
--- /dev/null
+/*\r
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi.data;
\ No newline at end of file
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-broker-impl</artifactId>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-remote</artifactId>
+ </dependency>
<!-- Testing Dependencies -->
<dependency>
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
import org.opendaylight.yangtools.concepts.Registration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
*
*/
public final class ToasterConsumerModule extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModule
{
+ private static final Logger log = LoggerFactory.getLogger(ToasterConsumerModule.class);
public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
public void close() throws Exception {
runtimeRegistration.close();
notificationRegistration.close();
+ log.info("Toaster consumer (instance {}) torn down.", this);
}
@Override
}
}
- return new AutoCloseableToastConsumer();
+ AutoCloseable ret = new AutoCloseableToastConsumer();
+ log.info("Toaster consumer (instance {}) initialized.", ret);
+ return ret;
}
}
mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+ systemPackages("sun.nio.ch"),
toasterBundles(),
mdSalCoreBundles(),
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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
*
*/
public final class ToasterProviderModule extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModule
{
+ private static final Logger log = LoggerFactory.getLogger(ToasterProviderModule.class);
public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
public void close() throws Exception {
rpcRegistration.close();
runtimeReg.close();
+ log.info("Toaster provider (instance {}) torn down.", this);
}
@Override
}
}
- return new AutoCloseableToaster();
+ AutoCloseable ret = new AutoCloseableToaster();
+ log.info("Toaster provider (instance {}) initialized.", ret);
+ return ret;
}
-
}
<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>junit</groupId>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
- <version>4.0.10.Final</version>
+ <version>${netty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
import org.slf4j.LoggerFactory
import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext
+import org.osgi.framework.BundleContext;
class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implements AutoCloseable {
LOG.info("FlowCapableTopologyProvider stopped.");
listenerRegistration?.close();
}
-
+
+ /**
+ * Gets called on start of a bundle.
+ * @param session
+ */
override onSessionInitiated(ProviderContext session) {
dataService = session.getSALService(DataProviderService)
notificationService = session.getSALService(NotificationProviderService)
exporter.start();
listenerRegistration = notificationService.registerNotificationListener(exporter);
}
+
+ /**
+ * Gets called during stop bundle
+ * @param context The execution context of the bundle being stopped.
+ */
+ override stopImpl(BundleContext context) {
+ close();
+ }
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
import static com.google.common.base.Preconditions.checkState;
public class Activator implements BundleActivator {
public void run() {
NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
logger.debug("Registering into OSGi");
- osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, null);
+ Dictionary<String, String> properties = new Hashtable<>();
+ properties.put("name", "config-netconf-connector");
+ osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, properties);
}
}
}
}
@Override
- public NetconfOperationServiceImpl createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ public NetconfOperationServiceImpl createService(String netconfSessionIdForReporting) {
try {
return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
} catch (YangStoreException e) {
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
Preconditions.checkState(priority != HandlingPriority.CANNOT_HANDLE);
- final Document response = op.handle(request, NetconfOperationRouterImpl.EXECUTION_TERMINATION_POINT);
+ final Document response = op.handle(request, NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT);
logger.debug("Got response\n{}", XmlUtil.toString(response));
return response.getDocumentElement();
}
<groupId>${project.groupId}</groupId>
<artifactId>netconf-api</artifactId>
</dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>netconf-client</artifactId>
- </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<instructions>
<Bundle-Activator>org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator
</Bundle-Activator>
- <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Require-Capability>
- <Import-Package>
- com.google.common.base,
- com.google.common.collect,
- javax.management,
- javax.xml.parsers,
- org.opendaylight.controller.config.persist.api,
- org.opendaylight.controller.netconf.api,
- org.opendaylight.controller.netconf.api.jmx,
- org.opendaylight.controller.netconf.client,
- org.opendaylight.controller.netconf.util.osgi,
- org.opendaylight.controller.netconf.util.xml,
- org.opendaylight.controller.netconf.util.messages,
- io.netty.channel,
- io.netty.channel.nio,
- io.netty.util.concurrent,
- org.osgi.framework,
- org.slf4j,
- org.w3c.dom,
- org.xml.sax,
- javax.xml.namespace,
- javax.xml.xpath,
- org.opendaylight.controller.config.api,
- org.opendaylight.controller.netconf.util
- </Import-Package>
+ <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter</Require-Capability>
<Export-Package>
</Export-Package>
</instructions>
* 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.netconf.persist.impl;
import com.google.common.annotations.VisibleForTesting;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import static com.google.common.base.Preconditions.checkState;
+/**
+ * Inspects snapshot xml to be stored, remove all capabilities that are not referenced by it.
+ * Useful when persisting current configuration.
+ */
public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHolder {
private static final Logger logger = LoggerFactory.getLogger(CapabilityStrippingConfigSnapshotHolder.class);
private final String configSnapshot;
private final StripCapabilitiesResult stripCapabilitiesResult;
- public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities, Pattern ignoredMissingCapabilityRegex) {
+ public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities) {
final XmlElement configElement = XmlElement.fromDomElement(snapshot);
configSnapshot = XmlUtil.toString(configElement.getDomElement());
- stripCapabilitiesResult = stripCapabilities(configElement, capabilities, ignoredMissingCapabilityRegex);
+ stripCapabilitiesResult = stripCapabilities(configElement, capabilities);
}
private static class StripCapabilitiesResult {
- private final SortedSet<String> requiredCapabilities, missingNamespaces;
+ private final SortedSet<String> requiredCapabilities, obsoleteCapabilities;
- private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> missingNamespaces) {
+ private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> obsoleteCapabilities) {
this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities);
- this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces);
+ this.obsoleteCapabilities = Collections.unmodifiableSortedSet(obsoleteCapabilities);
}
}
@VisibleForTesting
- static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello,
- Pattern ignoredMissingCapabilityRegex) {
+ static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello) {
// collect all namespaces
Set<String> foundNamespacesInXML = getNamespaces(configElement);
logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML);
// required are referenced both in xml and hello
SortedSet<String> requiredCapabilities = new TreeSet<>();
// can be removed
- Set<String> obsoleteCapabilities = new HashSet<>();
- // are in xml but not in hello
- SortedSet<String> missingNamespaces = new TreeSet<>(foundNamespacesInXML);
+ SortedSet<String> obsoleteCapabilities = new TreeSet<>();
for (String capability : allCapabilitiesFromHello) {
String namespace = capability.replaceAll("\\?.*","");
if (foundNamespacesInXML.contains(namespace)) {
requiredCapabilities.add(capability);
- checkState(missingNamespaces.remove(namespace));
} else {
obsoleteCapabilities.add(capability);
}
logger.trace("Required capabilities {}, \nObsolete capabilities {}",
requiredCapabilities, obsoleteCapabilities);
- for(Iterator<String> iterator = missingNamespaces.iterator();iterator.hasNext(); ){
- String capability = iterator.next();
- if (ignoredMissingCapabilityRegex.matcher(capability).matches()){
- logger.trace("Ignoring missing capability {}", capability);
- iterator.remove();
- }
- }
- if (missingNamespaces.size() > 0) {
- logger.warn("Some capabilities are missing: {}", missingNamespaces);
- }
- return new StripCapabilitiesResult(requiredCapabilities, missingNamespaces);
+ return new StripCapabilitiesResult(requiredCapabilities, obsoleteCapabilities);
}
static Set<String> getNamespaces(XmlElement element){
result.add(attribute.getValue().getValue());
}
}
- //element.getAttributes()
for(XmlElement child: element.getChildElements()) {
result.addAll(getNamespaces(child));
}
}
@VisibleForTesting
- Set<String> getMissingNamespaces(){
- return stripCapabilitiesResult.missingNamespaces;
+ Set<String> getObsoleteCapabilities(){
+ return stripCapabilitiesResult.obsoleteCapabilities;
}
@Override
import javax.management.ObjectName;
import java.io.Closeable;
import java.io.IOException;
-import java.util.regex.Pattern;
/**
* Responsible for listening for notifications from netconf (via JMX) containing latest
public ConfigPersisterNotificationHandler(MBeanServerConnection mBeanServerConnection,
- Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) {
+ Persister persisterAggregator) {
this.mBeanServerConnection = mBeanServerConnection;
- listener = new ConfigPersisterNotificationListener(persisterAggregator, ignoredMissingCapabilityRegex);
+ listener = new ConfigPersisterNotificationListener(persisterAggregator);
registerAsJMXListener(mBeanServerConnection, listener);
}
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationListener.class);
private final Persister persisterAggregator;
- private final Pattern ignoredMissingCapabilityRegex;
- ConfigPersisterNotificationListener(Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) {
+ ConfigPersisterNotificationListener(Persister persisterAggregator) {
this.persisterAggregator = persisterAggregator;
- this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex;
}
@Override
public void handleNotification(Notification notification, Object handback) {
- if (notification instanceof NetconfJMXNotification == false)
+ if (notification instanceof NetconfJMXNotification == false) {
return;
+ }
// Socket should not be closed at this point
// Activator unregisters this as JMX listener before close is called
logger.warn("Exception occured during notification handling: ", e);
throw e;
}
- } else
+ } else {
throw new IllegalStateException("Unknown config registry notification type " + notification);
+ }
}
private void handleAfterCommitNotification(final CommitJMXNotification notification) {
try {
persisterAggregator.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(),
- notification.getCapabilities(), ignoredMissingCapabilityRegex));
+ notification.getCapabilities()));
logger.trace("Configuration persisted successfully");
} catch (IOException e) {
throw new RuntimeException("Unable to persist configuration snapshot", e);
package org.opendaylight.controller.netconf.persist.impl;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Collections2;
import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.util.NetconfUtil;
-import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import javax.annotation.concurrent.Immutable;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collections;
+import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
+import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
@Immutable
public class ConfigPusher {
private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class);
- private final ConfigPusherConfiguration configuration;
+ private final long maxWaitForCapabilitiesMillis;
+ private final long conflictingVersionTimeoutMillis;
+ private final NetconfOperationServiceFactory configNetconfConnector;
- public ConfigPusher(ConfigPusherConfiguration configuration) {
- this.configuration = configuration;
+ public ConfigPusher(NetconfOperationServiceFactory configNetconfConnector, long maxWaitForCapabilitiesMillis,
+ long conflictingVersionTimeoutMillis) {
+ this.configNetconfConnector = configNetconfConnector;
+ this.maxWaitForCapabilitiesMillis = maxWaitForCapabilitiesMillis;
+ this.conflictingVersionTimeoutMillis = conflictingVersionTimeoutMillis;
}
- public synchronized LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponseWithRetries> pushConfigs(
- List<ConfigSnapshotHolder> configs) throws InterruptedException {
+ public synchronized LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponse> pushConfigs(List<ConfigSnapshotHolder> configs) {
logger.debug("Last config snapshots to be pushed to netconf: {}", configs);
-
- // first just make sure we can connect to netconf, even if nothing is being pushed
- {
- NetconfClient netconfClient = makeNetconfConnection(Collections.<String>emptySet());
- Util.closeClientAndDispatcher(netconfClient);
- }
- LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponseWithRetries> result = new LinkedHashMap<>();
+ LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponse> result = new LinkedHashMap<>();
// start pushing snapshots:
for (ConfigSnapshotHolder configSnapshotHolder : configs) {
- EditAndCommitResponseWithRetries editAndCommitResponseWithRetries = pushSnapshotWithRetries(configSnapshotHolder);
+ EditAndCommitResponse editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder);
logger.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result);
result.put(configSnapshotHolder, editAndCommitResponseWithRetries);
}
}
/**
- * Checks for ConflictingVersionException and retries until optimistic lock succeeds or maximal
- * number of attempts is reached.
+ * First calls {@link #getOperationServiceWithRetries(java.util.Set, String)} in order to wait until
+ * expected capabilities are present, then tries to push configuration. If {@link ConflictingVersionException}
+ * is caught, whole process is retried - new service instance need to be obtained from the factory. Closes
+ * {@link NetconfOperationService} after each use.
*/
- private synchronized EditAndCommitResponseWithRetries pushSnapshotWithRetries(ConfigSnapshotHolder configSnapshotHolder)
- throws InterruptedException {
-
- ConflictingVersionException lastException = null;
- int maxAttempts = configuration.netconfPushConfigAttempts;
-
- for (int retryAttempt = 1; retryAttempt <= maxAttempts; retryAttempt++) {
- NetconfClient netconfClient = makeNetconfConnection(configSnapshotHolder.getCapabilities());
- logger.trace("Pushing following xml to netconf {}", configSnapshotHolder);
- try {
- EditAndCommitResponse editAndCommitResponse = pushLastConfig(configSnapshotHolder, netconfClient);
- return new EditAndCommitResponseWithRetries(editAndCommitResponse, retryAttempt);
+ private synchronized EditAndCommitResponse pushConfigWithConflictingVersionRetries(ConfigSnapshotHolder configSnapshotHolder) {
+ ConflictingVersionException lastException;
+ Stopwatch stopwatch = new Stopwatch().start();
+ do {
+ try (NetconfOperationService operationService = getOperationServiceWithRetries(configSnapshotHolder.getCapabilities(), configSnapshotHolder.toString())) {
+ return pushConfig(configSnapshotHolder, operationService);
} catch (ConflictingVersionException e) {
- logger.debug("Conflicting version detected, will retry after timeout");
lastException = e;
- Thread.sleep(configuration.netconfPushConfigDelayMs);
- } catch (RuntimeException e) {
- throw new IllegalStateException("Unable to load " + configSnapshotHolder, e);
- } finally {
- Util.closeClientAndDispatcher(netconfClient);
+ logger.debug("Conflicting version detected, will retry after timeout");
+ sleep();
}
- }
- throw new IllegalStateException("Maximum attempt count has been reached for pushing " + configSnapshotHolder,
+ } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < conflictingVersionTimeoutMillis);
+ throw new IllegalStateException("Max wait for conflicting version stabilization timeout after " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms",
lastException);
}
- /**
- * @param expectedCaps capabilities that server hello must contain. Will retry until all are found or throws RuntimeException.
- * If empty set is provided, will only make sure netconf client successfuly connected to the server.
- * @return NetconfClient that has all required capabilities from server.
- */
- private synchronized NetconfClient makeNetconfConnection(Set<String> expectedCaps) throws InterruptedException {
-
- // TODO think about moving capability subset check to netconf client
- // could be utilized by integration tests
-
- final long pollingStartNanos = System.nanoTime();
- final long deadlineNanos = pollingStartNanos + TimeUnit.MILLISECONDS.toNanos(configuration.netconfCapabilitiesWaitTimeoutMs);
- int attempt = 0;
-
- NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("unknown",
- configuration.netconfAddress.getAddress().getHostAddress(),
- Integer.toString(configuration.netconfAddress.getPort()), "tcp", "persister");
-
- Set<String> latestCapabilities = null;
- while (System.nanoTime() < deadlineNanos) {
- attempt++;
- NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(configuration.eventLoopGroup,
- configuration.eventLoopGroup, additionalHeader, configuration.connectionAttemptTimeoutMs);
- NetconfClient netconfClient;
+ private NetconfOperationService getOperationServiceWithRetries(Set<String> expectedCapabilities, String idForReporting) {
+ Stopwatch stopwatch = new Stopwatch().start();
+ NotEnoughCapabilitiesException lastException;
+ do {
try {
- netconfClient = new NetconfClient(this.toString(), configuration.netconfAddress, configuration.connectionAttemptDelayMs, netconfClientDispatcher);
- } catch (IllegalStateException e) {
- logger.debug("Netconf {} was not initialized or is not stable, attempt {}", configuration.netconfAddress, attempt, e);
- netconfClientDispatcher.close();
- Thread.sleep(configuration.connectionAttemptDelayMs);
- continue;
- }
- latestCapabilities = netconfClient.getCapabilities();
- if (Util.isSubset(netconfClient, expectedCaps)) {
- logger.debug("Hello from netconf stable with {} capabilities", latestCapabilities);
- logger.trace("Session id received from netconf server: {}", netconfClient.getClientSession());
- return netconfClient;
+ return getOperationService(expectedCapabilities, idForReporting);
+ } catch (NotEnoughCapabilitiesException e) {
+ logger.debug("Not enough capabilities: " + e.toString());
+ lastException = e;
+ sleep();
}
- Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
- logger.debug("Netconf server did not provide required capabilities. Attempt {}. " +
- "Expected but not found: {}, all expected {}, current {}",
- attempt, allNotFound, expectedCaps, latestCapabilities);
- Util.closeClientAndDispatcher(netconfClient);
- Thread.sleep(configuration.connectionAttemptDelayMs);
+ } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < maxWaitForCapabilitiesMillis);
+ throw new IllegalStateException("Max wait for capabilities reached." + lastException.getMessage(), lastException);
+ }
+
+ private static class NotEnoughCapabilitiesException extends Exception {
+ private NotEnoughCapabilitiesException(String message) {
+ super(message);
}
- if (latestCapabilities == null) {
- logger.error("Could not connect to the server in {} ms", configuration.netconfCapabilitiesWaitTimeoutMs);
- throw new RuntimeException("Could not connect to netconf server");
+ }
+
+ /**
+ * Get NetconfOperationService iif all required capabilities are present.
+ *
+ * @param expectedCapabilities that must be provided by configNetconfConnector
+ * @param idForReporting
+ * @return service if capabilities are present, otherwise absent value
+ */
+ private NetconfOperationService getOperationService(Set<String> expectedCapabilities, String idForReporting) throws NotEnoughCapabilitiesException {
+ NetconfOperationService serviceCandidate = configNetconfConnector.createService(idForReporting);
+ Set<String> notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, serviceCandidate);
+ if (notFoundDiff.isEmpty()) {
+ return serviceCandidate;
+ } else {
+ serviceCandidate.close();
+ logger.debug("Netconf server did not provide required capabilities for {} " +
+ "Expected but not found: {}, all expected {}, current {}",
+ idForReporting, notFoundDiff, expectedCapabilities, serviceCandidate.getCapabilities()
+ );
+ throw new NotEnoughCapabilitiesException("Not enough capabilities for " + idForReporting + ". Expected but not found: " + notFoundDiff);
}
- Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
- logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}",
- allNotFound, expectedCaps, latestCapabilities);
- throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound);
}
- private static Set<String> computeNotFoundCapabilities(Set<String> expectedCaps, Set<String> latestCapabilities) {
- Set<String> allNotFound = new HashSet<>(expectedCaps);
- allNotFound.removeAll(latestCapabilities);
+ private static Set<String> computeNotFoundCapabilities(Set<String> expectedCapabilities, NetconfOperationService serviceCandidate) {
+ Collection<String> actual = Collections2.transform(serviceCandidate.getCapabilities(), new Function<Capability, String>() {
+ @Override
+ public String apply(Capability input) {
+ return input.getCapabilityUri();
+ }
+ });
+ Set<String> allNotFound = new HashSet<>(expectedCapabilities);
+ allNotFound.removeAll(actual);
return allNotFound;
}
+
+ private void sleep() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(e);
+ }
+ }
+
/**
* Sends two RPCs to the netconf server: edit-config and commit.
*
* @param configSnapshotHolder
- * @param netconfClient
* @throws ConflictingVersionException if commit fails on optimistic lock failure inside of config-manager
* @throws java.lang.RuntimeException if edit-config or commit fails otherwise
*/
- private synchronized EditAndCommitResponse pushLastConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfClient netconfClient)
+ private synchronized EditAndCommitResponse pushConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfOperationService operationService)
throws ConflictingVersionException {
Element xmlToBePersisted;
throw new IllegalStateException("Cannot parse " + configSnapshotHolder);
}
logger.trace("Pushing last configuration to netconf: {}", configSnapshotHolder);
-
+ Stopwatch stopwatch = new Stopwatch().start();
NetconfMessage editConfigMessage = createEditConfigMessage(xmlToBePersisted);
- // sending message to netconf
- NetconfMessage editResponseMessage;
- try {
- editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, netconfClient);
- } catch (IOException e) {
- throw new IllegalStateException("Edit-config failed on " + configSnapshotHolder, e);
- }
+ Document editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, operationService,
+ "edit-config", configSnapshotHolder.toString());
- // commit
- NetconfMessage commitResponseMessage;
- try {
- commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), netconfClient);
- } catch (IOException e) {
- throw new IllegalStateException("Edit commit succeeded, but commit failed on " + configSnapshotHolder, e);
- }
+ Document commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), operationService,
+ "commit", configSnapshotHolder.toString());
if (logger.isTraceEnabled()) {
StringBuilder response = new StringBuilder("editConfig response = {");
- response.append(XmlUtil.toString(editResponseMessage.getDocument()));
+ response.append(XmlUtil.toString(editResponseMessage));
response.append("}");
response.append("commit response = {");
- response.append(XmlUtil.toString(commitResponseMessage.getDocument()));
+ response.append(XmlUtil.toString(commitResponseMessage));
response.append("}");
logger.trace("Last configuration loaded successfully");
logger.trace("Detailed message {}", response);
+ logger.trace("Total time spent {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
return new EditAndCommitResponse(editResponseMessage, commitResponseMessage);
}
+ private NetconfOperation findOperation(NetconfMessage request, NetconfOperationService operationService) {
+ TreeMap<HandlingPriority, NetconfOperation> allOperations = new TreeMap<>();
+ Set<NetconfOperation> netconfOperations = operationService.getNetconfOperations();
+ if (netconfOperations.isEmpty()) {
+ throw new IllegalStateException("Possible code error: no config operations");
+ }
+ for (NetconfOperation netconfOperation : netconfOperations) {
+ HandlingPriority handlingPriority = netconfOperation.canHandle(request.getDocument());
+ allOperations.put(handlingPriority, netconfOperation);
+ }
+ Entry<HandlingPriority, NetconfOperation> highestEntry = allOperations.lastEntry();
+ if (highestEntry.getKey().isCannotHandle()) {
+ throw new IllegalStateException("Possible code error: operation with highest priority is CANNOT_HANDLE");
+ }
+ return highestEntry.getValue();
+ }
+
+ private Document sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfOperationService operationService,
+ String operationNameForReporting, String configIdForReporting)
+ throws ConflictingVersionException {
- private NetconfMessage sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfClient netconfClient)
- throws ConflictingVersionException, IOException {
+ NetconfOperation operation = findOperation(request, operationService);
+ Document response;
try {
- NetconfMessage netconfMessage = netconfClient.sendMessage(request,
- configuration.netconfSendMessageMaxAttempts, configuration.netconfSendMessageDelayMs);
- NetconfUtil.checkIsMessageOk(netconfMessage);
- return netconfMessage;
- } catch(ConflictingVersionException e) {
- logger.trace("conflicting version detected: {}", e.toString());
+ response = operation.handle(request.getDocument(), NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT);
+ } catch (NetconfDocumentedException | RuntimeException e) {
+ throw new IllegalStateException("Failed to send " + operationNameForReporting +
+ " for configuration " + configIdForReporting, e);
+ }
+ try {
+ return NetconfUtil.checkIsMessageOk(response);
+ } catch (ConflictingVersionException e) {
+ logger.trace("conflicting version detected: {} while committing {}", e.toString(), configIdForReporting);
throw e;
- } catch (RuntimeException | ExecutionException | InterruptedException | TimeoutException e) { // TODO: change NetconfClient#sendMessage to throw checked exceptions
- logger.debug("Error while executing netconf transaction {} to {}", request, netconfClient, e);
- throw new IOException("Failed to execute netconf transaction", e);
}
}
-
// load editConfig.xml template, populate /rpc/edit-config/config with parameter
private static NetconfMessage createEditConfigMessage(Element dataElement) {
String editConfigResourcePath = "/netconfOp/editConfig.xml";
return new NetconfMessage(doc);
} catch (IOException | SAXException e) {
// error reading the xml file bundled into the jar
- throw new RuntimeException("Error while opening local resource " + editConfigResourcePath, e);
+ throw new IllegalStateException("Error while opening local resource " + editConfigResourcePath, e);
}
}
return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
} catch (SAXException | IOException e) {
// error reading the xml file bundled into the jar
- throw new RuntimeException("Error while opening local resource " + resource, e);
+ throw new IllegalStateException("Error while opening local resource " + resource, e);
}
}
static class EditAndCommitResponse {
- private final NetconfMessage editResponse, commitResponse;
+ private final Document editResponse, commitResponse;
- EditAndCommitResponse(NetconfMessage editResponse, NetconfMessage commitResponse) {
+ EditAndCommitResponse(Document editResponse, Document commitResponse) {
this.editResponse = editResponse;
this.commitResponse = commitResponse;
}
- public NetconfMessage getEditResponse() {
+ public Document getEditResponse() {
return editResponse;
}
- public NetconfMessage getCommitResponse() {
+ public Document getCommitResponse() {
return commitResponse;
}
'}';
}
}
-
-
- static class EditAndCommitResponseWithRetries {
- private final EditAndCommitResponse editAndCommitResponse;
- private final int retries;
-
- EditAndCommitResponseWithRetries(EditAndCommitResponse editAndCommitResponse, int retries) {
- this.editAndCommitResponse = editAndCommitResponse;
- this.retries = retries;
- }
-
- public int getRetries() {
- return retries;
- }
-
- public EditAndCommitResponse getEditAndCommitResponse() {
- return editAndCommitResponse;
- }
-
- @Override
- public String toString() {
- return "EditAndCommitResponseWithRetries{" +
- "editAndCommitResponse=" + editAndCommitResponse +
- ", retries=" + retries +
- '}';
- }
- }
-
}
+++ /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.netconf.persist.impl;
-
-import io.netty.channel.EventLoopGroup;
-
-import javax.annotation.concurrent.Immutable;
-import java.net.InetSocketAddress;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Configuration properties for ConfigPusher. Contains delays and timeouts for netconf
- * connection establishment, netconf capabilities stabilization and configuration push.
- */
-@Immutable
-public final class ConfigPusherConfiguration {
-
- public static final long DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
- public static final int DEFAULT_CONNECTION_ATTEMPT_DELAY_MS = 5000;
-
- public static final int DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS = 20;
- public static final int DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS = 1000;
-
- public static final long DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(2);
-
- public static final int DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS = 30;
- public static final long DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS = TimeUnit.MINUTES.toMillis(1);
-
- final InetSocketAddress netconfAddress;
- final EventLoopGroup eventLoopGroup;
-
- /**
- * Total time to wait for capability stabilization
- */
- final long netconfCapabilitiesWaitTimeoutMs;
-
- /**
- * Delay between message send attempts
- */
- final int netconfSendMessageDelayMs;
- /**
- * Total number attempts to send a message
- */
- final int netconfSendMessageMaxAttempts;
-
- /**
- * Delay between connection establishment attempts
- */
- final int connectionAttemptDelayMs;
- /**
- * Total number of attempts to perform connection establishment
- */
- final long connectionAttemptTimeoutMs;
-
- /**
- * Total number of attempts to push configuration to netconf
- */
- final int netconfPushConfigAttempts;
- /**
- * Delay between configuration push attempts
- */
- final long netconfPushConfigDelayMs;
-
- ConfigPusherConfiguration(InetSocketAddress netconfAddress, long netconfCapabilitiesWaitTimeoutMs,
- int netconfSendMessageDelayMs, int netconfSendMessageMaxAttempts, int connectionAttemptDelayMs,
- long connectionAttemptTimeoutMs, EventLoopGroup eventLoopGroup, int netconfPushConfigAttempts,
- long netconfPushConfigDelayMs) {
- this.netconfAddress = netconfAddress;
- this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
- this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
- this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
- this.connectionAttemptDelayMs = connectionAttemptDelayMs;
- this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
- this.eventLoopGroup = eventLoopGroup;
- this.netconfPushConfigAttempts = netconfPushConfigAttempts;
- this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
- }
-}
+++ /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.netconf.persist.impl;
-
-import io.netty.channel.EventLoopGroup;
-
-import java.net.InetSocketAddress;
-
-public class ConfigPusherConfigurationBuilder {
- InetSocketAddress netconfAddress;
- EventLoopGroup eventLoopGroup;
-
- long netconfCapabilitiesWaitTimeoutMs = ConfigPusherConfiguration.DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS;
- int netconfSendMessageDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS;
- int netconfSendMessageMaxAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS;
- int connectionAttemptDelayMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_DELAY_MS;
- long connectionAttemptTimeoutMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS;
- int netconfPushConfigAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS;
- long netconfPushConfigDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS;
-
- private ConfigPusherConfigurationBuilder() {
- }
-
- public static ConfigPusherConfigurationBuilder aConfigPusherConfiguration() {
- return new ConfigPusherConfigurationBuilder();
- }
-
- public ConfigPusherConfigurationBuilder withNetconfAddress(InetSocketAddress netconfAddress) {
- this.netconfAddress = netconfAddress;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfCapabilitiesWaitTimeoutMs(long netconfCapabilitiesWaitTimeoutMs) {
- this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfSendMessageDelayMs(int netconfSendMessageDelayMs) {
- this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfSendMessageMaxAttempts(int netconfSendMessageMaxAttempts) {
- this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withConnectionAttemptDelayMs(int connectionAttemptDelayMs) {
- this.connectionAttemptDelayMs = connectionAttemptDelayMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withConnectionAttemptTimeoutMs(long connectionAttemptTimeoutMs) {
- this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withEventLoopGroup(EventLoopGroup eventLoopGroup) {
- this.eventLoopGroup = eventLoopGroup;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfPushConfigAttempts(int netconfPushConfigAttempts) {
- this.netconfPushConfigAttempts = netconfPushConfigAttempts;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfPushConfigDelayMs(long netconfPushConfigDelayMs) {
- this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
- return this;
- }
-
- public ConfigPusherConfiguration build() {
- ConfigPusherConfiguration configPusherConfiguration = new ConfigPusherConfiguration(netconfAddress,
- netconfCapabilitiesWaitTimeoutMs, netconfSendMessageDelayMs, netconfSendMessageMaxAttempts,
- connectionAttemptDelayMs, connectionAttemptTimeoutMs, eventLoopGroup, netconfPushConfigAttempts,
- netconfPushConfigDelayMs);
- return configPusherConfiguration;
- }
-}
+++ /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.netconf.persist.impl;
-
-import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-public final class Util {
- private static final Logger logger = LoggerFactory.getLogger(Util.class);
-
-
- public static boolean isSubset(NetconfClient netconfClient, Set<String> expectedCaps) {
- return isSubset(netconfClient.getCapabilities(), expectedCaps);
-
- }
-
- private static boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
- for (String exCap : expectedCaps) {
- if (currentCapabilities.contains(exCap) == false)
- return false;
- }
- return true;
- }
-
- public static void closeClientAndDispatcher(NetconfClient client) {
- NetconfClientDispatcher dispatcher = client.getNetconfClientDispatcher();
- Exception fromClient = null;
- try {
- client.close();
- } catch (Exception e) {
- fromClient = e;
- } finally {
- try {
- dispatcher.close();
- } catch (Exception e) {
- if (fromClient != null) {
- e.addSuppressed(fromClient);
- }
- throw new RuntimeException("Error closing temporary client ", e);
- }
- }
- }
-}
package org.opendaylight.controller.netconf.persist.impl.osgi;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
import org.opendaylight.controller.netconf.persist.impl.ConfigPusher;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator;
-import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.MBeanServer;
import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.concurrent.ThreadFactory;
-import java.util.regex.Pattern;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
public class ConfigPersisterActivator implements BundleActivator {
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class);
- public static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex";
-
- public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS = "maxWaitForCapabilitiesMillis";
+ public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY = "maxWaitForCapabilitiesMillis";
+ private static final long MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT = TimeUnit.MINUTES.toMillis(2);
+ public static final String CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY = "conflictingVersionTimeoutMillis";
+ private static final long CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT = TimeUnit.SECONDS.toMillis(30);
public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister";
public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass";
- public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0";
- private final MBeanServer platformMBeanServer;
+ private static final MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
- private final Optional<ConfigPusherConfiguration> initialConfigForPusher;
- private volatile ConfigPersisterNotificationHandler jmxNotificationHandler;
- private Thread initializationThread;
- private ThreadFactory initializationThreadFactory;
- private EventLoopGroup nettyThreadGroup;
- private PersisterAggregator persisterAggregator;
+ private List<AutoCloseable> autoCloseables;
- public ConfigPersisterActivator() {
- this(new ThreadFactory() {
- @Override
- public Thread newThread(Runnable initializationRunnable) {
- return new Thread(initializationRunnable, "ConfigPersister-registrator");
- }
- }, ManagementFactory.getPlatformMBeanServer(), null);
- }
-
- @VisibleForTesting
- protected ConfigPersisterActivator(ThreadFactory threadFactory, MBeanServer mBeanServer,
- ConfigPusherConfiguration initialConfigForPusher) {
- this.initializationThreadFactory = threadFactory;
- this.platformMBeanServer = mBeanServer;
- this.initialConfigForPusher = Optional.fromNullable(initialConfigForPusher);
- }
@Override
public void start(final BundleContext context) throws Exception {
logger.debug("ConfigPersister starting");
-
+ autoCloseables = new ArrayList<>();
PropertiesProviderBaseImpl propertiesProvider = new PropertiesProviderBaseImpl(context);
- final Pattern ignoredMissingCapabilityRegex = getIgnoredCapabilitiesProperty(propertiesProvider);
- persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
+ final PersisterAggregator persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
+ autoCloseables.add(persisterAggregator);
+ final long maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesMillis(propertiesProvider);
+ final List<ConfigSnapshotHolder> configs = persisterAggregator.loadLastConfigs();
+ final long conflictingVersionTimeoutMillis = getConflictingVersionTimeoutMillis(propertiesProvider);
+ logger.trace("Following configs will be pushed: {}", configs);
+ ServiceTrackerCustomizer<NetconfOperationServiceFactory, NetconfOperationServiceFactory> configNetconfCustomizer = new ServiceTrackerCustomizer<NetconfOperationServiceFactory, NetconfOperationServiceFactory>() {
+ @Override
+ public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
+ NetconfOperationServiceFactory service = reference.getBundle().getBundleContext().getService(reference);
+ final ConfigPusher configPusher = new ConfigPusher(service, maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis);
+ logger.debug("Configuration Persister got %s", service);
+ final Thread pushingThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ configPusher.pushConfigs(configs);
+ logger.info("Configuration Persister initialization completed.");
+ ConfigPersisterNotificationHandler jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator);
+ synchronized (ConfigPersisterActivator.this) {
+ autoCloseables.add(jmxNotificationHandler);
+ }
+ }
+ }, "config-pusher");
+ synchronized (ConfigPersisterActivator.this){
+ autoCloseables.add(new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ pushingThread.interrupt();
+ }
+ });
+ }
+ pushingThread.start();
+ return service;
+ }
- final ConfigPusher configPusher = new ConfigPusher(getConfigurationForPusher(context, propertiesProvider));
+ @Override
+ public void modifiedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
+ }
- // offload initialization to another thread in order to stop blocking activator
- Runnable initializationRunnable = new Runnable() {
@Override
- public void run() {
- try {
- configPusher.pushConfigs(persisterAggregator.loadLastConfigs());
- jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator,
- ignoredMissingCapabilityRegex);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- logger.error("Interrupted while waiting for netconf connection");
- // uncaught exception handler will deal with this failure
- throw new RuntimeException("Interrupted while waiting for netconf connection", e);
- }
- logger.info("Configuration Persister initialization completed.");
+ public void removedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
}
};
- initializationThread = initializationThreadFactory.newThread(initializationRunnable);
- initializationThread.start();
- }
-
- private Pattern getIgnoredCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
- String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX);
- String regex;
- if (regexProperty != null) {
- regex = regexProperty;
- } else {
- regex = DEFAULT_IGNORED_REGEX;
- }
- return Pattern.compile(regex);
- }
+ Filter filter = context.createFilter(getFilterString());
- private Optional<Long> getMaxWaitForCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
- String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS);
- return Optional.fromNullable(timeoutProperty == null ? null : Long.valueOf(timeoutProperty));
+ ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> tracker =
+ new ServiceTracker<>(context, filter, configNetconfCustomizer);
+ tracker.open();
}
- private ConfigPusherConfiguration getConfigurationForPusher(BundleContext context,
- PropertiesProviderBaseImpl propertiesProvider) {
-
- // If configuration was injected via constructor, use it
- if(initialConfigForPusher.isPresent())
- return initialConfigForPusher.get();
-
- Optional<Long> maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesProperty(propertiesProvider);
- final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
- "Netconf is not configured, persister is not operational", true);
-
- nettyThreadGroup = new NioEventLoopGroup();
- ConfigPusherConfigurationBuilder configPusherConfigurationBuilder = ConfigPusherConfigurationBuilder.aConfigPusherConfiguration();
+ @VisibleForTesting
+ public static String getFilterString() {
+ return "(&" +
+ "(" + Constants.OBJECTCLASS + "=" + NetconfOperationServiceFactory.class.getName() + ")" +
+ "(name" + "=" + "config-netconf-connector" + ")" +
+ ")";
+ }
- if(maxWaitForCapabilitiesMillis.isPresent())
- configPusherConfigurationBuilder.withNetconfCapabilitiesWaitTimeoutMs(maxWaitForCapabilitiesMillis.get());
+ private long getConflictingVersionTimeoutMillis(PropertiesProviderBaseImpl propertiesProvider) {
+ String timeoutProperty = propertiesProvider.getProperty(CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY);
+ return timeoutProperty == null ? CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT : Long.valueOf(timeoutProperty);
+ }
- return configPusherConfigurationBuilder
- .withEventLoopGroup(nettyThreadGroup)
- .withNetconfAddress(address)
- .build();
+ private long getMaxWaitForCapabilitiesMillis(PropertiesProviderBaseImpl propertiesProvider) {
+ String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY);
+ return timeoutProperty == null ? MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT : Long.valueOf(timeoutProperty);
}
@Override
- public void stop(BundleContext context) throws Exception {
- initializationThread.interrupt();
- if (jmxNotificationHandler != null) {
- jmxNotificationHandler.close();
+ public synchronized void stop(BundleContext context) throws Exception {
+ Exception lastException = null;
+ for (AutoCloseable autoCloseable : autoCloseables) {
+ try {
+ autoCloseable.close();
+ } catch (Exception e) {
+ if (lastException == null) {
+ lastException = e;
+ } else {
+ lastException.addSuppressed(e);
+ }
+ }
+ }
+ if (lastException != null) {
+ throw lastException;
}
- if(nettyThreadGroup!=null)
- nettyThreadGroup.shutdownGracefully();
- persisterAggregator.close();
}
}
import com.google.common.collect.Sets;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
-import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Element;
import java.io.IOException;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-import java.util.regex.Pattern;
import static org.junit.Assert.assertEquals;
public class CapabilityStrippingConfigSnapshotHolderTest {
@Test
- public void testCapabilityStripping() throws Exception {
+ public void testCapabilityStripping() throws Exception {
Set<String> allCapabilities = readLines("/capabilities-all.txt");
Set<String> expectedCapabilities = readLines("/capabilities-stripped.txt");
String snapshotAsString = readToString("/snapshot.xml");
Element element = XmlUtil.readXmlToElement(snapshotAsString);
- {
- CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
- element, allCapabilities, Pattern.compile(
- ConfigPersisterActivator.DEFAULT_IGNORED_REGEX
- ));
- assertEquals(expectedCapabilities, tested.getCapabilities());
- assertEquals(Collections.emptySet(), tested.getMissingNamespaces());
- }
- {
- // test regex
- CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
- element, allCapabilities, Pattern.compile(
- "^bar"
- ));
- assertEquals(expectedCapabilities, tested.getCapabilities());
- assertEquals(Sets.newHashSet(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX.substring(1)),
- tested.getMissingNamespaces());
- }
+ CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
+ element, allCapabilities);
+ assertEquals(expectedCapabilities, tested.getCapabilities());
+
+ Set<String> obsoleteCapabilities = Sets.difference(allCapabilities, expectedCapabilities);
+
+ assertEquals(obsoleteCapabilities, tested.getObsoleteCapabilities());
}
private Set<String> readLines(String fileName) throws IOException {
*/
package org.opendaylight.controller.netconf.persist.impl.osgi;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeoutException;
-
-import javax.management.MBeanServer;
-
+import com.google.common.collect.Sets;
import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.Before;
import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
import org.opendaylight.controller.config.api.ConflictingVersionException;
-import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.persist.impl.osgi.MockedBundleContext.DummyAdapterWithInitialSnapshot;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
-import com.google.common.collect.Lists;
-import io.netty.channel.nio.NioEventLoopGroup;
+import javax.management.MBeanServer;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
public class ConfigPersisterTest {
+ private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterTest.class);
private MockedBundleContext ctx;
private ConfigPersisterActivator configPersisterActivator;
private static final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ private TestingExceptionHandler handler;
- private static final String NETCONF_ADDRESS = "localhost";
- private static final String NETCONF_PORT = "18383";
- private static NioEventLoopGroup eventLoopGroup;
- private void setUpContextAndStartPersister(Thread.UncaughtExceptionHandler exHandler, String requiredCapability, ConfigPusherConfiguration configuration)
- throws Exception {
- MockedBundleContext.DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
- ctx = new MockedBundleContext(NETCONF_ADDRESS, NETCONF_PORT);
- configPersisterActivator = new ConfigPersisterActivator(getThreadFactory(exHandler), mBeanServer,
- configuration);
+ private void setUpContextAndStartPersister(String requiredCapability) throws Exception {
+ DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
+ ctx = new MockedBundleContext(1000, 1000);
+ configPersisterActivator = new ConfigPersisterActivator();
configPersisterActivator.start(ctx.getBundleContext());
}
- @BeforeClass
- public static void setUp() throws Exception {
- eventLoopGroup = new NioEventLoopGroup();
+ @Before
+ public void setUp() {
+ handler = new TestingExceptionHandler();
+ Thread.setDefaultUncaughtExceptionHandler(handler);
}
@After
public void tearDown() throws Exception {
+ Thread.setDefaultUncaughtExceptionHandler(null);
configPersisterActivator.stop(ctx.getBundleContext());
}
- @AfterClass
- public static void closeNettyGroup() throws Exception {
- eventLoopGroup.shutdownGracefully();
- }
-
- @Test
- public void testPersisterNetconfNotStarting() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
-
- setUpContextAndStartPersister(handler, "cap2", getConfiguration(100, 100).build());
-
- waitTestToFinish(2000);
-
- handler.assertException("connect to netconf endpoint", RuntimeException.class,
- "Could not connect to netconf server");
- }
-
@Test
public void testPersisterNotAllCapabilitiesProvided() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfiguration(500, 1000)
- .withNetconfCapabilitiesWaitTimeoutMs(1000).build();
-
- setUpContextAndStartPersister(handler, "required-cap", cfg);
+ setUpContextAndStartPersister("required-cap");
+ Thread.sleep(2000);
+ handler.assertException(IllegalStateException.class, "Max wait for capabilities reached.Not enough capabilities " +
+ "for <data><config-snapshot/></data>. Expected but not found: [required-cap]");
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
-
- waitTestToFinish(2500);
-
- handler.assertException("retrieve required capabilities from netconf endpoint", RuntimeException.class,
- "Expected but not found:[required-cap]");
- }
}
@Test
- public void testPersisterNoResponseFromNetconfAfterEdit() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
-
- waitTestToFinish(3000);
-
- handler.assertException("receive response from netconf endpoint", IllegalStateException.class,
- "Unable to load", TimeoutException.class,
- null, 3);
-
- assertEquals(1 + 2, endpoint.getReceivedMessages().size());
- assertHelloMessage(endpoint.getReceivedMessages().get(1));
- assertEditMessage(endpoint.getReceivedMessages().get(2));
- }
+ public void testPersisterSuccessfulPush() throws Exception {
+ setUpContextAndStartPersister("cap1");
+ NetconfOperationService service = getWorkingService(getOKDocument());
+ doReturn(service).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(2000);
+ assertCannotRegisterAsJMXListener_pushWasSuccessful();
}
- private ConfigPusherConfiguration getConfigurationWithOnePushAttempt() {
- return getConfiguration(500, 1000)
- .withNetconfCapabilitiesWaitTimeoutMs(1000)
- .withNetconfPushConfigAttempts(1)
- .withNetconfPushConfigDelayMs(100)
- .withNetconfSendMessageMaxAttempts(3)
- .withNetconfSendMessageDelayMs(500).build();
+ // this means pushing of config was successful
+ public void assertCannotRegisterAsJMXListener_pushWasSuccessful() {
+ handler.assertException(RuntimeException.class, "Cannot register as JMX listener to netconf");
}
- @Test
- public void testPersisterSuccessfulPush() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
- MockNetconfEndpoint.okMessage)) {
+ public NetconfOperationService getWorkingService(Document document) throws SAXException, IOException, NetconfDocumentedException {
+ NetconfOperationService service = mock(NetconfOperationService.class);
+ Capability capability = mock(Capability.class);
+ doReturn(Sets.newHashSet(capability)).when(service).getCapabilities();
+ doReturn("cap1").when(capability).getCapabilityUri();
- waitTestToFinish(4000);
- handler.assertException("register as JMX listener", RuntimeException.class,
- "Cannot register as JMX listener to netconf");
-
- assertEquals(1 + 3, endpoint.getReceivedMessages().size());
- assertCommitMessage(endpoint.getReceivedMessages().get(3));
- }
+ NetconfOperation mockedOperation = mock(NetconfOperation.class);
+ doReturn(Sets.newHashSet(mockedOperation)).when(service).getNetconfOperations();
+ doReturn(HandlingPriority.getHandlingPriority(1)).when(mockedOperation).canHandle(any(Document.class));
+ doReturn(document).when(mockedOperation).handle(any(Document.class), any(NetconfOperationChainedExecution.class));
+ doNothing().when(service).close();
+ return service;
}
- private ConfigPusherConfiguration getConfigurationForSuccess() {
- return getConfiguration(500, 1000)
- .withNetconfCapabilitiesWaitTimeoutMs(1000)
- .withNetconfPushConfigAttempts(3)
- .withNetconfPushConfigDelayMs(100)
- .withNetconfSendMessageMaxAttempts(3)
- .withNetconfSendMessageDelayMs(500).build();
+ private Document getOKDocument() throws SAXException, IOException {
+ return XmlUtil.readXmlToDocument(
+ "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<ok/>\n" +
+ "</rpc-reply>"
+ );
}
+
@Test
public void testPersisterConflictingVersionException() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
- MockNetconfEndpoint.conflictingVersionErrorMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier();) {
-
- Thread.sleep(4000);
-
- handler.assertException("register as JMX listener", IllegalStateException.class,
- "Maximum attempt count has been reached for pushing", ConflictingVersionException.class, "Optimistic lock failed", 1);
-
- assertEquals(1 + 3, endpoint.getReceivedMessages().size());
- assertCommitMessage(endpoint.getReceivedMessages().get(3));
- }
+ setUpContextAndStartPersister("cap1");
+ NetconfOperationService service = getWorkingService(getConflictVersionDocument());
+ doReturn(service).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(2000);
+ handler.assertException(IllegalStateException.class, "Max wait for conflicting version stabilization timeout");
}
- @Test
- public void testPersisterConflictingVersionExceptionThenSuccess() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- MockNetconfEndpoint.MessageSequence conflictingMessageSequence = new MockNetconfEndpoint.MessageSequence(
- MockNetconfEndpoint.okMessage, MockNetconfEndpoint.conflictingVersionErrorMessage);
- MockNetconfEndpoint.MessageSequence okMessageSequence = new MockNetconfEndpoint.MessageSequence(
- MockNetconfEndpoint.okMessage, MockNetconfEndpoint.okMessage);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1",
- Lists.newArrayList(conflictingMessageSequence, okMessageSequence));
- DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
-
- Thread.sleep(4000);
-
- handler.assertNoException();
-
- assertEquals(1 + 3/*Hello + Edit + Commit*/ + 3/*Hello + Edit + Commit*/, endpoint.getReceivedMessages().size());
- assertCommitMessage(endpoint.getReceivedMessages().get(6));
- }
+ private Document getConflictVersionDocument() throws SAXException, IOException {
+ return XmlUtil.readXmlToDocument(
+ "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<rpc-error><error-info><error>" +
+ ConflictingVersionException.class.getCanonicalName() +
+ "</error></error-info></rpc-error>\n" +
+ "</rpc-reply>"
+ );
}
@Test
- public void testPersisterSuccessfulPushAndSuccessfulJMXRegistration() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
- MockNetconfEndpoint.okMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
-
- Thread.sleep(2000);
-
- handler.assertNoException();
-
- assertEquals(1 + 3, endpoint.getReceivedMessages().size());
- }
- }
-
- private ConfigPusherConfigurationBuilder getConfiguration(int connectionAttemptDelayMs, int connectionAttemptTimeoutMs) {
- return ConfigPusherConfigurationBuilder.aConfigPusherConfiguration()
- .withEventLoopGroup(eventLoopGroup)
- .withConnectionAttemptDelayMs(connectionAttemptDelayMs)
- .withConnectionAttemptTimeoutMs(connectionAttemptTimeoutMs)
- .withNetconfCapabilitiesWaitTimeoutMs(44)
- .withNetconfAddress(new InetSocketAddress(NETCONF_ADDRESS, Integer.valueOf(NETCONF_PORT)));
+ public void testSuccessConflictingVersionException() throws Exception {
+ setUpContextAndStartPersister("cap1");
+ doReturn(getWorkingService(getConflictVersionDocument())).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(500);
+ // working service:
+ logger.info("Switching to working service **");
+ doReturn(getWorkingService(getOKDocument())).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(1000);
+ assertCannotRegisterAsJMXListener_pushWasSuccessful();
}
- private void waitTestToFinish(int i) throws InterruptedException {
- Thread.sleep(i);
- }
-
-
- private DefaultCommitNotificationProducer startJMXCommitNotifier() {
- return new DefaultCommitNotificationProducer(mBeanServer);
- }
-
- private void assertEditMessage(String netconfMessage) {
- assertThat(netconfMessage,
- JUnitMatchers.containsString(MockedBundleContext.DummyAdapterWithInitialSnapshot.CONFIG_SNAPSHOT));
- }
-
- private void assertCommitMessage(String netconfMessage) {
- assertThat(netconfMessage, JUnitMatchers.containsString("<commit"));
- }
-
- private void assertHelloMessage(String netconfMessage) {
- assertThat(netconfMessage,
- JUnitMatchers.containsString("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"));
- assertThat(netconfMessage, JUnitMatchers.containsString("<capability>"));
- }
-
- private MockNetconfEndpoint startMockNetconfEndpoint(String capability, List<MockNetconfEndpoint.MessageSequence> messageSequences) {
- // Add first empty sequence for testing connection created by config persister at startup
- messageSequences.add(0, new MockNetconfEndpoint.MessageSequence(Collections.<String>emptyList()));
- return new MockNetconfEndpoint(capability, NETCONF_PORT, messageSequences);
- }
-
- private MockNetconfEndpoint startMockNetconfEndpoint(String capability, String... messages) {
- return startMockNetconfEndpoint(capability, Lists.newArrayList(new MockNetconfEndpoint.MessageSequence(messages)));
- }
-
- public ThreadFactory getThreadFactory(final Thread.UncaughtExceptionHandler exHandler) {
- return new ThreadFactory() {
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(r, "config-persister-testing-activator");
- thread.setUncaughtExceptionHandler(exHandler);
- return thread;
- }
- };
- }
}
+++ /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.netconf.persist.impl.osgi;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-
-import com.google.common.collect.Lists;
-
-class MockNetconfEndpoint implements AutoCloseable {
-
- public static final int READ_SOCKET_TIMEOUT = 3000;
-
- public static final String MSG_SEPARATOR = "]]>]]>\n";
-
- private final AtomicBoolean stopped = new AtomicBoolean(false);
- private List<String> receivedMessages = Lists.newCopyOnWriteArrayList();
- private Thread innerThread;
-
- MockNetconfEndpoint(String capability, String netconfPort, List<MessageSequence> messageSequence) {
- helloMessage = helloMessage.replace("capability_place_holder", capability);
- start(netconfPort, messageSequence);
- }
-
- private String helloMessage = "<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
- "<capabilities>\n" +
- "<capability>capability_place_holder</capability>\n" +
- "</capabilities>\n" +
- "<session-id>1</session-id>\n" +
- "</hello>\n" +
- MSG_SEPARATOR;
-
- public static String conflictingVersionErrorMessage;
- static {
- try {
- conflictingVersionErrorMessage = XmlUtil.toString(XmlFileLoader
- .xmlFileToDocument("netconfMessages/conflictingversion/conflictingVersionResponse.xml")) + MSG_SEPARATOR;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static String okMessage = "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
- "<ok/>\n" +
- "</rpc-reply>" +
- MSG_SEPARATOR ;
-
- private void start(final String port, final List<MessageSequence> messagesToSend) {
- innerThread = new Thread(new Runnable() {
- @Override
- public void run() {
- int clientCounter = 0;
-
- while (stopped.get() == false) {
- try (ServerSocket s = new ServerSocket(Integer.valueOf(port))) {
- s.setSoTimeout(READ_SOCKET_TIMEOUT);
-
- Socket clientSocket = s.accept();
- clientCounter++;
- clientSocket.setSoTimeout(READ_SOCKET_TIMEOUT);
-
- PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
- BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
-
- // Negotiate
- sendMessage(out, helloMessage);
- receiveMessage(in);
-
- // Accept next message (edit-config)
- receiveMessage(in);
-
- for (String message : getMessageSequenceForClient(messagesToSend, clientCounter)) {
- sendMessage(out, message);
- receiveMessage(in);
- }
- } catch (SocketTimeoutException e) {
- // No more activity on netconf endpoint, close
- return;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- private Iterable<? extends String> getMessageSequenceForClient(List<MessageSequence> messagesToSend,
- int clientCounter) {
- if (messagesToSend.size() <= clientCounter) {
- return messagesToSend.get(messagesToSend.size() - 1).getMessages();
- } else {
- return messagesToSend.get(clientCounter - 1).getMessages();
- }
- }
-
- private void receiveMessage(BufferedReader in) throws Exception {
- String message = readMessage(in);
- if(message == null || message.equals(""))
- return;
- receivedMessages.add(message);
- }
-
- private String readMessage(BufferedReader in) throws IOException {
- int c;
- StringBuilder b = new StringBuilder();
-
- while((c = in.read()) != -1) {
- b.append((char)c);
- if(b.toString().endsWith("]]>]]>"))
- break;
- }
-
- return b.toString();
- }
-
- private void sendMessage(PrintWriter out, String message) throws InterruptedException {
- out.print(message);
- out.flush();
- }
-
- });
- innerThread.setName("Mocked-netconf-endpoint-inner-thread");
- innerThread.start();
- }
-
- public List<String> getReceivedMessages() {
- return receivedMessages;
- }
-
- public void close() throws IOException, InterruptedException {
- stopped.set(true);
- innerThread.join();
- }
-
- static class MessageSequence {
- private List<String> messages;
-
- MessageSequence(List<String> messages) {
- this.messages = messages;
- }
-
- MessageSequence(String... messages) {
- this(Lists.newArrayList(messages));
- }
-
- public Collection<String> getMessages() {
- return messages;
- }
- }
-}
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.PropertiesProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.persist.impl.DummyAdapter;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
import java.io.IOException;
+import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
final class MockedBundleContext {
-
@Mock
private BundleContext context;
+ @Mock
+ private Filter filter;
+ @Mock
+ private ServiceReference<?> serviceReference;
+ @Mock
+ private Bundle bundle;
+ @Mock
+ NetconfOperationServiceFactory serviceFactory;
+ @Mock
+ private NetconfOperationService service;
- MockedBundleContext(String netconfAddress, String netconfPort) {
+ MockedBundleContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) throws Exception {
MockitoAnnotations.initMocks(this);
- initContext(netconfAddress, netconfPort);
+ doReturn(null).when(context).getProperty(anyString());
+ initContext(maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis);
+ doReturn(filter).when(context).createFilter(ConfigPersisterActivator.getFilterString());
+ String filterString = "filter";
+ doReturn(filterString).when(filter).toString();
+ doNothing().when(context).addServiceListener(any(ServiceListener.class), eq(filterString));
+ ServiceReference<?>[] toBeReturned = {serviceReference};
+ doReturn(toBeReturned).when(context).getServiceReferences((String) null, filterString);
+ doReturn(bundle).when(serviceReference).getBundle();
+ doReturn(context).when(bundle).getBundleContext();
+ doReturn("").when(serviceReference).toString();
+ doReturn(serviceFactory).when(context).getService(any(ServiceReference.class));
+ doReturn(service).when(serviceFactory).createService(anyString());
+ doReturn(Collections.emptySet()).when(service).getCapabilities();
+ doNothing().when(service).close();
}
public BundleContext getBundleContext() {
return context;
}
- private void initContext(String netconfAddress, String netconfPort) {
- initProp(context, ConfigPersisterActivator.IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX, null);
-
- initPropNoPrefix(context, "netconf.tcp.client.address", netconfAddress);
- initPropNoPrefix(context, "netconf.tcp.client.port", netconfPort);
-
+ private void initContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) {
initProp(context, "active", "1");
initProp(context, "1." + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX, DummyAdapterWithInitialSnapshot.class.getName());
initProp(context, "1." + "readonly", "false");
initProp(context, "1." + ".properties.fileStorage", "target/configuration-persister-test/initial/");
-
+ initProp(context, ConfigPersisterActivator.MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY, String.valueOf(maxWaitForCapabilitiesMillis));
+ initProp(context, ConfigPersisterActivator.CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY, String.valueOf(conflictingVersionTimeoutMillis));
}
private void initProp(BundleContext context, String key, String value) {
@Override
public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- return Lists.newArrayList(getConfigSnapshopt());
+ return Lists.newArrayList(getConfigSnapshot());
}
@Override
return this;
}
- public ConfigSnapshotHolder getConfigSnapshopt() {
+ public ConfigSnapshotHolder getConfigSnapshot() {
return new ConfigSnapshotHolder() {
@Override
public String getConfigSnapshot() {
this.t = e;
}
+ public void assertException(Class<? extends Exception> exType, String exMessageToContain) {
+ assertException(exMessageToContain, exType, exMessageToContain);
+ }
+
public void assertException(String failMessageSuffix, Class<? extends Exception> exType, String exMessageToContain) {
if(t == null) {
fail("Should fail to " + failMessageSuffix);
*/
package org.opendaylight.controller.netconf.impl.osgi;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
import org.opendaylight.controller.netconf.api.NetconfSession;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
public class NetconfOperationRouterImpl implements NetconfOperationRouter {
return sortedPriority;
}
- public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() {
- @Override
- public boolean isExecutionTermination() {
- return true;
- }
-
- @Override
- public Document execute(Document requestMessage) throws NetconfDocumentedException {
- throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself");
- }
- };
-
private static class NetconfOperationExecution implements NetconfOperationChainedExecution {
private final NetconfOperation netconfOperation;
private NetconfOperationChainedExecution subsequentExecution;
package org.opendaylight.controller.netconf.impl.osgi;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
public class NetconfOperationServiceSnapshot implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceSnapshot.class);
Set<NetconfOperationService> services = new HashSet<>();
netconfSessionIdForReporting = getNetconfSessionIdForReporting(sessionId);
for (NetconfOperationServiceFactory factory : factories) {
- services.add(factory.createService(sessionId, netconfSessionIdForReporting));
+ services.add(factory.createService(netconfSessionIdForReporting));
}
this.services = Collections.unmodifiableSet(services);
}
private NetconfOperationServiceFactory mockOpF() {
return new NetconfOperationServiceFactory() {
@Override
- public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ public NetconfOperationService createService(String netconfSessionIdForReporting) {
return new NetconfOperationService() {
@Override
public Set<Capability> getCapabilities() {
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.spi.ModuleFactory;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.controller.netconf.client.NetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
import java.util.Collection;
import java.util.List;
import java.util.Set;
-import java.util.regex.Pattern;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any;
try (NetconfClient persisterClient = new NetconfClient("persister", tcpAddress, 4000, clientDispatcher)) {
try (ConfigPersisterNotificationHandler configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(
- platformMBeanServer, mockedAggregator, Pattern.compile(""))) {
+ platformMBeanServer, mockedAggregator)) {
try (NetconfClient netconfClient = new NetconfClient("client", tcpAddress, 4000, clientDispatcher)) {
import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
@RunWith(PaxExam.class)
systemProperty("osgi.console").value("2401"),
systemProperty("osgi.bundles.defaultStartLevel").value("4"),
systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
+ systemPackages("sun.nio.ch"),
testingModules(),
loggingModules(),
return getHandlingPriority(priority + priorityIncrease);
}
+ public boolean isCannotHandle() {
+ return this.equals(CANNOT_HANDLE);
+ }
+
@Override
public int compareTo(HandlingPriority o) {
if (this == o)
* Do not execute if this is termination point
*/
Document execute(Document requestMessage) throws NetconfDocumentedException;
+
+ public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() {
+ @Override
+ public boolean isExecutionTermination() {
+ return true;
+ }
+
+ @Override
+ public Document execute(Document requestMessage) throws NetconfDocumentedException {
+ throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself");
+ }
+ };
+
+
}
*/
public interface NetconfOperationServiceFactory {
- NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting);
+ NetconfOperationService createService(String netconfSessionIdForReporting);
}
}
@Override
- public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ public NetconfOperationService createService(String netconfSessionIdForReporting) {
return operationService;
}
}
netconf_ssh_output.setDaemon(false);
netconf_ssh_output.start();
- } catch (Throwable t){
- logger.error("SSH bridge couldn't create echo socket",t.getMessage(),t);
+ } catch (Exception t) {
+ logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
try {
if (netconf_ssh_input!=null){
Thread.currentThread().interrupt();
logger.error("netconf_ssh_output join error ",e);
}
-
}
} else {
try {
return (doc == null) ? null : new NetconfMessage(doc);
}
- public static void checkIsMessageOk(NetconfMessage responseMessage) throws ConflictingVersionException {
- XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
+ public static Document checkIsMessageOk(NetconfMessage responseMessage) throws ConflictingVersionException {
+ return checkIsMessageOk(responseMessage.getDocument());
+ }
+
+ public static Document checkIsMessageOk(Document response) throws ConflictingVersionException {
+ XmlElement element = XmlElement.fromDomDocument(response);
Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
element = element.getOnlyChildElement();
if (element.getName().equals(XmlNetconfConstants.OK)) {
- return;
+ return response;
}
if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) {
throw new ConflictingVersionException(error);
}
throw new IllegalStateException("Can not load last configuration, operation failed: "
- + XmlUtil.toString(responseMessage.getDocument()));
+ + XmlUtil.toString(response));
}
logger.warn("Can not load last configuration. Operation failed.");
throw new IllegalStateException("Can not load last configuration. Operation failed: "
- + XmlUtil.toString(responseMessage.getDocument()));
+ + XmlUtil.toString(response));
}
}
<osgi.version>5.0.0</osgi.version>
<maven.bundle.version>2.4.0</maven.bundle.version>
<slf4j.version>1.7.2</slf4j.version>
- <netconf.netty.version>4.0.10.Final</netconf.netty.version>
<salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
</properties>
import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
import org.opendaylight.controller.sal.utils.ServiceHelper;
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.CREATED).build();
}
- } catch (Throwable t) {
+ } catch (BridgeDomainConfigServiceException e) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.OK).build();
}
- } catch (Throwable t) {
+ } catch (Exception t) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.CREATED).build();
}
- } catch (Throwable t) {
+ } catch (Exception t) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.OK).build();
}
- } catch (Throwable t) {
+ } catch (Exception t) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
// switch
private ByteBuffer peerNetData; // encrypted message from the switch
private FileInputStream kfd = null, tfd = null;
+ private final String keyStoreFileDefault = "./configuration/tlsKeyStore";
+ private final String trustStoreFileDefault = "./configuration/tlsTrustStore";
+ private final String keyStorePasswordPropName = "controllerKeyStorePassword";
+ private final String trustStorePasswordPropName = "controllerTrustStorePassword";
+ private static String keyStorePassword = null;
+ private static String trustStorePassword = null;
public SecureMessageReadWriteService(SocketChannel socket, Selector selector)
throws Exception {
*/
private void createSecureChannel(SocketChannel socket) throws Exception {
String keyStoreFile = System.getProperty("controllerKeyStore");
- String keyStorePassword = System
- .getProperty("controllerKeyStorePassword");
String trustStoreFile = System.getProperty("controllerTrustStore");
- String trustStorePassword = System
- .getProperty("controllerTrustStorePassword");
+ String keyStorePasswordProp = System.getProperty(keyStorePasswordPropName);
+ String trustStorePasswordProp = System.getProperty(trustStorePasswordPropName);
if (keyStoreFile != null) {
keyStoreFile = keyStoreFile.trim();
+ } else {
+ keyStoreFile = keyStoreFileDefault;
}
if ((keyStoreFile == null) || keyStoreFile.isEmpty()) {
throw new FileNotFoundException("TLS KeyStore file not found.");
}
+
+ if ((keyStorePassword == null) || ((keyStorePasswordProp != null) && !keyStorePasswordProp.isEmpty())) {
+ keyStorePassword = keyStorePasswordProp;
+ }
if (keyStorePassword != null) {
keyStorePassword = keyStorePassword.trim();
+ System.setProperty(keyStorePasswordPropName, "");
}
if ((keyStorePassword == null) || keyStorePassword.isEmpty()) {
throw new FileNotFoundException("TLS KeyStore Password not provided.");
}
if (trustStoreFile != null) {
trustStoreFile = trustStoreFile.trim();
+ } else {
+ trustStoreFile = trustStoreFileDefault;
}
if ((trustStoreFile == null) || trustStoreFile.isEmpty()) {
throw new FileNotFoundException("TLS TrustStore file not found");
}
+
+ if ((trustStorePassword == null) || ((trustStorePasswordProp != null) && !trustStorePasswordProp.isEmpty())) {
+ trustStorePassword = trustStorePasswordProp;
+ }
if (trustStorePassword != null) {
trustStorePassword = trustStorePassword.trim();
+ System.setProperty(trustStorePasswordPropName, "");
}
if ((trustStorePassword == null) || trustStorePassword.isEmpty()) {
throw new FileNotFoundException("TLS TrustStore Password not provided.");
--- /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.sal.networkconfig.bridgedomain;
+
+/**
+ * Exception thrown by IPluginInBridgeDomainConfigService implementations.
+ */
+public class BridgeDomainConfigServiceException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public BridgeDomainConfigServiceException(String message) {
+ super(message);
+ }
+
+ public BridgeDomainConfigServiceException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
+
* @note This method will return false if one or more of the supplied params is not supported by the
* protocol plugin that serves the Node.
*/
- public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws Throwable;
+ public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws BridgeDomainConfigServiceException;
/**
* Delete a Bridge Domain
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
public class BridgeDomainConfigService implements IBridgeDomainConfigService {
protected static final Logger logger = LoggerFactory
.getLogger(BridgeDomainConfigService.class);
- private ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
+ private final ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
new ConcurrentHashMap<String, IPluginInBridgeDomainConfigService>();
void setPluginInService (Map props, IPluginInBridgeDomainConfigService s) {
@Override
public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params)
- throws Throwable {
+ throws BridgeDomainConfigServiceException {
if (pluginService != null) {
IPluginInBridgeDomainConfigService plugin = this.pluginService.get(node.getType());
if (plugin != null) {
import java.util.Hashtable;
import org.apache.felix.dm.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
import org.opendaylight.controller.hosttracker.IfIptoHost;
import org.opendaylight.controller.switchmanager.IInventoryListener;
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class Activator extends ComponentActivatorAbstractBase {
protected static final Logger logger = LoggerFactory
* instantiated in order to get an fully working implementation
* Object
*/
+ @Override
public Object[] getImplementations() {
Object[] res = { SimpleForwardingImpl.class,
SimpleBroadcastHandlerImpl.class };
* also optional per-container different behavior if needed, usually
* should not be the case though.
*/
+ @Override
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(SimpleForwardingImpl.class)) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("salListenerName", "simpleforwarding");
+
// export the service
c.setInterface(new String[] { IInventoryListener.class.getName(),
IfNewHostNotify.class.getName(),
- IListenRoutingUpdates.class.getName() }, null);
+ IListenRoutingUpdates.class.getName(),
+ IListenDataPacket.class.getName() }, props);
c.add(createContainerServiceDependency(containerName).setService(
IClusterContainerServices.class).setCallbacks(
c.add(createContainerServiceDependency(containerName).setService(
IRouting.class).setCallbacks("setRouting", "unsetRouting")
.setRequired(false));
- }else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
+ c.add(createContainerServiceDependency(containerName).setService(
+ IDataPacketService.class).setCallbacks("setDataPacketService",
+ "unsetDataPacketService").setRequired(false));
+
+ } else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
Dictionary<String, String> props = new Hashtable<String, String>();
props.put("salListenerName", "simplebroadcasthandler");
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.opendaylight.controller.sal.packet.Ethernet;
-import org.opendaylight.controller.sal.packet.IDataPacketService;
-import org.opendaylight.controller.sal.packet.IListenDataPacket;
import org.opendaylight.controller.sal.core.ConstructionException;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
import org.opendaylight.controller.sal.packet.Packet;
import org.opendaylight.controller.sal.packet.PacketResult;
import org.opendaylight.controller.sal.packet.RawPacket;
public PacketResult receiveDataPacket(RawPacket inPkt) {
/*
* note that this assumes that the protocol plugin will do appropriate
- * filtering to ensure that this only receives packets for it's
+ * filtering to ensure that this only receives packets for its
* container.
*/
lock.writeLock().unlock();
}
+ @Override
public void setMode(BroadcastMode m) {
lock.writeLock().lock();
mode = m;
package org.opendaylight.controller.samples.simpleforwarding.internal;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
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.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
import org.opendaylight.controller.sal.routing.IListenRoutingUpdates;
import org.opendaylight.controller.sal.routing.IRouting;
import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.NetUtils;
import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
import org.opendaylight.controller.sal.utils.Status;
import org.opendaylight.controller.samples.simpleforwarding.HostNodePair;
* installs those rules using <tt>installPerHostRules()</tt>.
*/
public class SimpleForwardingImpl implements IfNewHostNotify,
- IListenRoutingUpdates, IInventoryListener {
- private static Logger log = LoggerFactory
- .getLogger(SimpleForwardingImpl.class);
+ IListenRoutingUpdates, IInventoryListener, IListenDataPacket {
+ private static Logger log = LoggerFactory.getLogger(SimpleForwardingImpl.class);
private static short DEFAULT_IPSWITCH_PRIORITY = 1;
- private static String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
+ static final String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
private IfIptoHost hostTracker;
private IForwardingRulesManager frm;
private ITopologyManager topologyManager;
private Map<Node, List<FlowEntry>> tobePrunedPos = new HashMap<Node, List<FlowEntry>>();
private IClusterContainerServices clusterContainerService = null;
private ISwitchManager switchManager;
+ private IDataPacketService dataPacketService;
/**
* Return codes from the programming of the perHost rules in HW
public enum RulesProgrammingReturnCode {
SUCCESS, FAILED_FEW_SWITCHES, FAILED_ALL_SWITCHES, FAILED_WRONG_PARAMS
}
+ public void setDataPacketService(IDataPacketService s) {
+ log.debug("Setting dataPacketService");
+ this.dataPacketService = s;
+ }
+
+ public void unsetDataPacketService(IDataPacketService s) {
+ if (this.dataPacketService == s) {
+ this.dataPacketService = null;
+ }
+ }
public void setRouting(IRouting routing) {
+ log.debug("Setting routing");
this.routing = routing;
}
}
}
- public ITopologyManager getTopologyManager() {
- return topologyManager;
- }
-
public void setTopologyManager(ITopologyManager topologyManager) {
log.debug("Setting topologyManager");
this.topologyManager = topologyManager;
*
* @return a return code that convey the programming status of the HW
*/
- private RulesProgrammingReturnCode uninstallPerHostRules(
- HostNodeConnector host) {
+ private RulesProgrammingReturnCode uninstallPerHostRules(HostNodeConnector host) {
RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
Map<NodeConnector, FlowEntry> pos;
FlowEntry po;
for (Node swId : switches) {
List<FlowEntry> pl = tobePrunedPos.get(swId);
if (pl != null) {
- log
- .debug(
- "Policies for Switch: {} in the list to be deleted: {}",
- swId, pl);
+ log.debug("Policies for Switch: {} in the list to be deleted: {}", swId, pl);
Iterator<FlowEntry> plIter = pl.iterator();
//for (Policy po: pl) {
while (plIter.hasNext()) {
FlowEntry po = plIter.next();
- log.error("Removing Policy, Switch: {} Policy: {}", swId,
- po);
+ log.error("Removing Policy, Switch: {} Policy: {}", swId, po);
this.frm.uninstallFlowEntry(po);
plIter.remove();
}
this.switchManager = null;
}
}
+
+ @Override
+ public PacketResult receiveDataPacket(RawPacket inPkt) {
+ if (inPkt == null) {
+ return PacketResult.IGNORED;
+ }
+ log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
+ Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
+ if (formattedPak instanceof Ethernet) {
+ Object nextPak = formattedPak.getPayload();
+ if (nextPak instanceof IPv4) {
+ log.trace("Handle punted IP packet: {}", formattedPak);
+ handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector());
+ }
+ }
+ return PacketResult.IGNORED;
+
+ }
+
+ private void handlePuntedIPPacket(IPv4 pkt, NodeConnector incomingNodeConnector) {
+ InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
+ if (dIP == null || hostTracker == null) {
+ log.debug("Invalid param(s) in handlePuntedIPPacket.. DestIP: {}. hostTracker: {}", dIP, hostTracker);
+ return;
+ }
+ HostNodeConnector destHost = hostTracker.hostFind(dIP);
+ if (destHost != null
+ && (routing == null ||
+ routing.getRoute(incomingNodeConnector.getNode(), destHost.getnodeconnectorNode()) != null)) {
+
+ log.trace("Host {} is at {}", dIP, destHost.getnodeConnector());
+ HostNodePair key = new HostNodePair(destHost, incomingNodeConnector.getNode());
+
+ // If SimpleForwarding is aware of this host, it will try to install
+ // a path. Forward packet until it's done.
+ if (dataPacketService != null && this.rulesDB.containsKey(key)) {
+
+
+ /*
+ * if we know where the host is and there's a path from where this
+ * packet was punted to where the host is, then attempt best effort delivery to the host
+ */
+ NodeConnector nc = destHost.getnodeConnector();
+ log.trace("Forwarding punted IP received at {} to {}", incomingNodeConnector, nc);
+ // re-encode the Ethernet packet (the parent of the IPv4 packet)
+ RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
+ rp.setOutgoingNodeConnector(nc);
+ this.dataPacketService.transmitDataPacket(rp);
+ }
+
+ }
+ }
}
return result;
}
- @RequestMapping(value = "/connect/{nodeId}", method = RequestMethod.POST)
+ @RequestMapping(value = "/connect/{nodeId:.+}", method = RequestMethod.POST)
@ResponseBody
public Status addNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
@RequestParam(required = true) String ipAddress, @RequestParam(required = true) String port,
return new Status(StatusCode.SUCCESS);
}
- @RequestMapping(value = "/disconnect/{nodeId}", method = RequestMethod.POST)
+ @RequestMapping(value = "/disconnect/{nodeId:.+}", method = RequestMethod.POST)
@ResponseBody
public Status removeNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
@RequestParam(required = true) String nodeType) {
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.SupportedFlowActions;
import org.opendaylight.controller.sal.authorization.Privilege;
import org.opendaylight.controller.sal.authorization.UserLevel;
import org.opendaylight.controller.sal.core.Description;
return nodes;
}
-
@RequestMapping(value = "/flow", method = RequestMethod.POST)
@ResponseBody
public String actionFlow(@RequestParam(required = true) String action, @RequestParam(required = false) String body,
}
}
+ @RequestMapping(value = "/valid-flows/{nodeId}")
+ @ResponseBody
+ public Object getValidActions(HttpServletRequest request, @RequestParam(required = false) String container,
+ @PathVariable("nodeId") String nodeId) {
+ String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+
+ // Authorization check
+ String userName = request.getUserPrincipal().getName();
+ if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+ return "Operation not authorized";
+ }
+
+ ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+ if (switchManager == null) {
+ return null;
+ }
+
+ Map<String, String> result = new TreeMap<String, String>();
+
+ Node node = Node.fromString(nodeId);
+ SupportedFlowActions supportedFlows = (SupportedFlowActions) switchManager.getNodeProp(node, "supportedFlowActions");
+ List<Class<? extends Action>> actions = supportedFlows.getActions();
+ for (Class<? extends Action> action : actions) {
+ if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Drop.class)) {
+ result.put(ActionType.DROP.toString(), "Drop");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Loopback.class)) {
+ result.put(ActionType.LOOPBACK.toString(), "Loopback");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Flood.class)) {
+ result.put(ActionType.FLOOD.toString(), "Flood");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.FloodAll.class)) {
+ result.put(ActionType.FLOOD_ALL.toString(), "Flood All");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Controller.class)) {
+ result.put(ActionType.CONTROLLER.toString(), "Controller");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SwPath.class)) {
+ result.put(ActionType.SW_PATH.toString(), "Software Path");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.HwPath.class)) {
+ result.put(ActionType.HW_PATH.toString(), "Hardware Path");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Output.class)) {
+ result.put(ActionType.OUTPUT.toString(), "Output");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Enqueue.class)) {
+ result.put(ActionType.ENQUEUE.toString(), "Enqueue");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlSrc.class)) {
+ result.put(ActionType.SET_DL_SRC.toString(), "Set Datalayer Source Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlDst.class)) {
+ result.put(ActionType.SET_DL_DST.toString(), "Set Datalayer Destination Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanId.class)) {
+ result.put(ActionType.SET_VLAN_ID.toString(), "Set VLAN ID");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanPcp.class)) {
+ result.put(ActionType.SET_VLAN_PCP.toString(), "Set VLAN Priority");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanCfi.class)) {
+ result.put(ActionType.SET_VLAN_CFI.toString(), "Set VLAN CFI");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PopVlan.class)) {
+ result.put(ActionType.POP_VLAN.toString(), "Pop VLAN");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PushVlan.class)) {
+ result.put(ActionType.PUSH_VLAN.toString(), "Push VLAN");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlType.class)) {
+ result.put(ActionType.SET_DL_TYPE.toString(), "Set EtherType");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwSrc.class)) {
+ result.put(ActionType.SET_NW_SRC.toString(), "Set Network Source Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwDst.class)) {
+ result.put(ActionType.SET_NW_DST.toString(), "Set Network Destination Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwTos.class)) {
+ result.put(ActionType.SET_NW_TOS.toString(), "Modify ToS Bits");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpSrc.class)) {
+ result.put(ActionType.SET_TP_SRC.toString(), "Modify Transport Source Port");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpDst.class)) {
+ result.put(ActionType.SET_TP_DST.toString(), "Modify Transport Destination Port");
+ }
+ }
+
+ return result;
+ }
+
+ private boolean actionCompare(String name, ActionType type) {
+ return name.equals(type.getId().toLowerCase());
+ }
+
private String getNodeDesc(Node node, ISwitchManager switchManager) {
Description desc = (Description) switchManager.getNodeProp(node, Description.propertyName);
String description = (desc == null) ? "" : desc.getValue();
// specify dashlets and layouts
one.f.dashlet = {
- flows : {
- id : 'flows',
- name : 'Flow Entries'
- },
- nodes : {
- id : 'nodes',
- name : 'Nodes'
- },
- detail : {
- id : 'detail',
- name : 'Flow Detail'
- }
+ flows : {
+ id : 'flows',
+ name : 'Flow Entries'
+ },
+ nodes : {
+ id : 'nodes',
+ name : 'Nodes'
+ },
+ detail : {
+ id : 'detail',
+ name : 'Flow Detail'
+ }
};
one.f.menu = {
- left : {
- top : [
- one.f.dashlet.flows
- ],
- bottom : [
- one.f.dashlet.nodes
- ]
- },
- right : {
- top : [],
- bottom : [
- one.f.dashlet.detail
- ]
- }
+ left : {
+ top : [
+ one.f.dashlet.flows
+ ],
+ bottom : [
+ one.f.dashlet.nodes
+ ]
+ },
+ right : {
+ top : [],
+ bottom : [
+ one.f.dashlet.detail
+ ]
+ }
};
one.f.address = {
- root : "/controller/web/flows",
- flows : {
- main : "/main",
- flows : "/node-flows",
- nodes : "/node-ports",
- flow : "/flow",
- modifyFlow : "/modifyFlow",
- deleteFlows:"/flow/deleteFlows"
- }
+ root : "/controller/web/flows",
+ flows : {
+ main : "/main",
+ flows : "/node-flows",
+ nodes : "/node-ports",
+ flow : "/flow",
+ modifyFlow : "/modifyFlow",
+ deleteFlows:"/flow/deleteFlows"
+ }
}
/** NODES **/
one.f.nodes = {
- id : {
- dashlet: {
- datagrid: "one_f_nodes_id_dashlet_datagrid"
- }
- },
- registry : {},
- dashlet : function($dashlet) {
- var $h4 = one.lib.dashlet.header("Nodes");
- $dashlet.append($h4);
-
- one.f.nodes.ajax.dashlet(function(data) {
- var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
- searchable: true,
- filterable: false,
- pagination: true,
- flexibleRowsPerPage: true
- }, "table-striped table-condensed");
- $dashlet.append($gridHTML);
- var dataSource = one.f.nodes.data.nodesDataGrid(data);
- $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
- });
- },
- ajax : {
- dashlet : function(callback) {
- $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
- callback(data);
- });
- }
- },
- data : {
- nodesDataGrid: function(data) {
- var gridData = [];
- $.each(data, function(nodeName, flow) {
- var nodeFlowObject = {};
- nodeFlowObject["nodeName"] = nodeName;
- nodeFlowObject["flows"] = flow;
- nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
- gridData.push(nodeFlowObject);
- });
+ id : {
+ dashlet: {
+ datagrid: "one_f_nodes_id_dashlet_datagrid"
+ }
+ },
+ registry : {},
+ dashlet : function($dashlet) {
+ var $h4 = one.lib.dashlet.header("Nodes");
+ $dashlet.append($h4);
- var source = new StaticDataSource({
- columns: [
- {
- property: 'nodeName',
- label: 'Node',
- sortable: true
- },
- {
- property: 'flows',
- label: 'Flows',
- sortable: true
- }
- ],
- data: gridData,
- delay: 0
- });
- return source;
- }
- },
- body : {
- dashlet : function(body, callback) {
- var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
+ one.f.nodes.ajax.dashlet(function(data) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.nodes.data.nodesDataGrid(data);
+ $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
+ });
+ },
+ ajax : {
+ dashlet : function(callback) {
+ $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
+ callback(data);
+ });
+ }
+ },
+ data : {
+ nodesDataGrid: function(data) {
+ var gridData = [];
+ $.each(data, function(nodeName, flow) {
+ var nodeFlowObject = {};
+ nodeFlowObject["nodeName"] = nodeName;
+ nodeFlowObject["flows"] = flow;
+ nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
+ gridData.push(nodeFlowObject);
+ });
+
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Node',
+ sortable: true
+ },
+ {
+ property: 'flows',
+ label: 'Flows',
+ sortable: true
+ }
+ ],
+ data: gridData,
+ delay: 0
+ });
+ return source;
+ }
+ },
+ body : {
+ dashlet : function(body, callback) {
+ var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
- var headers = ['Node', 'Flows'];
- var $thead = one.lib.dashlet.table.header(headers);
- $table.append($thead);
+ var headers = ['Node', 'Flows'];
+ var $thead = one.lib.dashlet.table.header(headers);
+ $table.append($thead);
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
- return $table;
- }
+ return $table;
}
+ }
}
/** FLOW DETAIL **/
one.f.detail = {
- id : {},
- registry : {},
- dashlet : function($dashlet, details) {
- var $h4 = one.lib.dashlet.header("Flow Details");
- $dashlet.append($h4);
-
- // details
- if (details == undefined) {
- var $none = $(document.createElement('div'));
- $none.addClass('none');
- var $p = $(document.createElement('p'));
- $p.text('Please select a flow');
- $p.addClass('text-center').addClass('text-info');
+ id : {},
+ registry : {},
+ dashlet : function($dashlet, details) {
+ var $h4 = one.lib.dashlet.header("Flow Details");
+ $dashlet.append($h4);
- $dashlet.append($none)
- .append($p);
- }
- },
- data : {
- dashlet : function(data) {
- var body = [];
- var tr = {};
- var entry = [];
-
- entry.push(data['name']);
- entry.push(data['node']);
- entry.push(data['flow']['priority']);
- entry.push(data['flow']['hardTimeout']);
- entry.push(data['flow']['idleTimeout']);
-
- tr.entry = entry;
- body.push(tr);
- return body;
- },
- description : function(data) {
- var body = [];
- var tr = {};
- var entry = [];
- entry.push(data['flow']['ingressPort']);
- entry.push(data['flow']['etherType']);
- entry.push(data['flow']['vlanId']);
- entry.push(data['flow']['vlanPriority']);
- entry.push(data['flow']['srcMac']);
- entry.push(data['flow']['dstMac']);
- entry.push(data['flow']['srcIp']);
- entry.push(data['flow']['dstIp']);
- entry.push(data['flow']['tosBits']);
- entry.push(data['flow']['srcPort']);
- entry.push(data['flow']['dstPort']);
- entry.push(data['flow']['protocol']);
- entry.push(data['flow']['cookie']);
-
- tr.entry = entry;
- body.push(tr);
- return body;
- },
- actions : function(data) {
- var body = [];
- var tr = {};
- var entry = [];
- var actions = '';
-
- $(data['flow']['actions']).each(function(index, value) {
- var locEqualTo = value.indexOf("=");
- if ( locEqualTo == -1 ) {
- actions += value + ', ';
- } else {
- var action = value.substr(0,locEqualTo);
- if( action == "OUTPUT") {
- var portIds = value.substr(locEqualTo+1).split(",");
- actions += action + "=";
- var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
- for(var i =0; i < portIds.length ; i++) {
- var portName = allPorts[portIds[i]];
- actions += portName + ", ";
- }
- } else {
- actions += value + ', ';
- }
- }
- });
- actions = actions.slice(0,-2);
- entry.push(actions);
+ // details
+ if (details == undefined) {
+ var $none = $(document.createElement('div'));
+ $none.addClass('none');
+ var $p = $(document.createElement('p'));
+ $p.text('Please select a flow');
+ $p.addClass('text-center').addClass('text-info');
- tr.entry = entry;
- body.push(tr);
- return body;
+ $dashlet.append($none)
+ .append($p);
+ }
+ },
+ data : {
+ dashlet : function(data) {
+ var body = [];
+ var tr = {};
+ var entry = [];
+
+ entry.push(data['name']);
+ entry.push(data['node']);
+ entry.push(data['flow']['priority']);
+ entry.push(data['flow']['hardTimeout']);
+ entry.push(data['flow']['idleTimeout']);
+
+ tr.entry = entry;
+ body.push(tr);
+ return body;
+ },
+ description : function(data) {
+ var body = [];
+ var tr = {};
+ var entry = [];
+ entry.push(data['flow']['ingressPort']);
+ entry.push(data['flow']['etherType']);
+ entry.push(data['flow']['vlanId']);
+ entry.push(data['flow']['vlanPriority']);
+ entry.push(data['flow']['srcMac']);
+ entry.push(data['flow']['dstMac']);
+ entry.push(data['flow']['srcIp']);
+ entry.push(data['flow']['dstIp']);
+ entry.push(data['flow']['tosBits']);
+ entry.push(data['flow']['srcPort']);
+ entry.push(data['flow']['dstPort']);
+ entry.push(data['flow']['protocol']);
+ entry.push(data['flow']['cookie']);
+
+ tr.entry = entry;
+ body.push(tr);
+ return body;
+ },
+ actions : function(data) {
+ var body = [];
+ var tr = {};
+ var entry = [];
+ var actions = '';
+
+ $(data['flow']['actions']).each(function(index, value) {
+ var locEqualTo = value.indexOf("=");
+ if ( locEqualTo == -1 ) {
+ actions += value + ', ';
+ } else {
+ var action = value.substr(0,locEqualTo);
+ if( action == "OUTPUT") {
+ var portIds = value.substr(locEqualTo+1).split(",");
+ actions += action + "=";
+ var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
+ for(var i =0; i < portIds.length ; i++) {
+ var portName = allPorts[portIds[i]];
+ actions += portName + ", ";
+ }
+ } else {
+ actions += value + ', ';
+ }
}
+ });
+ actions = actions.slice(0,-2);
+ entry.push(actions);
+
+ tr.entry = entry;
+ body.push(tr);
+ return body;
+ }
+ },
+ body : {
+ dashlet : function(body) {
+ // create table
+ var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
+ var $thead = one.lib.dashlet.table.header(header);
+ var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
+ $table.append($thead);
+
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
+
+ return $table;
},
- body : {
- dashlet : function(body) {
- // create table
- var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
- var $thead = one.lib.dashlet.table.header(header);
- var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
- $table.append($thead);
-
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
-
- return $table;
- },
- description : function(body) {
- var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
- var $thead = one.lib.dashlet.table.header(header);
- var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
- $table.append($thead);
+ description : function(body) {
+ var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
+ var $thead = one.lib.dashlet.table.header(header);
+ var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
+ $table.append($thead);
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
- return $table;
- },
- actions : function(body) {
- var header = ['Actions'];
- var $thead = one.lib.dashlet.table.header(header);
- var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
- $table.append($thead);
+ return $table;
+ },
+ actions : function(body) {
+ var header = ['Actions'];
+ var $thead = one.lib.dashlet.table.header(header);
+ var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
+ $table.append($thead);
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
- return $table;
- }
+ return $table;
}
+ }
}
/** FLOW ENTRIES **/
one.f.flows = {
- id : {
- dashlet : {
- add : "one_f_flows_id_dashlet_add",
- removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
- remove : "one_f_flows_id_dashlet_remove",
- toggle : "one_f_flows_id_dashlet_toggle",
- edit : "one_f_flows_id_dashlet_edit",
- datagrid : "one_f_flows_id_dashlet_datagrid",
- selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
- },
+ id : {
+ dashlet : {
+ add : "one_f_flows_id_dashlet_add",
+ removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
+ remove : "one_f_flows_id_dashlet_remove",
+ toggle : "one_f_flows_id_dashlet_toggle",
+ edit : "one_f_flows_id_dashlet_edit",
+ datagrid : "one_f_flows_id_dashlet_datagrid",
+ selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
+ },
+ modal : {
+ install : "one_f_flows_id_modal_install",
+ edit : "one_f_flows_id_modal_edit",
+ add : "one_f_flows_id_modal_add",
+ close : "one_f_flows_id_modal_close",
+ modal : "one_f_flows_id_modal_modal",
+ dialog : {
+ modal : "one_f_flows_id_modal_dialog_modal",
+ remove : "one_f_flows_id_modal_dialog_remove",
+ close : "one_f_flows_id_modal_dialog_close"
+ },
+ action : {
+ button : "one_f_flows_id_modal_action_button",
+ modal : "one_f_flows_id_modal_action_modal",
+ add : "one_f_flows_id_modal_action_add",
+ close : "one_f_flows_id_modal_action_close",
+ table : "one_f_flows_id_modal_action_table",
+ addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
+ setVlanId : "one_f_flows_id_modal_action_setVlanId",
+ setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
+ modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
+ modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
+ modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
+ modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
+ modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
+ modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
+ modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
+ enqueue : 'one-f-flows-modal-action-enqueue',
+ queue : 'one-f-flows-modal-action-queue',
+ setEthertype : 'one-f-flows-modal-action-setEthertype',
+ pushVlan : 'one-f-flows-modal-action-pushVlan',
+ setVlanCfi : 'one-f-flows-modal-action-setVlanCfi',
modal : {
- install : "one_f_flows_id_modal_install",
- edit : "one_f_flows_id_modal_edit",
- add : "one_f_flows_id_modal_add",
- close : "one_f_flows_id_modal_close",
- modal : "one_f_flows_id_modal_modal",
- dialog : {
- modal : "one_f_flows_id_modal_dialog_modal",
- remove : "one_f_flows_id_modal_dialog_remove",
- close : "one_f_flows_id_modal_dialog_close"
- },
- action : {
- button : "one_f_flows_id_modal_action_button",
- modal : "one_f_flows_id_modal_action_modal",
- add : "one_f_flows_id_modal_action_add",
- close : "one_f_flows_id_modal_action_close",
- table : "one_f_flows_id_modal_action_table",
- addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
- setVlanId : "one_f_flows_id_modal_action_setVlanId",
- setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
- modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
- modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
- modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
- modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
- modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
- modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
- modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
- modal : {
- modal : "one_f_flows_modal_action_modal_modal",
- remove : "one_f_flows_modal_action_modal_remove",
- cancel : "one_f_flows_modal_action_modal_cancel"
- }
- },
- form : {
- name : "one_f_flows_id_modal_form_name",
- nodes : "one_f_flows_id_modal_form_nodes",
- port : "one_f_flows_id_modal_form_port",
- priority : "one_f_flows_id_modal_form_priority",
- hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
- idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
- cookie : "one_f_flows_id_modal_form_cookie",
- etherType : "one_f_flows_id_modal_form_etherType",
- vlanId : "one_f_flows_id_modal_form_vlanId",
- vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
- srcMac : "one_f_flows_id_modal_form_srcMac",
- dstMac : "one_f_flows_id_modal_form_dstMac",
- srcIp : "one_f_flows_id_modal_form_srcIp",
- dstIp : "one_f_flows_id_modal_form_dstIp",
- tosBits : "one_f_flows_id_modal_form_tosBits",
- srcPort : "one_f_flows_id_modal_form_srcPort",
- dstPort : "one_f_flows_id_modal_form_dstPort",
- protocol : "one_f_flows_id_modal_form_protocol"
- }
+ modal : "one_f_flows_modal_action_modal_modal",
+ remove : "one_f_flows_modal_action_modal_remove",
+ cancel : "one_f_flows_modal_action_modal_cancel"
}
- },
- registry : {},
- dashlet : function($dashlet, callback) {
-
- // load body
- one.f.flows.ajax.dashlet(function(data) {
-
- var $h4 = one.lib.dashlet.header("Flow Entries");
-
- $dashlet.append($h4);
- if (one.f.flows.registry.privilege === 'WRITE') {
- var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
- var $button = one.lib.dashlet.button.button(button);
-
- $button.click(function() {
- var $modal = one.f.flows.modal.initialize();
- $modal.modal();
- });
- $dashlet.append($button);
- var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
- var $button = one.lib.dashlet.button.button(button);
-
- $button.click(function() {
- var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
- if (checkedCheckBoxes.size() === 0) {
- return false;
- }
-
- var requestData = [];
-
- checkedCheckBoxes.each(function(index, value) {
- var flowEntry = {};
- flowEntry['name'] = checkedCheckBoxes[index].name;
- flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
- requestData.push(flowEntry);
- });
- one.f.flows.modal.removeMultiple.dialog(requestData);
- });
- $dashlet.append($button);
+ },
+ form : {
+ name : "one_f_flows_id_modal_form_name",
+ nodes : "one_f_flows_id_modal_form_nodes",
+ port : "one_f_flows_id_modal_form_port",
+ priority : "one_f_flows_id_modal_form_priority",
+ hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
+ idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
+ cookie : "one_f_flows_id_modal_form_cookie",
+ etherType : "one_f_flows_id_modal_form_etherType",
+ vlanId : "one_f_flows_id_modal_form_vlanId",
+ vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
+ srcMac : "one_f_flows_id_modal_form_srcMac",
+ dstMac : "one_f_flows_id_modal_form_dstMac",
+ srcIp : "one_f_flows_id_modal_form_srcIp",
+ dstIp : "one_f_flows_id_modal_form_dstIp",
+ tosBits : "one_f_flows_id_modal_form_tosBits",
+ srcPort : "one_f_flows_id_modal_form_srcPort",
+ dstPort : "one_f_flows_id_modal_form_dstPort",
+ protocol : "one_f_flows_id_modal_form_protocol",
+ action : 'one-f-flows-id-modal-form-action'
+ }
+ }
+ },
+ registry : {},
+ dashlet : function($dashlet, callback) {
+ // load body
+ one.f.flows.ajax.dashlet(function(data) {
+ var $h4 = one.lib.dashlet.header("Flow Entries");
+ $dashlet.append($h4);
+ if (one.f.flows.registry.privilege === 'WRITE') {
+ var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
+ var $button = one.lib.dashlet.button.button(button);
+
+ $button.click(function() {
+ var $modal = one.f.flows.modal.initialize();
+ $modal.modal();
+ });
+ $dashlet.append($button);
+ var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
+ var $button = one.lib.dashlet.button.button(button);
+
+ $button.click(function() {
+ var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
+ if (checkedCheckBoxes.size() === 0) {
+ return false;
+ }
- }
+ var requestData = [];
- var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
- searchable: true,
- filterable: false,
- pagination: true,
- flexibleRowsPerPage: true
- }, "table-striped table-condensed");
- $dashlet.append($gridHTML);
- var dataSource = one.f.flows.data.flowsDataGrid(data);
- $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
- $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
- });
-
- $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
- $tr = $(tr);
- $span = $("td span", $tr);
- var flowstatus = $span.data("flowstatus");
- if($span.data("installinhw") != null) {
- var installInHw = $span.data("installinhw").toString();
- if(installInHw == "true" && flowstatus == "Success") {
- $tr.addClass("success");
- } else {
- $tr.addClass("warning");
- }
- }
- // attach mouseover to show pointer cursor
- $tr.mouseover(function() {
- $(this).css("cursor", "pointer");
- });
- // attach click event
- $tr.click(function() {
- var $td = $($(this).find("td")[1]);
- var id = $td.text();
- var node = $td.find("span").data("nodeid");
- one.f.flows.detail(id, node);
- });
- $(".flowEntry").click(function(e){
- if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
- .prop("checked",
- true);
- } else {
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
- .prop("checked",
- false);
- }
- e.stopPropagation();
- });
- });
- });
-
- // details callback
- if(callback != undefined) callback();
+ checkedCheckBoxes.each(function(index, value) {
+ var flowEntry = {};
+ flowEntry['name'] = checkedCheckBoxes[index].name;
+ flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
+ requestData.push(flowEntry);
+ });
+ one.f.flows.modal.removeMultiple.dialog(requestData);
});
- },
- detail : function(id, node) {
- // clear flow details
- var $detailDashlet = one.main.dashlet.right.bottom;
- $detailDashlet.empty();
- var $h4 = one.lib.dashlet.header("Flow Overview");
- $detailDashlet.append($h4);
-
- // details
- var flows = one.f.flows.registry.flows;
- one.f.flows.registry['selectedId'] = id;
- one.f.flows.registry['selectedNode'] = node;
- var flow;
- $(flows).each(function(index, value) {
- if (value.name == id && value.nodeId == node) {
- flow = value;
- }
+ $dashlet.append($button);
+
+ }
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.flows.data.flowsDataGrid(data);
+ $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
+ $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
});
- if (one.f.flows.registry.privilege === 'WRITE') {
- // remove button
- var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
- var $button = one.lib.dashlet.button.button(button);
- $button.click(function() {
- var $modal = one.f.flows.modal.dialog.initialize(id, node);
- $modal.modal();
- });
- // edit button
- var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
- var $editButton = one.lib.dashlet.button.button(editButton);
- $editButton.click(function() {
- var install = flow['flow']['installInHw'];
- var $modal = one.f.flows.modal.initialize(true,install);
- $modal.modal().on('shown',function(){
- var $port = $('#'+one.f.flows.id.modal.form.port);
- $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
- });
- });
- // toggle button
- var toggle;
- if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
- toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
- } else {
- toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
- }
- var $toggle = one.lib.dashlet.button.button(toggle);
- $toggle.click(function() {
- one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
- if(data == "Success") {
- one.main.dashlet.right.bottom.empty();
- one.f.detail.dashlet(one.main.dashlet.right.bottom);
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top, function() {
- // checks are backwards due to stale registry
- if(flow['flow']['installInHw'] == 'true') {
- one.lib.alert('Uninstalled Flow');
- } else {
- one.lib.alert('Installed Flow');
- }
- one.f.flows.detail(id, node)
- });
- } else {
- one.lib.alert('Cannot toggle flow: '+data);
- }
- });
- });
- $detailDashlet.append($button).append($editButton).append($toggle);
- }
- // append details
- var body = one.f.detail.data.dashlet(flow);
- var $body = one.f.detail.body.dashlet(body);
- $detailDashlet.append($body);
- var body = one.f.detail.data.description(flow);
- var $body = one.f.detail.body.description(body);
- $detailDashlet.append($body);
- var body = one.f.detail.data.actions(flow);
- var $body = one.f.detail.body.actions(body);
- $detailDashlet.append($body);
- },
- modal : {
- dialog : {
- initialize : function(id, node) {
- var h3 = "Remove Flow";
- var $p = one.f.flows.modal.dialog.body(id);
- var footer = one.f.flows.modal.dialog.footer();
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
- $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
- $modal.modal('hide');
- });
- $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
- one.f.flows.modal.ajax.removeflow(id, node, function(data) {
- if (data == "Success") {
- $modal.modal('hide');
- one.main.dashlet.right.bottom.empty();
- one.f.detail.dashlet(one.main.dashlet.right.bottom);
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- one.lib.alert('Flow removed');
- } else {
- one.lib.alert('Cannot remove flow: '+data);
- }
- });
- });
- return $modal;
- },
- footer : function() {
- var footer = [];
-
- var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
- var $removeButton = one.lib.dashlet.button.button(removeButton);
- footer.push($removeButton);
-
- var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
-
- return footer;
- },
- body : function(id) {
- var $p = $(document.createElement('p'));
- $p.append('Remove flow '+id+'?');
- return $p;
+ $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
+ $tr = $(tr);
+ $span = $("td span", $tr);
+ var flowstatus = $span.data("flowstatus");
+ if($span.data("installinhw") != null) {
+ var installInHw = $span.data("installinhw").toString();
+ if(installInHw == "true" && flowstatus == "Success") {
+ $tr.addClass("success");
+ } else {
+ $tr.addClass("warning");
}
- },
- initialize : function(edit,install) {
- var h3;
- if(edit) {
- h3 = "Edit Flow Entry";
- var footer = one.f.flows.modal.footerEdit();
-
+ }
+ // attach mouseover to show pointer cursor
+ $tr.mouseover(function() {
+ $(this).css("cursor", "pointer");
+ });
+ // attach click event
+ $tr.click(function() {
+ var $td = $($(this).find("td")[1]);
+ var id = $td.text();
+ var node = $td.find("span").data("nodeid");
+ one.f.flows.detail(id, node);
+ });
+ $(".flowEntry").click(function(e){
+ if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+ .prop("checked",
+ true);
} else {
- h3 = "Add Flow Entry";
- var footer = one.f.flows.modal.footer();
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+ .prop("checked",
+ false);
}
-
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
-
- // bind close button
- $('#'+one.f.flows.id.modal.close, $modal).click(function() {
- $modal.modal('hide');
+ e.stopPropagation();
+ });
+ });
+ });
+
+ // details callback
+ if(callback != undefined) callback();
+ });
+ },
+ detail : function(id, node) {
+ // clear flow details
+ var $detailDashlet = one.main.dashlet.right.bottom;
+ $detailDashlet.empty();
+ var $h4 = one.lib.dashlet.header("Flow Overview");
+ $detailDashlet.append($h4);
+
+ // details
+ var flows = one.f.flows.registry.flows;
+ one.f.flows.registry['selectedId'] = id;
+ one.f.flows.registry['selectedNode'] = node;
+ var flow;
+ $(flows).each(function(index, value) {
+ if (value.name == id && value.nodeId == node) {
+ flow = value;
+ }
+ });
+ if (one.f.flows.registry.privilege === 'WRITE') {
+ // remove button
+ var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
+ var $button = one.lib.dashlet.button.button(button);
+ $button.click(function() {
+ var $modal = one.f.flows.modal.dialog.initialize(id, node);
+ $modal.modal();
+ });
+ // edit button
+ var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
+ var $editButton = one.lib.dashlet.button.button(editButton);
+ $editButton.click(function() {
+ var install = flow['flow']['installInHw'];
+ var $modal = one.f.flows.modal.initialize(true,install);
+ $modal.modal().on('shown',function(){
+ var $port = $('#'+one.f.flows.id.modal.form.port);
+ $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
+ });
+ });
+ // toggle button
+ var toggle;
+ if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
+ toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
+ } else {
+ toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
+ }
+ var $toggle = one.lib.dashlet.button.button(toggle);
+ $toggle.click(function() {
+ one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
+ if(data == "Success") {
+ one.main.dashlet.right.bottom.empty();
+ one.f.detail.dashlet(one.main.dashlet.right.bottom);
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top, function() {
+ // checks are backwards due to stale registry
+ if(flow['flow']['installInHw'] == 'true') {
+ one.lib.alert('Uninstalled Flow');
+ } else {
+ one.lib.alert('Installed Flow');
+ }
+ one.f.flows.detail(id, node)
});
+ } else {
+ one.lib.alert('Cannot toggle flow: '+data);
+ }
+ });
+ });
- if (edit) {
- // bind edit flow button
- $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
- one.f.flows.modal.save($modal, install, true);
- });
+ $detailDashlet.append($button).append($editButton).append($toggle);
+ }
+ // append details
+ var body = one.f.detail.data.dashlet(flow);
+ var $body = one.f.detail.body.dashlet(body);
+ $detailDashlet.append($body);
+ var body = one.f.detail.data.description(flow);
+ var $body = one.f.detail.body.description(body);
+ $detailDashlet.append($body);
+ var body = one.f.detail.data.actions(flow);
+ var $body = one.f.detail.body.actions(body);
+ $detailDashlet.append($body);
+ },
+ modal : {
+ dialog : {
+ initialize : function(id, node) {
+ var h3 = "Remove Flow";
+ var $p = one.f.flows.modal.dialog.body(id);
+ var footer = one.f.flows.modal.dialog.footer();
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
+ $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+ $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
+ one.f.flows.modal.ajax.removeflow(id, node, function(data) {
+ if (data == "Success") {
+ $modal.modal('hide');
+ one.main.dashlet.right.bottom.empty();
+ one.f.detail.dashlet(one.main.dashlet.right.bottom);
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ one.lib.alert('Flow removed');
} else {
- // bind add flow button
- $('#'+one.f.flows.id.modal.add, $modal).click(function() {
- one.f.flows.modal.save($modal, 'false');
- });
-
- // bind install flow button
- $('#'+one.f.flows.id.modal.install, $modal).click(function() {
- one.f.flows.modal.save($modal, 'true');
- });
+ one.lib.alert('Cannot remove flow: '+data);
}
+ });
+ });
+ return $modal;
+ },
+ footer : function() {
+ var footer = [];
+
+ var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
+ var $removeButton = one.lib.dashlet.button.button(removeButton);
+ footer.push($removeButton);
+
+ var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
+
+ return footer;
+ },
+ body : function(id) {
+ var $p = $(document.createElement('p'));
+ $p.append('Remove flow '+id+'?');
+ return $p;
+ }
+ },
+ initialize : function(edit,install) {
+ var h3;
+ if(edit) {
+ h3 = "Edit Flow Entry";
+ var footer = one.f.flows.modal.footerEdit();
+
+ } else {
+ h3 = "Add Flow Entry";
+ var footer = one.f.flows.modal.footer();
+ }
+
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
+
+ // bind close button
+ $('#'+one.f.flows.id.modal.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+
+ if (edit) {
+ // bind edit flow button
+ $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
+ one.f.flows.modal.save($modal, install, true);
+ });
+ } else {
+ // bind add flow button
+ $('#'+one.f.flows.id.modal.add, $modal).click(function() {
+ one.f.flows.modal.save($modal, 'false');
+ });
+ // bind install flow button
+ $('#'+one.f.flows.id.modal.install, $modal).click(function() {
+ one.f.flows.modal.save($modal, 'true');
+ });
+ }
- var nodes = one.f.flows.registry.nodes;
- var nodeports = one.f.flows.registry.nodeports;
- var $body = one.f.flows.modal.body(nodes, nodeports, edit);
- one.lib.modal.inject.body($modal, $body,edit);
-
- return $modal;
- },
- save : function($modal, install, edit) {
- var result = {};
-
- result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
- result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
- result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
- result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
- result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
- result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
- result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
- result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
- result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
- result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
- result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
- result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
- result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
- result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
- result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
- result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
- result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
- result['installInHw'] = install;
-
- var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
-
- $.each(result, function(key, value) {
- if (value == "") delete result[key];
- });
+ var nodes = one.f.flows.registry.nodes;
+ var nodeports = one.f.flows.registry.nodeports;
+ var $body = one.f.flows.modal.body(nodes, nodeports, edit);
+ one.lib.modal.inject.body($modal, $body,edit);
- var action = [];
- var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
- $($table.find('tbody').find('tr')).each(function(index, value) {
- if (!$(this).find('td').hasClass('empty')) {
- action.push($(value).data('action'));
- }
+ return $modal;
+ },
+ save : function($modal, install, edit) {
+ var result = {};
+
+ result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
+ result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
+ result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
+ result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
+ result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
+ result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
+ result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
+ result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
+ result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
+ result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
+ result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
+ result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
+ result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
+ result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
+ result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
+ result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
+ result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
+ result['installInHw'] = install;
+
+ var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
+
+ $.each(result, function(key, value) {
+ if (value == "") delete result[key];
+ });
+
+ var action = [];
+ var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
+ $($table.find('tbody').find('tr')).each(function(index, value) {
+ if (!$(this).find('td').hasClass('empty')) {
+ action.push($(value).data('action'));
+ }
+ });
+ result['actions'] = action;
+
+ // frontend validation
+ if (result['name'] == undefined) {
+ alert('Need flow name');
+ return;
+ }
+ if (nodeId == '') {
+ alert('Select node');
+ return;
+ }
+ if (action.length == 0) {
+ alert('Please specify an action');
+ return;
+ }
+
+ // package for ajax call
+ var resource = {};
+ resource['body'] = JSON.stringify(result);
+ if(edit){
+ resource['action'] = 'edit';
+ } else {
+ resource['action'] = 'add';
+ }
+
+ resource['nodeId'] = nodeId;
+
+ if (edit) {
+ one.f.flows.modal.ajax.saveflow(resource, function(data) {
+ if (data == "Success") {
+ $modal.modal('hide').on('hidden', function () {
+ one.f.flows.detail(result['name'], nodeId);
});
- result['actions'] = action;
-
- // frontend validation
- if (result['name'] == undefined) {
- alert('Need flow name');
- return;
- }
- if (nodeId == '') {
- alert('Select node');
- return;
- }
- if (action.length == 0) {
- alert('Please specify an action');
- return;
- }
-
- // package for ajax call
- var resource = {};
- resource['body'] = JSON.stringify(result);
- if(edit){
- resource['action'] = 'edit';
+ one.lib.alert('Flow Entry edited');
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ } else {
+ alert('Could not edit flow: '+data);
+ }
+ });
+ } else {
+ one.f.flows.modal.ajax.saveflow(resource, function(data) {
+ if (data == "Success") {
+ $modal.modal('hide');
+ one.lib.alert('Flow Entry added');
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ } else {
+ alert('Could not add flow: '+data);
+ }
+ });
+ }
+ },
+ ajax : {
+ nodes : function(successCallback) {
+ $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
+ var nodes = one.f.flows.modal.data.nodes(data);
+ var nodeports = data;
+ one.f.flows.registry['nodes'] = nodes;
+ one.f.flows.registry['nodeports'] = nodeports;
+
+ successCallback(nodes, nodeports);
+ });
+ },
+ saveflow : function(resource, callback) {
+ $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
+ callback(data);
+ });
+ },
+ removeflow : function(id, node, callback) {
+ resource = {};
+ resource['action'] = 'remove';
+ $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+ callback(data);
+ });
+ },
+ toggleflow : function(id, node, callback) {
+ resource = {};
+ resource['action'] = 'toggle';
+ $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+ callback(data);
+ });
+ }
+ },
+ data : {
+ nodes : function(data) {
+ result = {};
+ $.each(data, function(key, value) {
+ result[key] = value['name'];
+ });
+ return result;
+ }
+ },
+ body : function(nodes, nodeports, edit) {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ var existingFlow;
+ // flow description
+ var $legend = one.lib.form.legend("");
+ $legend.css('visibility', 'hidden');
+ $fieldset.append($legend);
+ // name
+ var $label = one.lib.form.label("Name");
+ var $input = one.lib.form.input("Flow Name");
+ $input.attr('id', one.f.flows.id.modal.form.name);
+ if(edit) {
+ $input.attr('disabled', 'disabled');
+ var flows = one.f.flows.registry.flows;
+ $(flows).each(function(index, value) {
+ if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
+ existingFlow = value.flow;
+ }
+ });
+ $input.val(existingFlow.name);
+ }
+
+ $fieldset.append($label).append($input);
+ // node
+ var $label = one.lib.form.label("Node");
+ var $select = one.lib.form.select.create(nodes);
+ one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
+ $select.val($select.find("option:first").val());
+ $select.attr('id', one.f.flows.id.modal.form.nodes);
+ if(edit) {
+ $select.attr('disabled', 'disabled');
+ $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
+ }
+
+ // bind onchange
+ $select.change(function() {
+ // retrieve port value
+ var node = $(this).find('option:selected').attr('value');
+ var $ports = $('#'+one.f.flows.id.modal.form.port);
+ if (node == '') {
+ one.lib.form.select.inject($ports, {});
+ return;
+ }
+ one.f.flows.registry['currentNode'] = node;
+ var ports = nodeports[node]['ports'];
+ one.lib.form.select.inject($ports, ports);
+ one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
+ $ports.val($ports.find("option:first").val());
+ if(edit) {
+ $ports.val( existingFlow.ingressPort );
+ }
+ $.getJSON(one.f.address.root+'/valid-flows/'+node, function(response) {
+ var $select = $('#'+one.f.flows.id.modal.form.action, $fieldset);
+ one.lib.form.select.inject($select, response);
+ one.lib.form.select.prepend($select, {'' : 'Please Select an Action'});
+ // when selecting an action
+ $select.change(function() {
+ var action = $(this).find('option:selected');
+ one.f.flows.modal.action.parse(action.attr('value'));
+ $select[0].selectedIndex = 0;
+ });
+ });
+ });
+
+ $fieldset.append($label).append($select);
+ // input port
+ var $label = one.lib.form.label("Input Port");
+ var $select = one.lib.form.select.create();
+
+ $select.attr('id', one.f.flows.id.modal.form.port);
+ $fieldset.append($label).append($select);
+ // priority
+ var $label = one.lib.form.label("Priority");
+ var $input = one.lib.form.input("Priority");
+ $input.attr('id', one.f.flows.id.modal.form.priority);
+ $input.val('500');
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.priority);
+ }
+ // hardTimeout
+ var $label = one.lib.form.label("Hard Timeout");
+ var $input = one.lib.form.input("Hard Timeout");
+ $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
+ if(edit) {
+ $input.val(existingFlow.hardTimeout);
+ }
+ $fieldset.append($label).append($input);
+
+ // idleTimeout
+ var $label = one.lib.form.label("Idle Timeout");
+ var $input = one.lib.form.input("Idle Timeout");
+ $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.idleTimeout);
+ }
+ // cookie
+ var $label = one.lib.form.label("Cookie");
+ var $input = one.lib.form.input("Cookie");
+ $input.attr('id', one.f.flows.id.modal.form.cookie);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.cookie);
+ }
+
+ // layer 2
+ var $legend = one.lib.form.legend("Layer 2");
+ $fieldset.append($legend);
+ // etherType
+ var $label = one.lib.form.label("Ethernet Type");
+ var $input = one.lib.form.input("Ethernet Type");
+ $input.attr('id', one.f.flows.id.modal.form.etherType);
+ $input.val('0x800');
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.etherType);
+ }
+ // vlanId
+ var $label = one.lib.form.label("VLAN Identification Number");
+ var $input = one.lib.form.input("VLAN Identification Number");
+ $input.attr('id', one.f.flows.id.modal.form.vlanId);
+ var $help = one.lib.form.help("Range: 0 - 4095");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.vlanId);
+ }
+
+ // vlanPriority
+ var $label = one.lib.form.label("VLAN Priority");
+ var $input = one.lib.form.input("VLAN Priority");
+ $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
+ var $help = one.lib.form.help("Range: 0 - 7");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.vlanPriority);
+ }
+
+ // srcMac
+ var $label = one.lib.form.label("Source MAC Address");
+ var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
+ $input.attr('id', one.f.flows.id.modal.form.srcMac);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.srcMac);
+ }
+ // dstMac
+ var $label = one.lib.form.label("Destination MAC Address");
+ var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
+ $input.attr('id', one.f.flows.id.modal.form.dstMac);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.dstMac);
+ }
+ // layer 3
+ var $legend = one.lib.form.legend("Layer 3");
+ $fieldset.append($legend);
+
+ // srcIp
+ var $label = one.lib.form.label("Source IP Address");
+ var $input = one.lib.form.input("192.168.3.128");
+ $input.attr('id', one.f.flows.id.modal.form.srcIp);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.srcIp);
+ }
+ // dstIp
+ var $label = one.lib.form.label("Destination IP Address");
+ var $input = one.lib.form.input("2001:2334::0/32");
+ $input.attr('id', one.f.flows.id.modal.form.dstIp);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.dstIp);
+ }
+ // tosBits
+ var $label = one.lib.form.label("ToS Bits");
+ var $input = one.lib.form.input("ToS Bits");
+ $input.attr('id', one.f.flows.id.modal.form.tosBits);
+ var $help = one.lib.form.help("Range: 0 - 63");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.tosBits);
+ }
+
+ // layer 4
+ var $legend = one.lib.form.legend("Layer 4");
+ $fieldset.append($legend);
+ // srcPort
+ var $label = one.lib.form.label("Source Port");
+ var $input = one.lib.form.input("Source Port");
+ $input.attr('id', one.f.flows.id.modal.form.srcPort);
+ var $help = one.lib.form.help("Range: 0 - 65535");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.srcPort);
+ }
+ // dstPort
+ var $label = one.lib.form.label("Destination Port");
+ var $input = one.lib.form.input("Destination Port");
+ $input.attr('id', one.f.flows.id.modal.form.dstPort);
+ var $help = one.lib.form.help("Range: 0 - 65535");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.dstPort);
+ }
+ // protocol
+ var $label = one.lib.form.label("Protocol");
+ var $input = one.lib.form.input("Protocol");
+ $input.attr('id', one.f.flows.id.modal.form.protocol);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.protocol);
+ }
+ // actions
+ var $legend = one.lib.form.label("Actions");
+ $fieldset.append($legend);
+ // actions table
+ var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
+ var $table = one.lib.dashlet.table.table(tableAttributes);
+ $table.attr('id', one.f.flows.id.modal.action.table);
+ var tableHeaders = ["Action", "Data"];
+ var $thead = one.lib.dashlet.table.header(tableHeaders);
+ var $tbody = one.lib.dashlet.table.body("", tableHeaders);
+ $table.append($thead).append($tbody);
+ // actions
+ var actions = {
+ "" : "Please Select an Action",
+ "DROP" : "Drop",
+ "LOOPBACK" : "Loopback",
+ "FLOOD" : "Flood",
+ "FLOOD_ALL" : "Flood All",
+ "CONTROLLER" : "Controller",
+ "SW_PATH" : "Software Path",
+ "HW_PATH" : "Hardware Path",
+ "OUTPUT" : "Add Output Ports",
+ "ENQUEUE" : "Enqueue",
+ "SET_VLAN_ID" : "Set VLAN ID",
+ "SET_VLAN_PCP" : "Set VLAN Priority",
+ "SET_VLAN_CFI" : "Set VLAN CFI",
+ "POP_VLAN" : "Strip VLAN Header",
+ "PUSH_VLAN" : "Push VLAN",
+ "SET_DL_SRC" : "Modify Datalayer Source Address",
+ "SET_DL_DST" : "Modify Datalayer Destination Address",
+ "SET_DL_TYPE" : "Set Ethertype",
+ "SET_NW_SRC" : "Modify Network Source Address",
+ "SET_NW_DST" :"Modify Network Destination Address",
+ "SET_NW_TOS" : "Modify ToS Bits",
+ "SET_TP_SRC" : "Modify Transport Source Port",
+ "SET_TP_DST" : "Modify Transport Destination Port"
+ };
+ var $select = one.lib.form.select.create(actions);
+ $select.attr('id', one.f.flows.id.modal.form.action);
+ // when selecting an action
+ $select.change(function() {
+ var action = $(this).find('option:selected');
+ one.f.flows.modal.action.parse(action.attr('value'));
+ $select[0].selectedIndex = 0;
+ });
+
+ if(edit) {
+ $(existingFlow.actions).each(function(index, value){
+ setTimeout(function(){
+ var locEqualTo = value.indexOf("=");
+ if ( locEqualTo == -1 ) {
+ one.f.flows.modal.action.add.add(actions[value], value);
} else {
- resource['action'] = 'add';
+ var action = value.substr(0,locEqualTo);
+ if( action == "OUTPUT") {
+ var portIds = value.substr(locEqualTo+1).split(",");
+ var ports = [];
+ var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+ for(var i =0; i < portIds.length ; i++) {
+ var portName = allPorts[portIds[i]];
+ ports.push(portName);
+ }
+ one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
+ } else {
+ var val = value.substr(locEqualTo+1);
+ one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
+ }
}
+ }, 1000)
+ });
+ }
+ $fieldset.append($select).append($table);
- resource['nodeId'] = nodeId;
-
- if (edit) {
- one.f.flows.modal.ajax.saveflow(resource, function(data) {
- if (data == "Success") {
- $modal.modal('hide').on('hidden', function () {
- one.f.flows.detail(result['name'], nodeId);
- });
- one.lib.alert('Flow Entry edited');
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- } else {
- alert('Could not edit flow: '+data);
- }
- });
- } else {
- one.f.flows.modal.ajax.saveflow(resource, function(data) {
- if (data == "Success") {
- $modal.modal('hide');
- one.lib.alert('Flow Entry added');
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- } else {
- alert('Could not add flow: '+data);
- }
- });
- }
+ // return
+ $form.append($fieldset);
+ return $form;
+ },
+ action : {
+ parse : function(option) {
+ switch (option) {
+ case "OUTPUT" :
+ var h3 = "Add Output Port";
+ var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
+ $modal.modal();
+ break;
+ case "SET_VLAN_ID" :
+ var h3 = "Set VLAN ID";
+ var placeholder = "VLAN Identification Number";
+ var id = one.f.flows.id.modal.action.setVlanId;
+ var help = "Range: 0 - 4095";
+ var action = 'SET_VLAN_ID';
+ var name = "VLAN ID";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_VLAN_PCP" :
+ var h3 = "Set VLAN Priority";
+ var placeholder = "VLAN Priority";
+ var id = one.f.flows.id.modal.action.setVlanPriority;
+ var help = "Range: 0 - 7";
+ var action = 'SET_VLAN_PCP';
+ var name = "VLAN Priority";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_VLAN_CFI" :
+ var h3 = "Set VLAN CFI";
+ var placeholder = "VLAN CFI";
+ var id = one.f.flows.id.modal.action.setVlanCfi;
+ var help = "Range: 0 - 1";
+ var action = 'SET_VLAN_CFI';
+ var name = "VLAN CFI";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "POP_VLAN" :
+ var name = "Strip VLAN Header";
+ var action = 'POP_VLAN';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "PUSH_VLAN" :
+ var h3 = "Push VLAN";
+ var placeholder = "VLAN";
+ var id = one.f.flows.id.modal.action.pushVlan;
+ var help = "Range: 0 - 4095";
+ var action = 'PUSH_VLAN';
+ var name = "VLAN";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_DL_SRC" :
+ var h3 = "Set Source MAC Address";
+ var placeholder = "Source MAC Address";
+ var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
+ var help = "Example: 00:11:22:aa:bb:cc";
+ var action = 'SET_DL_SRC';
+ var name = "Source MAC";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_DL_DST" :
+ var h3 = "Set Destination MAC Address";
+ var placeholder = "Destination MAC Address";
+ var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
+ var help = "Example: 00:11:22:aa:bb:cc";
+ var action = 'SET_DL_DST';
+ var name = "Destination MAC";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_DL_TYPE" :
+ var h3 = "Set Ethertype";
+ var placeholder = "Ethertype";
+ var id = one.f.flows.id.modal.action.setEthertype;
+ var help = "Range: 0 - 65535";
+ var action = 'SET_DL_TYPE';
+ var name = "Ethertype";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_NW_SRC" :
+ var h3 = "Set IP Source Address";
+ var placeholder = "Source IP Address";
+ var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
+ var help = "Example: 127.0.0.1";
+ var action = 'SET_NW_SRC';
+ var name = "Source IP";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_NW_DST" :
+ var h3 = "Set IP Destination Address";
+ var placeholder = "Destination IP Address";
+ var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
+ var help = "Example: 127.0.0.1";
+ var action = 'SET_NW_DST';
+ var name = "Destination IP";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_NW_TOS" :
+ var h3 = "Set IPv4 ToS";
+ var placeholder = "IPv4 ToS";
+ var id = one.f.flows.id.modal.action.modifyTosBits;
+ var help = "Range: 0 - 63";
+ var action = 'SET_NW_TOS';
+ var name = "ToS Bits";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_TP_SRC" :
+ var h3 = "Set Transport Source Port";
+ var placeholder = "Transport Source Port";
+ var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
+ var help = "Range: 1 - 65535";
+ var action = 'SET_TP_SRC';
+ var name = "Source Port";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_TP_DST" :
+ var h3 = "Set Transport Destination Port";
+ var placeholder = "Transport Destination Port";
+ var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
+ var help = "Range: 1 - 65535";
+ var action = 'SET_TP_DST';
+ var name = "Destination Port";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "ENQUEUE" :
+ var h3 = "Enqueue";
+ var placeholder = "Enqueue";
+ var id = one.f.flows.id.modal.action.enqueue;
+ var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addEnqueue, one.f.flows.modal.action.add.addEnqueue);
+ $modal.modal();
+ break;
+ case "DROP" :
+ var name = "Drop";
+ var action = 'DROP';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "LOOPBACK" :
+ var name = "Loopback";
+ var action = 'LOOPBACK';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "FLOOD" :
+ var name = "Flood";
+ var action = 'FLOOD';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "FLOOD_ALL" :
+ var name = "Flood All";
+ var action = 'FLOOD_ALL';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "SW_PATH" :
+ var name = "Software Path";
+ var action = 'SW_PATH';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "HW_PATH" :
+ var name = "Hardware Path";
+ var action = 'HW_PATH';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "CONTROLLER" :
+ var name = "Controller";
+ var action = 'CONTROLLER';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ }
+ },
+ initialize : function(h3, bodyCallback, addCallback) {
+ var footer = one.f.flows.modal.action.footer();
+ var $body = bodyCallback();
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
+ // bind close button
+ $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+ // bind add flow button
+ $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
+ addCallback($modal);
+ });
+ return $modal;
+ },
+ add : {
+ addOutputPorts : function($modal) {
+ var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+ var ports = '';
+ var pid = '';
+ $options.each(function(index, value) {
+ ports = ports+$(value).text()+", ";
+ pid = pid+$(value).attr('value')+",";
+ });
+ ports = ports.slice(0,-2);
+ pid = pid.slice(0,-1);
+ one.f.flows.modal.action.add.addPortsToTable(ports, pid);
+ $modal.modal('hide');
},
- ajax : {
- nodes : function(successCallback) {
- $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
- var nodes = one.f.flows.modal.data.nodes(data);
- var nodeports = data;
- one.f.flows.registry['nodes'] = nodes;
- one.f.flows.registry['nodeports'] = nodeports;
-
- successCallback(nodes, nodeports);
- });
- },
- saveflow : function(resource, callback) {
- $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
- callback(data);
- });
- },
- removeflow : function(id, node, callback) {
- resource = {};
- resource['action'] = 'remove';
- $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
- callback(data);
- });
- },
- toggleflow : function(id, node, callback) {
- resource = {};
- resource['action'] = 'toggle';
- $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
- callback(data);
- });
- }
+ addEnqueue : function($modal) {
+ var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+ var ports = '';
+ var pid = '';
+ $options.each(function(index, value) {
+ ports = ports+$(value).text()+", ";
+ pid = pid+$(value).attr('value')+",";
+ });
+ var $input = $('#'+one.f.flows.id.modal.action.queue);
+ var queue = $input.val();
+ ports = ports.slice(0,-2);
+ pid = pid.slice(0,-1);
+ one.f.flows.modal.action.add.addEnqueueToTable(ports, pid, queue);
+ $modal.modal('hide');
},
- data : {
- nodes : function(data) {
- result = {};
- $.each(data, function(key, value) {
- result[key] = value['name'];
- });
- return result;
- }
+ addEnqueueToTable : function(ports, pid, queue) {
+ if (queue !== '' && queue >= 0) {
+ ports += ':'+queue;
+ }
+ var $tr = one.f.flows.modal.action.table.add("Enqueue", ports);
+ $tr.attr('id', 'ENQUEUE');
+ if (queue !== '' && queue >= 0) {
+ $tr.data('action', 'ENQUEUE='+pid+':'+queue);
+ } else {
+ $tr.data('action', 'ENQUEUE='+pid+':0'); // default queue to 0
+ }
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
},
- body : function(nodes, nodeports, edit) {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- var existingFlow;
- // flow description
- var $legend = one.lib.form.legend("");
- $legend.css('visibility', 'hidden');
- $fieldset.append($legend);
- // name
- var $label = one.lib.form.label("Name");
- var $input = one.lib.form.input("Flow Name");
- $input.attr('id', one.f.flows.id.modal.form.name);
- if(edit) {
- $input.attr('disabled', 'disabled');
- var flows = one.f.flows.registry.flows;
- $(flows).each(function(index, value) {
- if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
- existingFlow = value.flow;
- }
- });
- $input.val(existingFlow.name);
- }
-
- $fieldset.append($label).append($input);
- // node
- var $label = one.lib.form.label("Node");
- var $select = one.lib.form.select.create(nodes);
- one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
- $select.val($select.find("option:first").val());
- $select.attr('id', one.f.flows.id.modal.form.nodes);
- if(edit) {
- $select.attr('disabled', 'disabled');
- $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
- }
+ addPortsToTable : function(ports, pid){
+ var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
+ $tr.attr('id', 'OUTPUT');
+ $tr.data('action', 'OUTPUT='+pid);
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
+ },
+ add : function(name, action) {
+ var $tr = one.f.flows.modal.action.table.add(name);
+ $tr.attr('id', action);
+ $tr.data('action', action);
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
+ },
+ set : function(name, id, action, $modal) {
+ var $input = $('#'+id);
+ var value = $input.val();
+ one.f.flows.modal.action.add.addDataToTable(name,value,action)
+ $modal.modal('hide');
+ },
+ addDataToTable : function(name,value,action) {
+ var $tr = one.f.flows.modal.action.table.add(name, value);
+ $tr.attr('id', action);
+ $tr.data('action', action+'='+value);
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
+ },
+ remove : function(that) {
+ $(that).remove();
+ var $table = $('#'+one.f.flows.id.modal.action.table);
+ if ($table.find('tbody').find('tr').size() == 0) {
+ var $tr = $(document.createElement('tr'));
+ var $td = $(document.createElement('td'));
+ $td.attr('colspan', '3');
+ $tr.addClass('empty');
+ $td.text('No data available');
+ $tr.append($td);
+ $table.find('tbody').append($tr);
+ }
+ },
+ modal : {
+ initialize : function(that) {
+ var h3 = "Remove Action";
+ var footer = one.f.flows.modal.action.add.modal.footer();
+ var $body = one.f.flows.modal.action.add.modal.body();
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
+
+ // bind cancel button
+ $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
+ $modal.modal('hide');
+ });
- // bind onchange
- $select.change(function() {
- // retrieve port value
- var node = $(this).find('option:selected').attr('value');
- var $ports = $('#'+one.f.flows.id.modal.form.port);
- if (node == '') {
- one.lib.form.select.inject($ports, {});
- return;
- }
- one.f.flows.registry['currentNode'] = node;
- var ports = nodeports[node]['ports'];
- one.lib.form.select.inject($ports, ports);
- one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
- $ports.val($ports.find("option:first").val());
- if(edit) {
- $ports.val( existingFlow.ingressPort );
- }
+ // bind remove button
+ $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
+ one.f.flows.modal.action.add.remove(that);
+ $modal.modal('hide');
});
- $fieldset.append($label).append($select);
- // input port
- var $label = one.lib.form.label("Input Port");
- var $select = one.lib.form.select.create();
-
- $select.attr('id', one.f.flows.id.modal.form.port);
- $fieldset.append($label).append($select);
- // priority
- var $label = one.lib.form.label("Priority");
- var $input = one.lib.form.input("Priority");
- $input.attr('id', one.f.flows.id.modal.form.priority);
- $input.val('500');
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.priority);
- }
- // hardTimeout
- var $label = one.lib.form.label("Hard Timeout");
- var $input = one.lib.form.input("Hard Timeout");
- $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
- if(edit) {
- $input.val(existingFlow.hardTimeout);
- }
- $fieldset.append($label).append($input);
-
- // idleTimeout
- var $label = one.lib.form.label("Idle Timeout");
- var $input = one.lib.form.input("Idle Timeout");
- $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.idleTimeout);
- }
- // cookie
- var $label = one.lib.form.label("Cookie");
- var $input = one.lib.form.input("Cookie");
- $input.attr('id', one.f.flows.id.modal.form.cookie);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.cookie);
- }
+ $modal.modal();
+ },
+ body : function() {
+ var $p = $(document.createElement('p'));
+ $p.append("Remove this action?");
+ return $p;
+ },
+ footer : function() {
+ var footer = [];
- // layer 2
- var $legend = one.lib.form.legend("Layer 2");
- $fieldset.append($legend);
- // etherType
- var $label = one.lib.form.label("Ethernet Type");
- var $input = one.lib.form.input("Ethernet Type");
- $input.attr('id', one.f.flows.id.modal.form.etherType);
- $input.val('0x800');
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.etherType);
- }
- // vlanId
- var $label = one.lib.form.label("VLAN Identification Number");
- var $input = one.lib.form.input("VLAN Identification Number");
- $input.attr('id', one.f.flows.id.modal.form.vlanId);
- var $help = one.lib.form.help("Range: 0 - 4095");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.vlanId);
- }
+ var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
+ var $removeButton = one.lib.dashlet.button.button(removeButton);
+ footer.push($removeButton);
- // vlanPriority
- var $label = one.lib.form.label("VLAN Priority");
- var $input = one.lib.form.input("VLAN Priority");
- $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
- var $help = one.lib.form.help("Range: 0 - 7");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.vlanPriority);
- }
+ var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
+ var $cancelButton = one.lib.dashlet.button.button(cancelButton);
+ footer.push($cancelButton);
- // srcMac
- var $label = one.lib.form.label("Source MAC Address");
- var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
- $input.attr('id', one.f.flows.id.modal.form.srcMac);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.srcMac);
- }
- // dstMac
- var $label = one.lib.form.label("Destination MAC Address");
- var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
- $input.attr('id', one.f.flows.id.modal.form.dstMac);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.dstMac);
- }
- // layer 3
- var $legend = one.lib.form.legend("Layer 3");
- $fieldset.append($legend);
-
- // srcIp
- var $label = one.lib.form.label("Source IP Address");
- var $input = one.lib.form.input("192.168.3.128");
- $input.attr('id', one.f.flows.id.modal.form.srcIp);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.srcIp);
- }
- // dstIp
- var $label = one.lib.form.label("Destination IP Address");
- var $input = one.lib.form.input("2001:2334::0/32");
- $input.attr('id', one.f.flows.id.modal.form.dstIp);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.dstIp);
- }
- // tosBits
- var $label = one.lib.form.label("ToS Bits");
- var $input = one.lib.form.input("ToS Bits");
- $input.attr('id', one.f.flows.id.modal.form.tosBits);
- var $help = one.lib.form.help("Range: 0 - 63");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.tosBits);
- }
+ return footer;
+ }
+ }
+ },
+ table : {
+ add : function(action, data) {
+ var $tr = $(document.createElement('tr'));
+ var $td = $(document.createElement('td'));
+ $td.append(action);
+ $tr.append($td);
+ var $td = $(document.createElement('td'));
+ if (data != undefined) $td.append(data);
+ $tr.append($td);
+ return $tr;
+ },
+ append : function($tr) {
+ var $table = $('#'+one.f.flows.id.modal.action.table);
+ var $empty = $table.find('.empty').parent();
+ if ($empty.size() > 0) $empty.remove();
+ $table.append($tr);
+ }
+ },
+ body : {
+ common : function() {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ return [$form, $fieldset];
+ },
+ addOutputPorts : function() {
+ var common = one.f.flows.modal.action.body.common();
+ var $form = common[0];
+ var $fieldset = common[1];
+ // output port
+ $label = one.lib.form.label("Select Output Ports");
+ if (one.f.flows.registry.currentNode == undefined){
+ return; //Selecting Output ports without selecting node throws an exception
+ }
+ var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+ $select = one.lib.form.select.create(ports, true);
+ $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+ $fieldset.append($label).append($select);
+ $form.append($fieldset);
+ return $form;
+ },
+ addEnqueue : function() {
+ var common = one.f.flows.modal.action.body.common();
+ var $form = common[0];
+ var $fieldset = common[1];
+ // output port
+ $label = one.lib.form.label("Select Output Ports");
+ if (one.f.flows.registry.currentNode == undefined){
+ return; //Selecting Output ports without selecting node throws an exception
+ }
+ var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+ $select = one.lib.form.select.create(ports);
+ $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+ $fieldset.append($label).append($select);
+ $label = one.lib.form.label('Queue (Optional)');
+ $input = one.lib.form.input('Queue')
+ .attr('id', one.f.flows.id.modal.action.queue);
+ $help = one.lib.form.help('Range: 1 - 2147483647');
+ $fieldset.append($label).append($input).append($help);
+ $form.append($fieldset);
+ return $form;
+ },
+ set : function(label, placeholder, id, help) {
+ var common = one.f.flows.modal.action.body.common();
+ var $form = common[0];
+ var $fieldset = common[1];
+ // input
+ $label = one.lib.form.label(label);
+ $input = one.lib.form.input(placeholder);
+ $input.attr('id', id);
+ $help = one.lib.form.help(help);
+ // append
+ $fieldset.append($label).append($input).append($help);
+ $form.append($fieldset);
+ return $form;
+ }
+ },
+ footer : function() {
+ var footer = [];
+ var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
+ var $addButton = one.lib.dashlet.button.button(addButton);
+ footer.push($addButton);
+
+ var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
+
+ return footer;
+ }
+ },
+ footer : function() {
+ var footer = [];
- // layer 4
- var $legend = one.lib.form.legend("Layer 4");
- $fieldset.append($legend);
- // srcPort
- var $label = one.lib.form.label("Source Port");
- var $input = one.lib.form.input("Source Port");
- $input.attr('id', one.f.flows.id.modal.form.srcPort);
- var $help = one.lib.form.help("Range: 0 - 65535");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.srcPort);
- }
- // dstPort
- var $label = one.lib.form.label("Destination Port");
- var $input = one.lib.form.input("Destination Port");
- $input.attr('id', one.f.flows.id.modal.form.dstPort);
- var $help = one.lib.form.help("Range: 0 - 65535");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.dstPort);
- }
- // protocol
- var $label = one.lib.form.label("Protocol");
- var $input = one.lib.form.input("Protocol");
- $input.attr('id', one.f.flows.id.modal.form.protocol);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.protocol);
- }
- // actions
- var $legend = one.lib.form.label("Actions");
- $fieldset.append($legend);
- // actions table
- var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
- var $table = one.lib.dashlet.table.table(tableAttributes);
- $table.attr('id', one.f.flows.id.modal.action.table);
- var tableHeaders = ["Action", "Data"];
- var $thead = one.lib.dashlet.table.header(tableHeaders);
- var $tbody = one.lib.dashlet.table.body("", tableHeaders);
- $table.append($thead).append($tbody);
- // actions
- var actions = {
- "" : "Please Select an Action",
- "DROP" : "Drop",
- "LOOPBACK" : "Loopback",
- "FLOOD" : "Flood",
- "SW_PATH" : "Software Path",
- "HW_PATH" : "Hardware Path",
- "CONTROLLER" : "Controller",
- "OUTPUT" : "Add Output Ports",
- "SET_VLAN_ID" : "Set VLAN ID",
- "SET_VLAN_PCP" : "Set VLAN Priority",
- "POP_VLAN" : "Strip VLAN Header",
- "SET_DL_SRC" : "Modify Datalayer Source Address",
- "SET_DL_DST" : "Modify Datalayer Destination Address",
- "SET_NW_SRC" : "Modify Network Source Address",
- "SET_NW_DST" :"Modify Network Destination Address",
- "SET_NW_TOS" : "Modify ToS Bits",
- "SET_TP_SRC" : "Modify Transport Source Port",
- "SET_TP_DST" : "Modify Transport Destination Port"
- };
- var $select = one.lib.form.select.create(actions);
- // when selecting an action
- $select.change(function() {
- var action = $(this).find('option:selected');
- one.f.flows.modal.action.parse(action.attr('value'));
- $select[0].selectedIndex = 0;
- });
+ var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
+ var $installButton = one.lib.dashlet.button.button(installButton);
+ footer.push($installButton);
- if(edit) {
- $(existingFlow.actions).each(function(index, value){
- setTimeout(function(){
- var locEqualTo = value.indexOf("=");
- if ( locEqualTo == -1 ) {
- one.f.flows.modal.action.add.add(actions[value], value);
- } else {
- var action = value.substr(0,locEqualTo);
- if( action == "OUTPUT") {
- var portIds = value.substr(locEqualTo+1).split(",");
- var ports = [];
- var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
- for(var i =0; i < portIds.length ; i++) {
- var portName = allPorts[portIds[i]];
- ports.push(portName);
- }
- one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
- } else {
- var val = value.substr(locEqualTo+1);
- one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
- }
- }
- }, 1000)
- });
- }
- $fieldset.append($select).append($table);
+ var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
+ var $addButton = one.lib.dashlet.button.button(addButton);
+ footer.push($addButton);
- // return
- $form.append($fieldset);
- return $form;
- },
- action : {
- parse : function(option) {
- switch (option) {
- case "OUTPUT" :
- var h3 = "Add Output Port";
- var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
- $modal.modal();
- break;
- case "SET_VLAN_ID" :
- var h3 = "Set VLAN ID";
- var placeholder = "VLAN Identification Number";
- var id = one.f.flows.id.modal.action.setVlanId;
- var help = "Range: 0 - 4095";
- var action = 'SET_VLAN_ID';
- var name = "VLAN ID";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_VLAN_PCP" :
- var h3 = "Set VLAN Priority";
- var placeholder = "VLAN Priority";
- var id = one.f.flows.id.modal.action.setVlanPriority;
- var help = "Range: 0 - 7";
- var action = 'SET_VLAN_PCP';
- var name = "VLAN Priority";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "POP_VLAN" :
- var name = "Strip VLAN Header";
- var action = 'POP_VLAN';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "SET_DL_SRC" :
- var h3 = "Set Source MAC Address";
- var placeholder = "Source MAC Address";
- var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
- var help = "Example: 00:11:22:aa:bb:cc";
- var action = 'SET_DL_SRC';
- var name = "Source MAC";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_DL_DST" :
- var h3 = "Set Destination MAC Address";
- var placeholder = "Destination MAC Address";
- var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
- var help = "Example: 00:11:22:aa:bb:cc";
- var action = 'SET_DL_DST';
- var name = "Destination MAC";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_NW_SRC" :
- var h3 = "Set IP Source Address";
- var placeholder = "Source IP Address";
- var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
- var help = "Example: 127.0.0.1";
- var action = 'SET_NW_SRC';
- var name = "Source IP";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_NW_DST" :
- var h3 = "Set IP Destination Address";
- var placeholder = "Destination IP Address";
- var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
- var help = "Example: 127.0.0.1";
- var action = 'SET_NW_DST';
- var name = "Destination IP";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_NW_TOS" :
- var h3 = "Set IPv4 ToS";
- var placeholder = "IPv4 ToS";
- var id = one.f.flows.id.modal.action.modifyTosBits;
- var help = "Range: 0 - 63";
- var action = 'SET_NW_TOS';
- var name = "ToS Bits";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_TP_SRC" :
- var h3 = "Set Transport Source Port";
- var placeholder = "Transport Source Port";
- var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
- var help = "Range: 1 - 65535";
- var action = 'SET_TP_SRC';
- var name = "Source Port";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_TP_DST" :
- var h3 = "Set Transport Destination Port";
- var placeholder = "Transport Destination Port";
- var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
- var help = "Range: 1 - 65535";
- var action = 'SET_TP_DST';
- var name = "Destination Port";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "DROP" :
- var name = "Drop";
- var action = 'DROP';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "LOOPBACK" :
- var name = "Loopback";
- var action = 'LOOPBACK';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "FLOOD" :
- var name = "Flood";
- var action = 'FLOOD';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "SW_PATH" :
- var name = "Software Path";
- var action = 'SW_PATH';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "HW_PATH" :
- var name = "Hardware Path";
- var action = 'HW_PATH';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "CONTROLLER" :
- var name = "Controller";
- var action = 'CONTROLLER';
- one.f.flows.modal.action.add.add(name, action);
- break;
- }
- },
- initialize : function(h3, bodyCallback, addCallback) {
- var footer = one.f.flows.modal.action.footer();
- var $body = bodyCallback();
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
- // bind close button
- $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
- $modal.modal('hide');
- });
- // bind add flow button
- $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
- addCallback($modal);
- });
- return $modal;
- },
- add : {
- addOutputPorts : function($modal) {
- var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
- var ports = '';
- var pid = '';
- $options.each(function(index, value) {
- ports = ports+$(value).text()+", ";
- pid = pid+$(value).attr('value')+",";
- });
- ports = ports.slice(0,-2);
- pid = pid.slice(0,-1);
- one.f.flows.modal.action.add.addPortsToTable(ports, pid);
- $modal.modal('hide');
- },
- addPortsToTable : function(ports, pid){
- var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
- $tr.attr('id', 'OUTPUT');
- $tr.data('action', 'OUTPUT='+pid);
- $tr.click(function() {
- one.f.flows.modal.action.add.modal.initialize(this);
- });
- one.f.flows.modal.action.table.append($tr);
- },
- add : function(name, action) {
- var $tr = one.f.flows.modal.action.table.add(name);
- $tr.attr('id', action);
- $tr.data('action', action);
- $tr.click(function() {
- one.f.flows.modal.action.add.modal.initialize(this);
- });
- one.f.flows.modal.action.table.append($tr);
- },
- set : function(name, id, action, $modal) {
- var $input = $('#'+id);
- var value = $input.val();
- one.f.flows.modal.action.add.addDataToTable(name,value,action)
- $modal.modal('hide');
- },
- addDataToTable : function(name,value,action) {
- var $tr = one.f.flows.modal.action.table.add(name, value);
- $tr.attr('id', action);
- $tr.data('action', action+'='+value);
- $tr.click(function() {
- one.f.flows.modal.action.add.modal.initialize(this);
- });
- one.f.flows.modal.action.table.append($tr);
- },
- remove : function(that) {
- $(that).remove();
- var $table = $('#'+one.f.flows.id.modal.action.table);
- if ($table.find('tbody').find('tr').size() == 0) {
- var $tr = $(document.createElement('tr'));
- var $td = $(document.createElement('td'));
- $td.attr('colspan', '3');
- $tr.addClass('empty');
- $td.text('No data available');
- $tr.append($td);
- $table.find('tbody').append($tr);
- }
- },
- modal : {
- initialize : function(that) {
- var h3 = "Remove Action";
- var footer = one.f.flows.modal.action.add.modal.footer();
- var $body = one.f.flows.modal.action.add.modal.body();
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
-
- // bind cancel button
- $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
- $modal.modal('hide');
- });
-
- // bind remove button
- $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
- one.f.flows.modal.action.add.remove(that);
- $modal.modal('hide');
- });
-
- $modal.modal();
- },
- body : function() {
- var $p = $(document.createElement('p'));
- $p.append("Remove this action?");
- return $p;
- },
- footer : function() {
- var footer = [];
-
- var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
- var $removeButton = one.lib.dashlet.button.button(removeButton);
- footer.push($removeButton);
-
- var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
- var $cancelButton = one.lib.dashlet.button.button(cancelButton);
- footer.push($cancelButton);
-
- return footer;
- }
- }
- },
- table : {
- add : function(action, data) {
- var $tr = $(document.createElement('tr'));
- var $td = $(document.createElement('td'));
- $td.append(action);
- $tr.append($td);
- var $td = $(document.createElement('td'));
- if (data != undefined) $td.append(data);
- $tr.append($td);
- return $tr;
- },
- append : function($tr) {
- var $table = $('#'+one.f.flows.id.modal.action.table);
- var $empty = $table.find('.empty').parent();
- if ($empty.size() > 0) $empty.remove();
- $table.append($tr);
- }
- },
- body : {
- common : function() {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- return [$form, $fieldset];
- },
- addOutputPorts : function() {
- var common = one.f.flows.modal.action.body.common();
- var $form = common[0];
- var $fieldset = common[1];
- // output port
- $label = one.lib.form.label("Select Output Ports");
- if (one.f.flows.registry.currentNode == undefined){
- return; //Selecting Output ports without selecting node throws an exception
- }
- var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
- $select = one.lib.form.select.create(ports, true);
- $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
- $fieldset.append($label).append($select);
- $form.append($fieldset);
- return $form;
- },
- set : function(label, placeholder, id, help) {
- var common = one.f.flows.modal.action.body.common();
- var $form = common[0];
- var $fieldset = common[1];
- // input
- $label = one.lib.form.label(label);
- $input = one.lib.form.input(placeholder);
- $input.attr('id', id);
- $help = one.lib.form.help(help);
- // append
- $fieldset.append($label).append($input).append($help);
- $form.append($fieldset);
- return $form;
- }
- },
- footer : function() {
- var footer = [];
- var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
- var $addButton = one.lib.dashlet.button.button(addButton);
- footer.push($addButton);
-
- var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
-
- return footer;
- }
- },
- footer : function() {
- var footer = [];
+ var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
- var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
- var $installButton = one.lib.dashlet.button.button(installButton);
- footer.push($installButton);
+ return footer;
+ },
+ footerEdit : function() {
+ var footer = [];
- var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
- var $addButton = one.lib.dashlet.button.button(addButton);
- footer.push($addButton);
+ var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
+ var $editButton = one.lib.dashlet.button.button(editButton);
+ footer.push($editButton);
- var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
+ var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
- return footer;
- },
- footerEdit : function() {
- var footer = [];
+ return footer;
+ },
+ removeMultiple: {
+ dialog: function(flows) {
+ var h3 = 'Remove Flow Entry';
+ var flowList = [];
+ for (var i = 0; i < flows.length; i++) {
+ flowList.push(flows[i]["name"]);
+ }
+ var footer = one.f.flows.modal.removeMultiple.footer();
+ var $body = one.f.flows.modal.removeMultiple.body(flowList);
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
- var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
- var $editButton = one.lib.dashlet.button.button(editButton);
- footer.push($editButton);
+ // bind close button
+ $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
- var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
+ // bind remove rule button
+ $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
+ var resource = {};
+ resource['body'] = JSON.stringify(flows);
- return footer;
- },
- removeMultiple: {
- dialog: function(flows) {
- var h3 = 'Remove Flow Entry';
- var flowList = [];
- for (var i = 0; i < flows.length; i++) {
- flowList.push(flows[i]["name"]);
- }
- var footer = one.f.flows.modal.removeMultiple.footer();
- var $body = one.f.flows.modal.removeMultiple.body(flowList);
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
-
- // bind close button
- $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
- $modal.modal('hide');
- });
-
- // bind remove rule button
- $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
- var resource = {};
- resource['body'] = JSON.stringify(flows);
-
- $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
- $modal.modal('hide');
- if(response == "Success") {
- one.lib.alert("Flow Entry(s) successfully removed");
- } else {
- one.lib.alert(response);
- }
- one.main.dashlet.right.bottom.empty();
- one.f.detail.dashlet(one.main.dashlet.right.bottom);
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- });
- });
- $modal.modal();
- },
- footer : function() {
- var footer = [];
- var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
- var $remove = one.lib.dashlet.button.button(remove);
- footer.push($remove);
-
- var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
- var $cancel = one.lib.dashlet.button.button(cancel);
- footer.push($cancel);
-
- return footer;
- },
- body : function (flows) {
- var $p = $(document.createElement('p'));
- var p = 'Remove the following Flow Entry(s)?';
- //creata a BS label for each rule and append to list
- $(flows).each(function(){
- var $span = $(document.createElement('span'));
- $span.append(this);
- p += '<br/>' + $span[0].outerHTML;
- });
- $p.append(p);
- return $p;
+ $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
+ $modal.modal('hide');
+ if(response == "Success") {
+ one.lib.alert("Flow Entry(s) successfully removed");
+ } else {
+ one.lib.alert(response);
}
- }
- },
- ajax : {
- dashlet : function(callback) {
- $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
- one.f.flows.registry['flows'] = data.flows;
- one.f.flows.registry['privilege'] = data.privilege;
- one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+ one.main.dashlet.right.bottom.empty();
+ one.f.detail.dashlet(one.main.dashlet.right.bottom);
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ });
+ });
+ $modal.modal();
+ },
+ footer : function() {
+ var footer = [];
+ var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
+ var $remove = one.lib.dashlet.button.button(remove);
+ footer.push($remove);
+
+ var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
+ var $cancel = one.lib.dashlet.button.button(cancel);
+ footer.push($cancel);
+
+ return footer;
+ },
+ body : function (flows) {
+ var $p = $(document.createElement('p'));
+ var p = 'Remove the following Flow Entry(s)?';
+ //creata a BS label for each rule and append to list
+ $(flows).each(function(){
+ var $span = $(document.createElement('span'));
+ $span.append(this);
+ p += '<br/>' + $span[0].outerHTML;
+ });
+ $p.append(p);
+ return $p;
+ }
+ }
+ },
+ ajax : {
+ dashlet : function(callback) {
+ $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
+ one.f.flows.registry['flows'] = data.flows;
+ one.f.flows.registry['privilege'] = data.privilege;
+ one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+
+ callback(data);
+ });
+ }
+ },
+ data : {
+ flowsDataGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'selector',
+ label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
+ sortable: false
+ },
+ {
+ property: 'name',
+ label: 'Flow Name',
+ sortable: true
+ },
+ {
+ property: 'node',
+ label: 'Node',
+ sortable: true
+ }
+ ],
+ data: data.flows,
+ formatter: function(items) {
+ $.each(items, function(index, item) {
+ var $checkbox = document.createElement("input");
+ $checkbox.setAttribute("type", "checkbox");
+ $checkbox.setAttribute("name", item.name);
+ $checkbox.setAttribute("node", item.nodeId);
+ $checkbox.setAttribute('class','flowEntry')
+ item.selector = $checkbox.outerHTML;
+ item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] +
+ ' data-flowstatus=' + item["flow"]["status"] +
+ ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
+ });
- callback(data);
- });
- }
- },
- data : {
- flowsDataGrid: function(data) {
- var source = new StaticDataSource({
- columns: [
- {
- property: 'selector',
- label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
- sortable: false
- },
- {
- property: 'name',
- label: 'Flow Name',
- sortable: true
- },
- {
- property: 'node',
- label: 'Node',
- sortable: true
- }
- ],
- data: data.flows,
- formatter: function(items) {
- $.each(items, function(index, item) {
- var $checkbox = document.createElement("input");
- $checkbox.setAttribute("type", "checkbox");
- $checkbox.setAttribute("name", item.name);
- $checkbox.setAttribute("node", item.nodeId);
- $checkbox.setAttribute('class','flowEntry')
- item.selector = $checkbox.outerHTML;
- item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] +
- ' data-flowstatus=' + item["flow"]["status"] +
- ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
- });
-
- },
- delay: 0
- });
- return source;
- },
- dashlet : function(data) {
- var body = [];
- $(data).each(function(index, value) {
- var tr = {};
- var entry = [];
-
-
- entry.push(value['name']);
- entry.push(value['node']);
- if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
- tr['type'] = ['success'];
- else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
- tr['type'] = ['warning'];
- else
- tr['type'] = ['warning'];
- tr['entry'] = entry;
- tr['id'] = value['nodeId'];
-
- body.push(tr);
- });
- return body;
- }
+ },
+ delay: 0
+ });
+ return source;
},
- body : {
- dashlet : function(body, callback) {
- var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
- var $table = one.lib.dashlet.table.table(attributes);
-
- var headers = ['Flow Name', 'Node'];
-
- var $thead = one.lib.dashlet.table.header(headers);
- $table.append($thead);
-
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
- return $table;
- }
+ dashlet : function(data) {
+ var body = [];
+ $(data).each(function(index, value) {
+ var tr = {};
+ var entry = [];
+
+
+ entry.push(value['name']);
+ entry.push(value['node']);
+ if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
+ tr['type'] = ['success'];
+ else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
+ tr['type'] = ['warning'];
+ else
+ tr['type'] = ['warning'];
+ tr['entry'] = entry;
+ tr['id'] = value['nodeId'];
+
+ body.push(tr);
+ });
+ return body;
}
+ },
+ body : {
+ dashlet : function(body, callback) {
+ var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
+ var $table = one.lib.dashlet.table.table(attributes);
+
+ var headers = ['Flow Name', 'Node'];
+
+ var $thead = one.lib.dashlet.table.header(headers);
+ $table.append($thead);
+
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
+ return $table;
+ }
+ }
}
/** INIT **/
// populate nav tabs
$(one.f.menu.left.top).each(function(index, value) {
- var $nav = $(".nav", "#left-top");
- one.main.page.dashlet($nav, value);
+ var $nav = $(".nav", "#left-top");
+ one.main.page.dashlet($nav, value);
});
$(one.f.menu.left.bottom).each(function(index, value) {
- var $nav = $(".nav", "#left-bottom");
- one.main.page.dashlet($nav, value);
+ var $nav = $(".nav", "#left-bottom");
+ one.main.page.dashlet($nav, value);
});
$(one.f.menu.right.bottom).each(function(index, value) {
- var $nav = $(".nav", "#right-bottom");
- one.main.page.dashlet($nav, value);
+ var $nav = $(".nav", "#right-bottom");
+ one.main.page.dashlet($nav, value);
});
one.f.populate = function($dashlet, header) {
- var $h4 = one.lib.dashlet.header(header);
- $dashlet.append($h4);
+ var $h4 = one.lib.dashlet.header(header);
+ $dashlet.append($h4);
};
// bind dashlet nav
$('.dash .nav a', '#main').click(function() {
- // de/activation
- var $li = $(this).parent();
- var $ul = $li.parent();
- one.lib.nav.unfocus($ul);
- $li.addClass('active');
- // clear respective dashlet
- var $dashlet = $ul.parent().find('.dashlet');
- one.lib.dashlet.empty($dashlet);
- // callback based on menu
- var id = $(this).attr('id');
- var menu = one.f.dashlet;
- switch (id) {
- case menu.flows.id:
- one.f.flows.dashlet($dashlet);
- break;
- case menu.nodes.id:
- one.f.nodes.dashlet($dashlet);
- break;
- case menu.detail.id:
- one.f.detail.dashlet($dashlet);
- break;
- };
+ // de/activation
+ var $li = $(this).parent();
+ var $ul = $li.parent();
+ one.lib.nav.unfocus($ul);
+ $li.addClass('active');
+ // clear respective dashlet
+ var $dashlet = $ul.parent().find('.dashlet');
+ one.lib.dashlet.empty($dashlet);
+ // callback based on menu
+ var id = $(this).attr('id');
+ var menu = one.f.dashlet;
+ switch (id) {
+ case menu.flows.id:
+ one.f.flows.dashlet($dashlet);
+ break;
+ case menu.nodes.id:
+ one.f.nodes.dashlet($dashlet);
+ break;
+ case menu.detail.id:
+ one.f.detail.dashlet($dashlet);
+ break;
+ };
});
// activate first tab on each dashlet
$('.dash .nav').each(function(index, value) {
- $($(value).find('li')[0]).find('a').click();
+ $($(value).find('li')[0]).find('a').click();
});