--- /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.arphandler;
+
+public class ARPCacheEvent {
+ private ARPEvent event;
+ private boolean newReply;
+
+ public ARPCacheEvent(ARPEvent event, boolean newReply) {
+ super();
+ this.event = event;
+ this.newReply = newReply;
+ }
+
+ public ARPEvent getEvent() {
+ return event;
+ }
+
+ public boolean isNewReply() {
+ return newReply;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((event == null) ? 0 : event.hashCode());
+ result = prime * result + (newReply ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ARPCacheEvent other = (ARPCacheEvent) obj;
+ if (event == null) {
+ if (other.event != null)
+ return false;
+ } else if (!event.equals(other.event))
+ return false;
+ if (newReply != other.newReply)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "ARPCacheEvent [event=" + event + ", newReply=" + newReply + "]";
+ }
+}
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.opendaylight.controller.arphandler.ARPCacheEvent;
import org.opendaylight.controller.arphandler.ARPEvent;
import org.opendaylight.controller.arphandler.ARPReply;
import org.opendaylight.controller.arphandler.ARPRequest;
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.topology.TopoEdgeUpdate;
import org.opendaylight.controller.sal.utils.EtherTypes;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.NetUtils;
private ConcurrentMap<InetAddress, Set<HostNodeConnector>> arpRequestors;
private ConcurrentMap<InetAddress, Short> countDownTimers;
private Timer periodicTimer;
+ private BlockingQueue<ARPCacheEvent> ARPCacheEvents = new LinkedBlockingQueue<ARPCacheEvent>();
+ Thread cacheEventHandler;
/*
* A cluster allocated cache. Used for synchronizing ARP request/reply
* events across all cluster controllers. To raise an event, we put() a specific
void init() {
arpRequestors = new ConcurrentHashMap<InetAddress, Set<HostNodeConnector>>();
countDownTimers = new ConcurrentHashMap<InetAddress, Short>();
+ cacheEventHandler = new Thread(new ARPCacheEventHandler(), "ARPCacheEventHandler Thread");
allocateCaches();
retrieveCaches();
try{
clusterContainerService.createCache(ARP_EVENT_CACHE_NAME,
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheConfigException e){
log.error("ARPHandler cache configuration invalid!");
} catch (CacheExistException e){
*/
void start() {
startPeriodicTimer();
+ cacheEventHandler.start();
+
}
/**
@Override
public void entryUpdated(ARPEvent key, Boolean new_value, String cacheName, boolean originLocal) {
- if (key instanceof ARPRequest) {
- ARPRequest req = (ARPRequest) key;
- // If broadcast request
- if (req.getHost() == null) {
- sendBcastARPRequest(req.getTargetIP(), req.getSubnet());
-
- //If unicast and local, send reply
- } else if (connectionManager.isLocal(req.getHost().getnodeconnectorNode())) {
- sendUcastARPRequest(req.getHost(), req.getSubnet());
- }
- } else if (key instanceof ARPReply) {
- ARPReply rep = (ARPReply) key;
- // New reply received by controller, notify all awaiting requestors across the cluster
- if (new_value) {
- generateAndSendReply(rep.getTargetIP(), rep.getTargetMac());
-
- // Otherwise, a specific reply. If local, send out.
- } else if (connectionManager.isLocal(rep.getPort().getNode())) {
- sendARPReply(rep.getPort(),
- rep.getSourceMac(),
- rep.getSourceIP(),
- rep.getTargetMac(),
- rep.getTargetIP());
- }
- }
+ enqueueARPCacheEvent(key, new_value);
}
@Override
public void entryDeleted(ARPEvent key, String cacheName, boolean originLocal) {
// nothing to do
}
+
+ private void enqueueARPCacheEvent (ARPEvent event, boolean new_value) {
+ try {
+ ARPCacheEvent cacheEvent = new ARPCacheEvent(event, new_value);
+ if (!ARPCacheEvents.contains(cacheEvent)) {
+ this.ARPCacheEvents.add(cacheEvent);
+ }
+ } catch (Exception e) {
+ log.debug("enqueueARPCacheEvent caught Interrupt Exception for event {}", event);
+ }
+ }
+
+ /*
+ * this thread monitors the connectionEvent queue for new incoming events from
+ */
+ private class ARPCacheEventHandler implements Runnable {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ ARPCacheEvent ev = ARPCacheEvents.take();
+ ARPEvent event = ev.getEvent();
+ if (event instanceof ARPRequest) {
+ ARPRequest req = (ARPRequest) event;
+ // If broadcast request
+ if (req.getHost() == null) {
+ sendBcastARPRequest(req.getTargetIP(), req.getSubnet());
+
+ //If unicast and local, send reply
+ } else if (connectionManager.isLocal(req.getHost().getnodeconnectorNode())) {
+ sendUcastARPRequest(req.getHost(), req.getSubnet());
+ }
+ } else if (event instanceof ARPReply) {
+ ARPReply rep = (ARPReply) event;
+ // New reply received by controller, notify all awaiting requestors across the cluster
+ if (ev.isNewReply()) {
+ generateAndSendReply(rep.getTargetIP(), rep.getTargetMac());
+
+ // Otherwise, a specific reply. If local, send out.
+ } else if (connectionManager.isLocal(rep.getPort().getNode())) {
+ sendARPReply(rep.getPort(),
+ rep.getSourceMac(),
+ rep.getSourceIP(),
+ rep.getTargetMac(),
+ rep.getTargetIP());
+ }
+ }
+ } catch (InterruptedException e) {
+ ARPCacheEvents.clear();
+ return;
+ }
+ }
+ }
+ }
}
if (amIGossipRouter) {
logger.info("I'm a GossipRouter will listen on port {}",
gossipRouterPort);
- res = new GossipRouter(gossipRouterPort);
+ // Start a GossipRouter with JMX support
+ res = new GossipRouter(gossipRouterPort, null, true);
}
return res;
}
syncRollbackPhase="true"
syncCommitPhase="true"
cacheStopTimeout="30000"
- use1PcForAutoCommitTransactions="false"
+ use1PcForAutoCommitTransactions="true"
autoCommit="true"
lockingMode="OPTIMISTIC"
useSynchronization="true"
<geminiweb.version>2.2.0.RELEASE</geminiweb.version>
<checkstyle.version>2.10</checkstyle.version>
<testvm.argLine>-Xmx1024m -XX:MaxPermSize=256m</testvm.argLine>
+ <yang.version>0.5.7-SNAPSHOT</yang.version>
+ <guava.version>14.0.1</guava.version>
+ <ietf-inet-types.version>2010.09.24-SNAPSHOT</ietf-inet-types.version>
+ <ietf-yang-types.version>2010.09.24-SNAPSHOT</ietf-yang-types.version>
+ <yang-ext.version>2013.09.07-SNAPSHOT</yang-ext.version>
+ <javassist.version>3.17.1-GA</javassist.version>
+ <sample-toaster.version>1.0-SNAPSHOT</sample-toaster.version>
</properties>
<pluginRepositories>
<artifactId>jersey-json</artifactId>
<version>${jersey.version}</version>
</dependency>
+
+ <!-- yangtools -->
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-util</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+
+ <!-- Models -->
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ <version>${ietf-inet-types.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ <version>${ietf-yang-types.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-ext</artifactId>
+ <version>${yang-ext.version}</version>
+ </dependency>
+
+ <!-- Other MDSAL dependencies -->
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ <version>${javassist.version}</version>
+ </dependency>
</dependencies>
</project>
}
try {
this.clusterServices.createCache("config.event.save",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheConfigException cce) {
logger.error("Error creating Configuration cache ", cce);
} catch (CacheExistException cce) {
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.opendaylight.controller.sal.inventory.IInventoryService;
import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
public class Activator extends ComponentActivatorAbstractBase {
* ComponentActivatorAbstractBase.
*
*/
+ @Override
public void init() {
}
* cleanup done by ComponentActivatorAbstractBase
*
*/
+ @Override
public void destroy() {
}
* @return The list of implementations the bundle will support,
* in Global version
*/
+ @Override
protected Object[] getGlobalImplementations() {
Object[] res = { ConnectionManager.class };
return res;
* @param imp implementation to be configured
* @param containerName container on which the configuration happens
*/
+ @Override
protected void configureGlobalInstance(Component c, Object imp) {
if (imp.equals(ConnectionManager.class)) {
Dictionary<String, Object> props = new Hashtable<String, Object>();
c.add(createServiceDependency().setService(IConnectionService.class)
.setCallbacks("setConnectionService", "unsetConnectionService")
.setRequired(true));
+ c.add(createServiceDependency().setService(IInventoryService.class, "(scope=Global)")
+ .setCallbacks("setInventoryService", "unsetInventoryService")
+ .setRequired(true));
}
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.core.Property;
import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.inventory.IInventoryService;
import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
import org.opendaylight.controller.sal.utils.Status;
import org.opendaylight.controller.sal.utils.StatusCode;
private IConnectionService connectionService;
private Thread connectionEventThread;
private BlockingQueue<ConnectionMgmtEvent> connectionEvents;
+ private IInventoryService inventoryService;
public void setClusterServices(IClusterGlobalServices i) {
this.clusterServices = i;
}
}
+ public void setInventoryService(IInventoryService service) {
+ logger.trace("Got inventory service set request {}", service);
+ this.inventoryService = service;
+ }
+
+ public void unsetInventoryService(IInventoryService service) {
+ logger.trace("Got a service UNset request");
+ this.inventoryService = null;
+ }
+
+ private void getInventories() {
+ Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
+ for (Map.Entry<Node, Map<String, Property>> entry : nodeProp.entrySet()) {
+ Node node = entry.getKey();
+ logger.debug("getInventories for node:{}", new Object[] { node });
+ Map<String, Property> propMap = entry.getValue();
+ Set<Property> props = new HashSet<Property>();
+ for (Property property : propMap.values()) {
+ props.add(property);
+ }
+ updateNode(node, UpdateType.ADDED, props);
+ }
+
+ Map<NodeConnector, Map<String, Property>> nodeConnectorProp = this.inventoryService.getNodeConnectorProps();
+ for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProp.entrySet()) {
+ Map<String, Property> propMap = entry.getValue();
+ Set<Property> props = new HashSet<Property>();
+ for (Property property : propMap.values()) {
+ props.add(property);
+ }
+ updateNodeConnector(entry.getKey(), UpdateType.ADDED, props);
+ }
+ }
+
public void started() {
connectionEventThread = new Thread(new EventHandler(), "ConnectionEvent Thread");
connectionEventThread.start();
registerWithOSGIConsole();
notifyClusterViewChanged();
+ // Should pull the Inventory updates in case we missed it
+ getInventories();
}
public void init() {
@SuppressWarnings("deprecation")
public void handleClusterViewChanged() {
+ log.debug("Handling Cluster View changed notification");
List<InetAddress> controllers = clusterServices.getClusteredControllers();
ConcurrentMap <InetAddress, Set<Node>> controllerNodesMap = getControllerToNodesMap();
List<InetAddress> toRemove = new ArrayList<InetAddress>();
clusterServices.tcommit();
}
} catch (Exception e) {
- log.error("Exception in replacing nodeConnections ", e);
- retry = false;
+ log.debug("Exception in replacing nodeConnections ", e);
+ retry = true;
try {
clusterServices.trollback();
} catch (Exception e1) {}
}
if (retry) {
try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {}
handleClusterViewChanged();
}
}
* with this controller to take hold of a Node.
*/
if (isConnectionAllowed(node)) {
- if (!nodeConnections.replace(node, oldControllers, newControllers)) {
+ if (oldControllers == null || !nodeConnections.replace(node, oldControllers, newControllers)) {
clusterServices.trollback();
try {
Thread.sleep(100);
} catch ( InterruptedException e) {}
- log.debug("Replace failed... old={} with new={} for {} to {}", oldControllers.toString(), newControllers.toString(),
- controller.getHostAddress(), node.toString());
+ log.debug("Retrying ... {} with {}", controller.getHostAddress(), node.toString());
return putNodeToController(node, controller);
} else {
log.debug("Replace successful old={} with new={} for {} to {}", oldControllers.toString(), newControllers.toString(),
import org.opendaylight.controller.sal.utils.Status;
/**
- * Container Manager interface
- *
+ * Container Manager Interface - provides methods to get information on existing OSGI containers
*
*/
public interface IContainerManager {
/**
* Returns true if there are any non-default Containers present.
*
- * @return true, if any non-default container is present, else false
+ * @return true if any non-default container is present false otherwise.
*/
public boolean hasNonDefaultContainer();
/**
- * Returns a list of Containers that currently exist.
+ * Returns a list of the existing containers.
*
- * @return array of String Container names
+ * @return List of Container name strings.
*/
public List<String> getContainerNames();
*
* @return status code
*/
+ @Deprecated
public Status saveContainerConfig();
}
<listEntry value="4"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
-<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;switchmanager.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;commons.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;configuration&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;configuration.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;containermanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;flowprogrammer.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwarding.staticrouting.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;hosttracker&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;hosttracker.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;protocol_plugins.openflow&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;statistics.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;subnets.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;topology.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;containermanager.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;devices.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;flows.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;samples.simpleforwarding&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;topology.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;troubleshoot.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;clustering.services&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;mactracker&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;sal&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;slicemanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;switchmanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;topologymanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;arphandler&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;clustering.services-implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;clustering.test&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;configuration.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwarding.ipswitch&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwarding.staticrouting&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwardingrulesmanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;home.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;monitor&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;northboundtest&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;onep.topology.southbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;openflowj&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;protocol_plugin.openflow&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;routing.dijkstra_implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;sal.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;slicemanager.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;statisticsmanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;tifmgr&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;usermanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> </sourceContainers> </sourceLookupDirector> "/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sourceLookupDirector> <sourceContainers duplicates="false"> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;switchmanager.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;commons.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;configuration&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;configuration.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;containermanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;flowprogrammer.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwarding.staticrouting.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;hosttracker&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;hosttracker.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;protocol_plugins.openflow&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;statistics.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;subnets.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;topology.northbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;containermanager.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;devices.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;flows.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;samples.simpleforwarding&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;topology.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;troubleshoot.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;clustering.services&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;mactracker&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;sal&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;slicemanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;switchmanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;topologymanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;arphandler&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;clustering.services-implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;clustering.test&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;configuration.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwarding.ipswitch&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwarding.staticrouting&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwardingrulesmanager.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;forwardingrulesmanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;home.web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;monitor&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;northboundtest&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;onep.topology.southbound&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;openflowj&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;protocol_plugin.openflow&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;routing.dijkstra_implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;sal.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;slicemanager.implementation&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;statisticsmanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;tifmgr&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;usermanager&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> <container memento="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;javaProject name=&quot;web&quot;/&gt;&#10;" typeId="org.eclipse.jdt.launching.sourceContainer.javaProject"/> </sourceContainers> </sourceLookupDirector> "/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
<prerequisites>
<maven>3.0</maven>
</prerequisites>
-
<scm>
- <connection>scm:git:https://git.opendaylight.org/gerrit/p/controller.git</connection>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
<developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
</scm>
<parent>
<groupId>org.opendaylight.controller</groupId>
<module>../../../third-party/openflowj</module>
<module>../../../third-party/net.sf.jung2</module>
<module>../../../third-party/jersey-servlet</module>
+ <module>../../../third-party/commons/thirdparty</module>
<!-- SAL bundles -->
<module>../../sal/api</module>
<module>../../sal/networkconfiguration/api</module>
<module>../../sal/networkconfiguration/implementation</module>
+ <!-- MD-SAL bundles -->
+ <module>../../sal/yang-prototype</module>
+
<!-- Web bundles -->
<module>../../web/root</module>
<module>../../web/flows</module>
<module>../../samples/simpleforwarding</module>
<module>../../samples/loadbalancer</module>
<module>../../samples/northbound/loadbalancer</module>
+
+ <!-- Parents -->
<module>../../commons/concepts</module>
<module>../../commons/integrationtest</module>
<module>../../commons/checkstyle</module>
<module>../../commons/opendaylight</module>
+ <module>../../commons/parent</module>
</modules>
<build>
# entries, including switches' Certification Authority (CA) certificates. For example,
# secureChannelEnabled=true
# controllerKeyStore=./configuration/ctlKeyStore
-# controllerKeyStorePassword=xxxxx (this password should match the password used for KeyStore generation)
+# controllerKeyStorePassword=xxxxxxxx (this password should match the password used for KeyStore generation and at least 6 characters)
# controllerTrustStore=./configuration/ctlTrustStore
-# controllerTrustStorePassword=xxxxx (this password should match the password used for TrustStore generation)
+# controllerTrustStorePassword=xxxxxxxx (this password should match the password used for TrustStore generation and at least 6 characters)
secureChannelEnabled=false
controllerKeyStore=
</root>
<!-- Base log level -->
+ <logger name="org.opendaylight" level="INFO"/>
+
+ <!-- Controller log level -->
<logger name="org.opendaylight.controller" level="INFO"/>
<!-- OSGi logging bridge -->
<logger name="audit" level="INFO" additivity="false">
<appender-ref ref="audit-file"/>
</logger>
-</configuration>
\ No newline at end of file
+</configuration>
#!/bin/bash
-[[ -z ${JAVA_HOME} ]] && echo "Need to set JAVA_HOME environment variable" && exit -1;
-[[ ! -x ${JAVA_HOME}/bin/java ]] && echo "Cannot find an executable \
-JVM at path ${JAVA_HOME}/bin/java check your JAVA_HOME" && exit -1;
-
platform='unknown'
unamestr=`uname`
if [[ "$unamestr" == 'Linux' ]]; then
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
fullpath=$RESULT
+
+ [[ -z ${JAVA_HOME} ]] && [[ -x "/usr/libexec/java_home" ]] && export JAVA_HOME=`/usr/libexec/java_home -v 1.7`;
+
fi
+[[ -z ${JAVA_HOME} ]] && echo "Need to set JAVA_HOME environment variable" && exit -1;
+[[ ! -x ${JAVA_HOME}/bin/java ]] && echo "Cannot find an executable \
+JVM at path ${JAVA_HOME}/bin/java check your JAVA_HOME" && exit -1;
+
basedir=`dirname ${fullpath}`
function usage {
- echo "Usage: $0 [-debug] [-debugsuspend] [-debugport <num>] [-start [<console port>]] [-stop] [-status] [-console] [-help] [<other args will automatically be used for the JVM>]"
+ echo "Usage: $0 [-jmx] [-jmxport <num>] [-debug] [-debugsuspend] [-debugport <num>] [-start [<console port>]] [-stop] [-status] [-console] [-help] [<other args will automatically be used for the JVM>]"
exit 1
}
startdaemon=0
daemonport=2400
daemonportread=""
+jmxport=1088
+jmxportread=""
+startjmx=0
stopdaemon=0
statusdaemon=0
consolestart=1
while true ; do
case "$1" in
-debug) debug=1; shift ;;
+ -jmx) startjmx=1; shift ;;
-debugsuspend) debugsuspend=1; shift ;;
-debugport) shift; debugportread="$1"; if [[ "${debugportread}" =~ ^[0-9]+$ ]] ; then debugport=${debugportread}; shift; else echo "-debugport expects a number but was not found"; exit -1; fi;;
+ -jmxport) shift; jmxportread="$1"; if [[ "${jmxportread}" =~ ^[0-9]+$ ]] ; then jmxport=${jmxportread}; shift; else echo "-jmxport expects a number but was not found"; exit -1; fi;;
-start) startdaemon=1; shift; daemonportread="$1"; if [[ "${daemonportread}" =~ ^[0-9]+$ ]] ; then daemonport=${daemonportread}; shift; fi;;
-stop) stopdaemon=1; shift ;;
-status) statusdaemon=1; shift ;;
exit -1
fi
+# Validate jmx port
+if [[ "${jmxport}" -lt 1024 ]] || [[ "${jmxport}" -gt 65535 ]]; then
+ echo "JMX Port not in the range [1024,65535] value is ${jmxport}"
+ exit -1
+fi
+
# Debug options
if [ "${debugsuspend}" -eq 1 ]; then
extraJVMOpts="${extraJVMOpts} -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${debugport}"
extraJVMOpts="${extraJVMOpts} -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${debugport}"
fi
+# Add JMX support
+if [ "${startjmx}" -eq 1 ]; then
+ extraJVMOpts="${extraJVMOpts} -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=${jmxport} -Dcom.sun.management.jmxremote"
+fi
+
########################################
# Now add to classpath the OSGi JAR
########################################
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.configuration.IConfigurationContainerAware;
import org.opendaylight.controller.hosttracker.IfIptoHost;
*/
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(StaticRoutingImplementation.class)) {
- Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
- Set<String> propSet = new HashSet<String>();
- propSet.add("forwarding.staticrouting.configSaveEvent");
- props.put("cachenames", propSet);
- // export the service
-
- c.setInterface(new String[] { ICacheUpdateAware.class.getName(),
+ c.setInterface(new String[] {
IForwardingStaticRouting.class.getName(),
IfNewHostNotify.class.getName(),
- IConfigurationContainerAware.class.getName() }, props);
+ IConfigurationContainerAware.class.getName() }, null);
c.add(createContainerServiceDependency(containerName).setService(
IClusterContainerServices.class).setCallbacks(
import org.apache.felix.dm.Component;
import org.opendaylight.controller.clustering.services.CacheConfigException;
import org.opendaylight.controller.clustering.services.CacheExistException;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
import org.opendaylight.controller.configuration.IConfigurationContainerAware;
* Static Routing feature provides the bridge between SDN and Non-SDN networks.
*/
public class StaticRoutingImplementation implements IfNewHostNotify,
- IForwardingStaticRouting, IObjectReader, IConfigurationContainerAware,
- ICacheUpdateAware<Long, String> {
+ IForwardingStaticRouting, IObjectReader, IConfigurationContainerAware {
private static Logger log = LoggerFactory
.getLogger(StaticRoutingImplementation.class);
private static String ROOT = GlobalConstants.STARTUPHOME.toString();
private IfIptoHost hostTracker;
private Timer gatewayProbeTimer;
private String staticRoutesFileName = null;
- private Map<Long, String> configSaveEvent;
private IClusterContainerServices clusterContainerService = null;
private Set<IStaticRoutingAware> staticRoutingAware = Collections
.synchronizedSet(new HashSet<IStaticRoutingAware>());
}
}
+ @Override
public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
return staticRouteConfigs;
}
private Status saveConfig() {
- // Publish the save config event to the cluster nodes
- configSaveEvent.put(new Date().getTime(), SAVE);
return saveConfigInternal();
}
try {
clusterContainerService.createCache(
"forwarding.staticrouting.routes", EnumSet
- .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ .of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache(
"forwarding.staticrouting.configs", EnumSet
- .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
- clusterContainerService.createCache(
- "forwarding.staticrouting.configSaveEvent", EnumSet
- .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
-
+ .of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheExistException cee) {
log
.error("\nCache already exists - destroy and recreate if needed");
if (staticRouteConfigs == null) {
log.error("\nFailed to get rulesDB handle");
}
- configSaveEvent = (ConcurrentMap<Long, String>) clusterContainerService
- .getCache("forwarding.staticrouting.configSaveEvent");
- if (configSaveEvent == null) {
- log.error("\nFailed to get cache for configSaveEvent");
- }
- }
-
- @Override
- public void entryCreated(Long key, String cacheName, boolean local) {
- }
-
- @Override
- public void entryUpdated(Long key, String new_value, String cacheName,
- boolean originLocal) {
- saveConfigInternal();
- }
-
- @Override
- public void entryDeleted(Long key, String cacheName, boolean originLocal) {
}
private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
}
private void notifyHostUpdate(HostNodeConnector host, boolean added) {
- if (host == null)
+ if (host == null) {
return;
+ }
for (StaticRoute s : staticRoutes.values()) {
- if (s.getType() == StaticRoute.NextHopType.SWITCHPORT)
+ if (s.getType() == StaticRoute.NextHopType.SWITCHPORT) {
continue;
+ }
if (s.getNextHopAddress().equals(host.getNetworkAddress())) {
if (added) {
s.setHost(host);
}
public boolean isIPv4AddressValid(String cidr) {
- if (cidr == null)
+ if (cidr == null) {
return false;
+ }
String values[] = cidr.split("/");
Pattern ipv4Pattern = Pattern
return 0;
}
+ @Override
public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
ByteBuffer bblongestPrefix = null;
try {
return longestPrefixRoute;
}
+ @Override
public Status addStaticRoute(StaticRouteConfig config) {
- Status status;
-
- status = config.isValid();
+ Status status = config.isValid();
if (!status.isSuccess()) {
return status;
}
"A valid Static Route configuration with this name " +
"already exists. Please use a different name");
}
- for (StaticRouteConfig s : staticRouteConfigs.values()) {
- if (s.equals(config)) {
+
+ // Update database
+ StaticRoute sRoute = new StaticRoute(config);
+
+ for (Map.Entry<String, StaticRoute> entry : staticRoutes.entrySet()) {
+ if (entry.getValue().compareTo(sRoute) == 0) {
return new Status(StatusCode.CONFLICT,
- "This conflicts with an existing Static Route " +
- "Configuration. Please check the configuration " +
- "and try again");
+ "This conflicts with an existing Static Route " +
+ "Configuration. Please check the configuration " +
+ "and try again");
}
}
+ staticRoutes.put(config.getName(), sRoute);
+ // Update config databse
staticRouteConfigs.put(config.getName(), config);
- StaticRoute sRoute = new StaticRoute(config);
- staticRoutes.put(config.getName(), sRoute);
+
+ // Notify
checkAndUpdateListeners(sRoute, true);
return status;
}
+ @Override
public Status removeStaticRoute(String name) {
staticRouteConfigs.remove(name);
StaticRoute sRoute = staticRoutes.remove(name);
allocateCaches();
retrieveCaches();
this.executor = Executors.newFixedThreadPool(1);
- if (staticRouteConfigs.isEmpty())
+ if (staticRouteConfigs.isEmpty()) {
loadConfiguration();
+ }
/*
* Slow probe to identify any gateway that might have silently appeared
Assert.assertFalse(staticRoute1.equals(staticRoute3));
Assert.assertFalse(staticRoute1.equals(staticRoute4));
- Assert.assertTrue(staticRoute1.compareTo(staticRoute2) == 0 ? true : false);
- Assert.assertFalse(staticRoute1.compareTo(staticRoute3) == 0 ? true : false);
- Assert.assertTrue(staticRoute1.compareTo(staticRoute4) == 0 ? true : false);
+ Assert.assertTrue(staticRoute1.compareTo(staticRoute2) == 0);
+ Assert.assertFalse(staticRoute1.compareTo(staticRoute3) == 0);
+ Assert.assertTrue(staticRoute1.compareTo(staticRoute4) == 0);
}
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(FlowConfig.class);
private static final String NAMEREGEX = "^[a-zA-Z0-9]+$";
- private static final String STATICFLOWGROUP = "__StaticFlows__";
+ public static final String STATICFLOWGROUP = "__StaticFlows__";
public static final String INTERNALSTATICFLOWGROUP = "__InternalStaticFlows__";
public static final String INTERNALSTATICFLOWBEGIN = "__";
public static final String INTERNALSTATICFLOWEND = "__";
public FlowEntry mergeWith(ContainerFlow containerFlow) {
Match myMatch = flow.getMatch();
- // Based on this flow direction, rearrange the match
- Match match = containerFlow.getMatch();
+ Match filter = containerFlow.getMatch();
// Merge
- myMatch.mergeWithFilter(match);
+ Match merge = myMatch.mergeWithFilter(filter);
// Replace this Flow's match with merged version
- flow.setMatch(myMatch);
+ flow.setMatch(merge);
return this;
}
/**
* Returns the list of Flow entries across network nodes which are part of
- * the same flow group, policy
+ * the same flow group, policy. This list contains the flows as they were
+ * requested to be installed by the applications, before any merging with
+ * container flow is done.
*
* @param group
* the group name
- * @return the list of flow entries belonging to the specified group
+ * @return the original list of flow entries belonging to the specified group
*/
public List<FlowEntry> getFlowEntriesForGroup(String group);
+ /**
+ * Returns the list of Flow entries installed in network nodes which are part of
+ * the same flow group, policy. This list contains the effective flows installed
+ * on the nodes after the merging with any possible container flow was performed.
+ * If no container flow are specified, this method returns the same list returned
+ * by getFlowEntriesForGroup(String group).
+ *
+ * @param group
+ * the group name
+ * @return the list of container flow merged flow entries belonging to the specified group
+ */
+ public List<FlowEntry> getInstalledFlowEntriesForGroup(String policyName);
+
/**
* Add a list of output port to the flow with the specified name on the
* specified network node
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
public class Activator extends ComponentActivatorAbstractBase {
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(ForwardingRulesManager.class)) {
String interfaces[] = null;
- Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
- Set<String> propSet = new HashSet<String>();
- propSet.add("frm.flowsSaveEvent");
- props.put("cachenames", propSet);
// export the service
interfaces = new String[] { IContainerListener.class.getName(), ISwitchManagerAware.class.getName(),
IForwardingRulesManager.class.getName(), IInventoryListener.class.getName(),
- ICacheUpdateAware.class.getName(), IConfigurationContainerAware.class.getName(),
+ IConfigurationContainerAware.class.getName(),
IFlowProgrammerListener.class.getName() };
- c.setInterface(interfaces, props);
+ c.setInterface(interfaces, null);
c.add(createContainerServiceDependency(containerName).setService(IFlowProgrammerService.class)
.setCallbacks("setFlowProgrammerService", "unsetFlowProgrammerService").setRequired(true));
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Date;
import java.util.EnumSet;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.opendaylight.controller.clustering.services.CacheConfigException;
import org.opendaylight.controller.clustering.services.CacheExistException;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
import org.opendaylight.controller.configuration.IConfigurationContainerAware;
*/
public class ForwardingRulesManager implements IForwardingRulesManager, PortGroupChangeListener,
IContainerListener, ISwitchManagerAware, IConfigurationContainerAware, IInventoryListener, IObjectReader,
- ICacheUpdateAware<Long, String>, CommandProvider, IFlowProgrammerListener {
- private static final String SAVE = "Save";
+ CommandProvider, IFlowProgrammerListener {
private static final String NODEDOWN = "Node is Down";
private static final String SUCCESS = StatusCode.SUCCESS.toString();
private static final Logger log = LoggerFactory.getLogger(ForwardingRulesManager.class);
- private Map<Long, String> flowsSaveEvent;
+ private static final String PORTREMOVED = "Port removed";
private String frmFileName;
private String portGroupFileName;
private ConcurrentMap<Integer, FlowConfig> staticFlows;
portGroupConfigs = new ConcurrentHashMap<String, PortGroupConfig>();
portGroupData = new ConcurrentHashMap<PortGroupConfig, Map<Node, PortGroup>>();
staticFlows = new ConcurrentHashMap<Integer, FlowConfig>();
- flowsSaveEvent = new HashMap<Long, String>();
inactiveFlows = new ConcurrentHashMap<FlowEntry, FlowEntry>();
}
return list;
}
+ @Override
+ public List<FlowEntry> getInstalledFlowEntriesForGroup(String policyName) {
+ List<FlowEntry> list = new ArrayList<FlowEntry>();
+ if (policyName != null && !policyName.trim().isEmpty()) {
+ for (Map.Entry<FlowEntryInstall, FlowEntryInstall> entry : this.installedSwView.entrySet()) {
+ if (policyName.equals(entry.getKey().getGroupName())) {
+ list.add(entry.getKey().getInstall().clone());
+ }
+ }
+ }
+ return list;
+ }
+
@Override
public void addOutputPort(Node node, String flowName, List<NodeConnector> portList) {
try {
clusterContainerService.createCache("frm.originalSwView",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.installedSwView",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.inactiveFlows",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.nodeFlows",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.groupFlows",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.staticFlows",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.flowsSaveEvent",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.staticFlowsOrdinal",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.portGroupConfigs",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.portGroupData",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("frm.TSPolicies",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheConfigException cce) {
log.error("CacheConfigException");
log.error("Retrieval of frm.staticFlows cache failed for Container {}", container.getName());
}
- map = clusterContainerService.getCache("frm.flowsSaveEvent");
- if (map != null) {
- flowsSaveEvent = (ConcurrentMap<Long, String>) map;
- } else {
- log.error("Retrieval of frm.flowsSaveEvent cache failed for Container {}", container.getName());
- }
-
map = clusterContainerService.getCache("frm.staticFlowsOrdinal");
if (map != null) {
staticFlowsOrdinal = (ConcurrentMap<Integer, Integer>) map;
@Override
public Status saveConfig() {
- // Publish the save config event to the cluster nodes
- flowsSaveEvent.put(new Date().getTime(), SAVE);
return saveConfigInternal();
}
return new Status(StatusCode.SUCCESS, null);
}
- @Override
- public void entryCreated(Long key, String cacheName, boolean local) {
- }
-
- @Override
- public void entryUpdated(Long key, String new_value, String cacheName, boolean originLocal) {
- saveConfigInternal();
- }
-
- @Override
- public void entryDeleted(Long key, String cacheName, boolean originLocal) {
- }
-
@Override
public void subnetNotify(Subnet sub, boolean add) {
}
}
}
+ private boolean doesFlowContainNodeConnector(Flow flow, NodeConnector nc) {
+ if (nc == null) {
+ return false;
+ }
+
+ Match match = flow.getMatch();
+ if (match.isPresent(MatchType.IN_PORT)) {
+ NodeConnector matchPort = (NodeConnector) match.getField(MatchType.IN_PORT).getValue();
+ if (matchPort.equals(nc)) {
+ return true;
+ }
+ }
+ List<Action> actionsList = flow.getActions();
+ if (actionsList != null) {
+ for (Action action : actionsList) {
+ if (action instanceof Output) {
+ NodeConnector actionPort = ((Output) action).getPort();
+ if (actionPort.equals(nc)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public void notifyNode(Node node, UpdateType type, Map<String, Property> propMap) {
this.pendingEvents.offer(new NodeUpdateEvent(type, node));
}
@Override
- public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType t) {
+ public void nodeConnectorUpdated(String containerName, NodeConnector nc, UpdateType t) {
if (!container.getName().equals(containerName)) {
return;
}
+
+ boolean updateStaticFlowCluster = false;
+
+ switch (t) {
+ case REMOVED:
+
+ List<FlowEntryInstall> nodeFlowEntries = nodeFlows.get(nc.getNode());
+ if (nodeFlowEntries == null) {
+ return;
+ }
+ for (FlowEntryInstall fei : new ArrayList<FlowEntryInstall>(nodeFlowEntries)) {
+ if (doesFlowContainNodeConnector(fei.getInstall().getFlow(), nc)) {
+ Status status = this.removeEntryInternal(fei, true);
+ if (!status.isSuccess()) {
+ continue;
+ }
+ /*
+ * If the flow entry is a static flow, then update its
+ * configuration
+ */
+ if (fei.getGroupName().equals(FlowConfig.STATICFLOWGROUP)) {
+ FlowConfig flowConfig = getStaticFlow(fei.getFlowName(), fei.getNode());
+ if (flowConfig != null) {
+ flowConfig.setStatus(PORTREMOVED);
+ updateStaticFlowCluster = true;
+ }
+ }
+ }
+ }
+ if (updateStaticFlowCluster) {
+ refreshClusterStaticFlowsStatus(nc.getNode());
+ }
+ break;
+ case ADDED:
+ List<FlowConfig> flowConfigForNode = getStaticFlows(nc.getNode());
+ for (FlowConfig flowConfig : flowConfigForNode) {
+ if (doesFlowContainNodeConnector(flowConfig.getFlow(), nc)) {
+ if (flowConfig.installInHw()) {
+ Status status = this.installFlowEntry(flowConfig.getFlowEntry());
+ if (!status.isSuccess()) {
+ flowConfig.setStatus(status.getDescription());
+ } else {
+ flowConfig.setStatus(SUCCESS);
+ }
+ updateStaticFlowCluster = true;
+ }
+ }
+ }
+ if (updateStaticFlowCluster) {
+ refreshClusterStaticFlowsStatus(nc.getNode());
+ }
+ break;
+ case CHANGED:
+ break;
+ default:
+ }
}
@Override
private short vlan;
@XmlElement
private boolean staticHost;
- private transient short arpSendCountDown;
+ private short arpSendCountDown;
/**
* Private constructor used for JAXB mapping
import java.util.concurrent.Future;
import org.apache.felix.dm.Component;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
import org.opendaylight.controller.clustering.services.CacheConfigException;
import org.opendaylight.controller.clustering.services.CacheExistException;
import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.switchmanager.Subnet;
import org.opendaylight.controller.topologymanager.ITopologyManager;
import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAware, IInventoryListener,
- ITopologyManagerAware, ICacheUpdateAware<InetAddress, HostNodeConnector> {
- static final String ACTIVE_HOST_CACHE = "hostTrackerAH";
- static final String INACTIVE_HOST_CACHE = "hostTrackerIH";
+ ITopologyManagerAware, ICacheUpdateAware<InetAddress, HostNodeConnector>, CommandProvider {
+ 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);
private IHostFinder hostFinder;
private ConcurrentMap<InetAddress, HostNodeConnector> hostsDB;
hostTrackerCallable = callable;
}
}
-
// This list contains the hosts for which ARP requests are being sent
// periodically
- private final List<ARPPending> ARPPendingList = new ArrayList<HostTracker.ARPPending>();
+ ConcurrentMap<InetAddress, ARPPending> ARPPendingList;
/*
* This list below contains the hosts which were initially in ARPPendingList
* above, but ARP response didn't come from there hosts after multiple
*
* We can't recover from condition 3 above
*/
- private final List<ARPPending> failedARPReqList = new ArrayList<HostTracker.ARPPending>();
+ ConcurrentMap<InetAddress, ARPPending> failedARPReqList;
public HostTracker() {
}
private void startUp() {
+ nonClusterObjectCreate();
allocateCache();
retrieveCache();
logger.debug("Creating Cache for HostTracker");
try {
this.clusterContainerService.createCache(ACTIVE_HOST_CACHE,
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
this.clusterContainerService.createCache(INACTIVE_HOST_CACHE,
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheConfigException cce) {
logger.error("Cache couldn't be created for HostTracker - check cache mode");
} catch (CacheExistException cce) {
public void nonClusterObjectCreate() {
hostsDB = new ConcurrentHashMap<InetAddress, HostNodeConnector>();
inactiveStaticHosts = new ConcurrentHashMap<NodeConnector, HostNodeConnector>();
+ ARPPendingList = new ConcurrentHashMap<InetAddress, ARPPending>();
+ failedARPReqList = new ConcurrentHashMap<InetAddress, ARPPending>();
}
arphost.setHostIP(networkAddr);
arphost.setSent_count((short) 1);
- ARPPendingList.add(arphost);
+ ARPPendingList.put(networkAddr, arphost);
logger.debug("Host Added to ARPPending List, IP: {}", networkAddr);
}
- private void removePendingARPFromList(int index) {
- if (index >= ARPPendingList.size()) {
- logger.warn("removePendingARPFromList(): index greater than the List. Size:{}, Index:{}",
- ARPPendingList.size(), index);
- return;
- }
- ARPPending arphost = ARPPendingList.remove(index);
- HostTrackerCallable htCallable = arphost.getHostTrackerCallable();
- if (htCallable != null)
- htCallable.wakeup();
- }
-
public void setCallableOnPendingARP(InetAddress networkAddr, HostTrackerCallable callable) {
ARPPending arphost;
- for (int i = 0; i < ARPPendingList.size(); i++) {
- arphost = ARPPendingList.get(i);
+ for (Entry <InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
+ arphost = entry.getValue();
if (arphost.getHostIP().equals(networkAddr)) {
arphost.setHostTrackerCallable(callable);
}
private void processPendingARPReqs(InetAddress networkAddr) {
ARPPending arphost;
- for (int i = 0; i < ARPPendingList.size(); i++) {
- arphost = ARPPendingList.get(i);
- if (arphost.getHostIP().equals(networkAddr)) {
- /*
- * An ARP was sent for this host. The address is learned, remove
- * the request
- */
- removePendingARPFromList(i);
- logger.debug("Host Removed from ARPPending List, IP: {}", networkAddr);
- return;
- }
+ if ((arphost = ARPPendingList.remove(networkAddr)) != null) {
+ // Remove the arphost from ARPPendingList as it has been learned now
+ logger.debug("Host Removed from ARPPending List, IP: {}", networkAddr);
+ HostTrackerCallable htCallable = arphost.getHostTrackerCallable();
+ if (htCallable != null)
+ htCallable.wakeup();
+ return;
}
/*
* It could have been a host from the FailedARPReqList
*/
- for (int i = 0; i < failedARPReqList.size(); i++) {
- arphost = failedARPReqList.get(i);
- if (arphost.getHostIP().equals(networkAddr)) {
- /*
- * An ARP was sent for this host. The address is learned, remove
- * the request
- */
- failedARPReqList.remove(i);
- logger.debug("Host Removed from FailedARPReqList List, IP: {}", networkAddr);
- return;
- }
+ if (failedARPReqList.containsKey(networkAddr)) {
+ failedARPReqList.remove(networkAddr);
+ logger.debug("Host Removed from FailedARPReqList List, IP: {}", networkAddr);
}
}
}
private void replaceHost(InetAddress networkAddr, HostNodeConnector removedHost, HostNodeConnector newHost) {
+ // Ignore ARP messages from internal nodes
+ NodeConnector newHostNc = newHost.getnodeConnector();
+ boolean newHostIsInternal = topologyManager.isInternal(newHostNc);
+ if (newHostIsInternal) {
+ return;
+ }
+
newHost.initArpSendCountDown();
+
if (hostsDB.replace(networkAddr, removedHost, newHost)) {
logger.debug("Host move occurred: Old Host IP:{}, New Host IP: {}", removedHost.getNetworkAddress()
.getHostAddress(), newHost.getNetworkAddress().getHostAddress());
public void subnetNotify(Subnet sub, boolean add) {
logger.debug("Received subnet notification: {} add={}", sub, add);
if (add) {
- for (int i = 0; i < failedARPReqList.size(); i++) {
+ for (Entry <InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
ARPPending arphost;
- arphost = failedARPReqList.get(i);
+ arphost = entry.getValue();
if (hostFinder == null) {
logger.warn("ARPHandler Services are not available on subnet addition");
continue;
@Override
public void run() {
ARPPending arphost;
+
/* This routine runs every 4 seconds */
- for (int i = 0; i < ARPPendingList.size(); i++) {
- arphost = ARPPendingList.get(i);
+ logger.trace("Number of Entries in ARP Pending/Failed Lists: ARPPendingList = {}, failedARPReqList = {}",
+ ARPPendingList.size(), failedARPReqList.size());
+ for (Entry <InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
+ arphost = entry.getValue();
+
+ if (hostsDB.containsKey(arphost.getHostIP())) {
+ // this host is already learned, shouldn't be in ARPPendingList
+ // Remove it and continue
+ logger.warn("Learned Host {} found in ARPPendingList", arphost.getHostIP());
+ ARPPendingList.remove(entry.getKey());
+ continue;
+ }
if (arphost.getSent_count() < switchManager.getHostRetryCount()) {
/*
* No reply has been received of first ARP Req, send the
* ARP requests have been sent without receiving a
* reply, remove this from the pending list
*/
- removePendingARPFromList(i);
+ ARPPendingList.remove(entry.getKey());
logger.debug("ARP reply not received after multiple attempts, removing from Pending List IP: {}",
arphost.getHostIP().getHostAddress());
/*
* on link up events
*/
logger.debug("Adding the host to FailedARPReqList IP: {}", arphost.getHostIP().getHostAddress());
- failedARPReqList.add(arphost);
+ failedARPReqList.put(entry.getKey(), arphost);
} else {
- logger.error("Inavlid arp_sent count for entry at index: {}", i);
+ logger.error("Inavlid arp_sent count for entry: {}", entry);
}
}
}
*/
if (switchManager.isNodeConnectorEnabled(nc)) {
learnNewHost(host);
+ processPendingARPReqs(networkAddr);
notifyHostLearnedOrRemoved(host, true);
} else {
inactiveStaticHosts.put(nc, host);
private void handleNodeConnectorStatusUp(NodeConnector nodeConnector) {
ARPPending arphost;
+ HostNodeConnector host = null;
logger.debug("handleNodeConnectorStatusUp {}", nodeConnector);
- for (int i = 0; i < failedARPReqList.size(); i++) {
- arphost = failedARPReqList.get(i);
+ for (Entry <InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
+ arphost = entry.getValue();
logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostIP().getHostAddress());
if (hostFinder == null) {
logger.warn("ARPHandler is not available at interface up");
nodeConnector);
continue;
}
- hostFinder.find(arphost.getHostIP());
+
+ // Send a broadcast ARP only on the interface which just came up.
+ // Use hostFinder's "probe" method
+ try {
+ byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr();
+ host = new HostNodeConnector(dataLayerAddress, arphost.getHostIP(), nodeConnector, (short) 0);
+ hostFinder.probe(host);
+ } catch (ConstructionException e) {
+ logger.debug("HostNodeConnector couldn't be created for Host: {}, NodeConnector: {}",
+ arphost.getHostIP(), nodeConnector);
+ logger.error("", e);
+ }
+ logger.debug("Done. handleNodeConnectorStatusUp {}", nodeConnector);
}
- HostNodeConnector host = inactiveStaticHosts.get(nodeConnector);
+
+ host = inactiveStaticHosts.get(nodeConnector);
if (host != null) {
inactiveStaticHosts.remove(nodeConnector);
learnNewHost(host);
+ processPendingARPReqs(host.getNetworkAddress());
notifyHostLearnedOrRemoved(host, true);
}
}
*
*/
void start() {
+ registerWithOSGIConsole();
}
/**
boolean originLocal) {
}
+ private void registerWithOSGIConsole() {
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this, null);
+ }
+
+ @Override
+ public String getHelp() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void _dumpPendingARPReqList(CommandInterpreter ci) {
+ ARPPending arphost;
+ for (Entry <InetAddress, ARPPending> entry : ARPPendingList.entrySet()) {
+ arphost = entry.getValue();
+ ci.println(arphost.getHostIP().toString());
+ }
+ }
+
+ public void _dumpFailedARPReqList(CommandInterpreter ci) {
+ ARPPending arphost;
+ for (Entry <InetAddress, ARPPending> entry : failedARPReqList.entrySet()) {
+ arphost = entry.getValue();
+ ci.println(arphost.getHostIP().toString());
+ }
+ }
}
package org.opendaylight.controller.northbound.commons;
public enum RestMessages {
- SUCCESS("Success"), NOCONTAINER("Container does not exist"), NOFLOWSPEC(
- "Flow Spec does not exist"), NOSUBNET("Subnet does not exist"), NOSTATICROUTE(
- "Static Route does not exist"), NOHOST("Host does not exist"), NOFLOW(
- "Flow does not exist"), NONODE("Node does not exist"), NOPOLICY(
- "Policy does not exist"), NORESOURCE("Resource does not exist"), RESOURCECONFLICT(
- "Operation failed due to Resource Conflict"), NODEFAULT(
- "Container default is not a custom container"), DEFAULTDISABLED(
+ SUCCESS("Success"), NOCONTAINER("Container does not exist"), NOSUBNET("Subnet does not exist"), NOSTATICROUTE(
+ "Static Route does not exist"), NOHOST("Host does not exist"), NOFLOW("Flow does not exist"), NONODE(
+ "Node does not exist"), NOPOLICY("Policy does not exist"), NORESOURCE("Resource does not exist"), RESOURCECONFLICT(
+ "Operation failed due to Resource Conflict"), NODEFAULT("Container default is not a custom container"), DEFAULTDISABLED(
"Container(s) are configured. Container default is not operational"), NOTALLOWEDONDEFAULT(
- "Container default is a static resource, no modification allowed on it"), UNKNOWNACTION(
- "Unknown action"), INVALIDJSON("JSON message is invalid"), INVALIDADDRESS(
- "invalid InetAddress"), AVAILABLESOON(
+ "Container default is a static resource, no modification allowed on it"), UNKNOWNACTION("Unknown action"), INVALIDJSON(
+ "JSON message is invalid"), INVALIDADDRESS("invalid InetAddress"), AVAILABLESOON(
"Resource is not implemented yet"), INTERNALERROR("Internal Error"), SERVICEUNAVAILABLE(
"Service is not available. Could be down for maintanence"), INVALIDDATA(
"Data is invalid or conflicts with URI");
message = msg;
}
+ @Override
public String toString() {
return message;
}
--- /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.northbound.commons.exception;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Status Code 400 (Bad Request)
+ *
+ * The request could not be understood by the server due to malformed syntax.
+ * The client SHOULD NOT repeat the request without modifications.
+ */
+public class BadRequestException extends WebApplicationException {
+ private static final long serialVersionUID = 1L;
+
+ public BadRequestException(String string) {
+ super(Response.status(Response.Status.BAD_REQUEST).entity(string).type(MediaType.TEXT_PLAIN).build());
+ }
+}
--- /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.northbound.commons.exception;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * Implementation of StatusType for error 501 as in:
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6
+ */
+public class NotImplemented implements Response.StatusType {
+ @Override
+ public int getStatusCode() {
+ return 501;
+ }
+
+ @Override
+ public String getReasonPhrase() {
+ return "Not Implemented";
+ }
+
+ @Override
+ public Response.Status.Family getFamily() {
+ return Response.Status.Family.SERVER_ERROR;
+ }
+}
\ No newline at end of file
--- /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.northbound.commons.exception;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Status Code 501 (Not Implemented)
+ *
+ * The server does not support the functionality required to fulfill the
+ * request. This is the appropriate response when the server does not recognize
+ * the request method and is not capable of supporting it for any resource.
+ */
+public class NotImplementedException extends WebApplicationException {
+ private static final long serialVersionUID = 1L;
+
+ public NotImplementedException(String string) {
+ super(Response.status(new NotImplemented()).entity(string).type(MediaType.TEXT_PLAIN).build());
+ }
+}
import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
-@XmlRootElement
+@XmlRootElement (name = "list")
@XmlAccessorType(XmlAccessType.NONE)
public class FlowConfigs {
import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
import org.opendaylight.controller.northbound.commons.RestMessages;
import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
+import org.opendaylight.controller.northbound.commons.exception.MethodNotAllowedException;
import org.opendaylight.controller.northbound.commons.exception.NotAcceptableException;
import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
* Returns a list of Flows configured on the given container
*
* @param containerName
- * Name of the Container. The Container name for the base
- * controller is "default".
- * @return List of configured flows configured on a given container
+ * Name of the Container (Eg. 'default')
+ * @return List of flows configured on a given container
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/flow/default
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <list>
+ *    <flowConfig>
+ *       <installInHw>true</installInHw>
+ *       <name>flow1</name>
+ *       <node id="00:00:00:00:00:00:00:01" type="OF"/>
+ *       <ingressPort>1</ingressPort>
+ *       <priority>500</priority>
+ *       <etherType>0x800</etherType>
+ *       <nwSrc>9.9.1.1</nwSrc>
+ *       <actions>OUTPUT=2</actions>
+ *    </flowConfig>
+ * </list>
+ *
+ * Response in JSON:
+ * {"flowConfig":{"installInHw":"true","name":"flow1","node":{"@id":"00:00:00:00:00:00:00:01","@type":"OF"},
+ * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}}
+ *
+ * </pre>
*/
@Path("/{containerName}")
@GET
@TypeHint(FlowConfigs.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The containerName is not found"),
@ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
public FlowConfigs getStaticFlows(
* Returns a list of Flows configured on a Node in a given container
*
* @param containerName
- * Name of the Container. The Container name for the base
- * controller is "default".
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
- * Node Identifier
- * @return List of configured flows configured on a Node in a container
+ * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
+ * @return List of flows configured on a Node in a container
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <list>
+ *    <flowConfig>
+ *       <installInHw>true</installInHw>
+ *       <name>flow1</name>
+ *       <node id="00:00:00:00:00:00:00:01" type="OF"/>
+ *       <ingressPort>1</ingressPort>
+ *       <priority>500</priority>
+ *       <etherType>0x800</etherType>
+ *       <nwSrc>9.9.1.1</nwSrc>
+ *       <actions>OUTPUT=2</actions>
+ *    </flowConfig>
+ * </list>
+ *
+ * Response in JSON:
+ * {"flowConfig":{"installInHw":"true","name":"flow1","node":{"@id":"00:00:00:00:00:00:00:01","@type":"OF"},
+ * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}}
+ *
+ * </pre>
*/
- @Path("/{containerName}/{nodeType}/{nodeId}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(FlowConfigs.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The containerName or nodeId is not found"),
@ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
public FlowConfigs getStaticFlows(
* on a given Container.
*
* @param containerName
- * Name of the Container. The Container name for the base
- * controller is "default".
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
- * Node Identifier
+ * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
* @param name
- * Human-readable name for the configured flow.
+ * Human-readable name for the configured flow (Eg. 'Flow1')
* @return Flow configuration matching the name and nodeId on a Container
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <flowConfig>
+ *    <installInHw>true</installInHw>
+ *    <name>flow1</name>
+ *    <node id="00:00:00:00:00:00:00:01" type="OF"/>
+ *    <ingressPort>1</ingressPort>
+ *    <priority>500</priority>
+ *    <etherType>0x800</etherType>
+ *    <nwSrc>9.9.1.1</nwSrc>
+ *    <actions>OUTPUT=2</actions>
+ * </flowConfig>
+ *
+ * Response in JSON:
+ * {"installInHw":"true","name":"flow1","node":{"@id":"00:00:00:00:00:00:00:01","@type":"OF"},
+ * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}
+ *
+ * </pre>
*/
- @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(FlowConfig.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The containerName or NodeId or Configuration name is not found"),
@ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
public FlowConfig getStaticFlow(
* Add a flow configuration
*
* @param containerName
- * Name of the Container. The Container name for the base
- * controller is "default".
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
- * Node Identifier
+ * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
* @param name
- * Name of the Static Flow configuration
+ * Name of the Static Flow configuration (Eg. 'Flow2')
* @param FlowConfig
* Flow Configuration in JSON or XML format
* @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ *
+ * Request in XML:
+ * <flowConfig>
+ *    <installInHw>true</installInHw>
+ *    <name>flow1</name>
+ *    <node id="00:00:00:00:00:00:00:01" type="OF"/>
+ *    <ingressPort>1</ingressPort>
+ *    <priority>500</priority>
+ *    <etherType>0x800</etherType>
+ *    <nwSrc>9.9.1.1</nwSrc>
+ *    <actions>OUTPUT=2</actions>
+ * </flowConfig>
+ *
+ * Request in JSON:
+ * {"installInHw":"true","name":"flow1","node":{"@id":"00:00:00:00:00:00:00:01","@type":"OF"},
+ * "ingressPort":"1","priority":"500","etherType":"0x800","nwSrc":"9.9.1.1","actions":"OUTPUT=2"}
+ *
+ * </pre>
*/
- @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
- @POST
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+ @PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 201, condition = "Flow Config processed successfully"),
- @ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
+ @ResponseCode(code = 400, condition = "Failed to create Static Flow entry due to invalid flow configuration"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+ @ResponseCode(code = 404, condition = "The Container Name or nodeId is not found"),
@ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
- @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name"),
+ @ResponseCode(code = 409, condition = "Failed to create Static Flow entry due to Conflicting Name or configuration"),
@ResponseCode(code = 500, condition = "Failed to create Static Flow entry. Failure Reason included in HTTP Error response"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
public Response addFlow(
"User is not authorized to perform this operation on container "
+ containerName);
}
+ handleResourceCongruence(name, flowConfig.getValue().getName());
+ handleResourceCongruence(nodeId, flowConfig.getValue().getNode().getNodeIDString());
handleDefaultDisabled(containerName);
IForwardingRulesManager frm = getForwardingRulesManagerService(containerName);
if (status.isSuccess()) {
NorthboundUtils.auditlog("Flow", username, "added", name, containerName);
- return Response.status(Response.Status.CREATED).build();
+ return Response.status(Response.Status.CREATED).entity("Success").build();
}
- throw new InternalServerErrorException(status.getDescription());
+ return NorthboundUtils.getResponse(status);
}
/**
* Delete a Flow configuration
*
- * DELETE /flows/{containerName}/{nodeType}/{nodeId}/{name}
- *
* @param containerName
- * Name of the Container. The Container name for the base
- * controller is "default".
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
- * Node Identifier
+ * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
* @param name
- * Name of the Static Flow configuration
+ * Name of the Static Flow configuration (Eg. 'Flow1')
* @return Response as dictated by the HTTP Response code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ *
+ * </pre>
*/
- @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
- @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
+ @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
@ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
@ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
public Response deleteFlow(
Status status = frm.removeStaticFlow(name, node);
if (status.isSuccess()) {
NorthboundUtils.auditlog("Flow", username, "removed", name, containerName);
- return Response.ok().build();
}
- throw new InternalServerErrorException(status.getDescription());
+ return NorthboundUtils.getResponse(status);
}
/**
* Toggle a Flow configuration
*
* @param containerName
- * Name of the Container. The Container name for the base
- * controller is "default".
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
- * Node Identifier
+ * Node Identifier (Eg. '00:00:00:00:00:00:00:01')
* @param name
- * Name of the Static Flow configuration
+ * Name of the Static Flow configuration (Eg. 'Flow1')
* @return Response as dictated by the HTTP Response code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/flow/default/node/OF/00:00:00:00:00:00:00:01/static-flow/flow1
+ *
+ * </pre>
*/
-
- @Path("/{containerName}/{nodeType}/{nodeId}/{name}")
- @PUT
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/static-flow/{name}")
+ @POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
- @ResponseCode(code = 200, condition = "Flow Config deleted successfully"),
+ @ResponseCode(code = 200, condition = "Flow Config processed successfully"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or Node-id or Flow Name passed is not found"),
- @ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
+ @ResponseCode(code = 406, condition = "Failed to delete Flow config due to invalid operation. Failure details included in HTTP Error response"),
@ResponseCode(code = 500, condition = "Failed to delete Flow config. Failure Reason included in HTTP Error response"),
@ResponseCode(code = 503, condition = "One or more of Controller service is unavailable") })
public Response toggleFlow(
Status status = frm.toggleStaticFlowStatus(staticFlow);
if (status.isSuccess()) {
NorthboundUtils.auditlog("Flow", username, "toggled", name, containerName);
- return Response.ok().build();
}
- throw new InternalServerErrorException(status.getDescription());
+ return NorthboundUtils.getResponse(status);
}
private Node handleNodeAvailability(String containerName, String nodeType,
}
}
+ private void handleResourceCongruence(String resource, String configured) {
+ if (!resource.equals(configured)) {
+ throw new MethodNotAllowedException("Path's resource name conflicts with payload's resource name");
+ }
+ }
+
}
if (timestamp == null || timestampName == null) {
Assert.assertFalse(properties.has("timeStamp"));
} else {
- Assert.assertEquals(timestamp, (Integer) properties.getJSONObject("timeStamp").getInt("timestamp"));
- Assert.assertEquals(timestampName, properties.getJSONObject("timeStamp").getString("timestampName"));
+ Assert.assertEquals(timestamp, (Integer) properties.getJSONObject("timeStamp").getInt("value"));
+ Assert.assertEquals(timestampName, properties.getJSONObject("timeStamp").getString("name"));
}
if (actionsValue == null) {
Assert.assertFalse(properties.has("actions"));
} else {
- Assert.assertEquals(actionsValue, (Integer) properties.getJSONObject("actions").getInt("actionsValue"));
+ Assert.assertEquals(actionsValue, (Integer) properties.getJSONObject("actions").getInt("value"));
}
if (capabilitiesValue == null) {
Assert.assertFalse(properties.has("capabilities"));
} else {
Assert.assertEquals(capabilitiesValue,
- (Integer) properties.getJSONObject("capabilities").getInt("capabilitiesValue"));
+ (Integer) properties.getJSONObject("capabilities").getInt("value"));
}
if (tablesValue == null) {
Assert.assertFalse(properties.has("tables"));
} else {
- Assert.assertEquals(tablesValue, (Integer) properties.getJSONObject("tables").getInt("tablesValue"));
+ Assert.assertEquals(tablesValue, (Integer) properties.getJSONObject("tables").getInt("value"));
}
if (buffersValue == null) {
Assert.assertFalse(properties.has("buffers"));
} else {
- Assert.assertEquals(buffersValue, (Integer) properties.getJSONObject("buffers").getInt("buffersValue"));
+ Assert.assertEquals(buffersValue, (Integer) properties.getJSONObject("buffers").getInt("value"));
}
}
if (state == null) {
Assert.assertFalse(properties.has("state"));
} else {
- Assert.assertEquals(state, (Integer) properties.getJSONObject("state").getInt("stateValue"));
+ Assert.assertEquals(state, (Integer) properties.getJSONObject("state").getInt("value"));
}
if (capabilities == null) {
Assert.assertFalse(properties.has("capabilities"));
} else {
Assert.assertEquals(capabilities,
- (Integer) properties.getJSONObject("capabilities").getInt("capabilitiesValue"));
+ (Integer) properties.getJSONObject("capabilities").getInt("value"));
}
if (bandwidth == null) {
Assert.assertFalse(properties.has("bandwidth"));
} else {
- Assert.assertEquals(bandwidth, (Integer) properties.getJSONObject("bandwidth").getInt("bandwidthValue"));
+ Assert.assertEquals(bandwidth, (Integer) properties.getJSONObject("bandwidth").getInt("value"));
}
}
// Test insert static route
String requestBody = "{\"name\":\"" + name1 + "\", \"prefix\":\"" + prefix1 + "\", \"nextHop\":\"" + nextHop1
+ "\"}";
- result = getJsonResult(baseURL + "default/" + name1, "POST", requestBody);
+ result = getJsonResult(baseURL + "default/route/" + name1, "POST", requestBody);
Assert.assertEquals(201, httpResponseCode.intValue());
requestBody = "{\"name\":\"" + name2 + "\", \"prefix\":\"" + prefix2 + "\", \"nextHop\":\"" + nextHop2 + "\"}";
- result = getJsonResult(baseURL + "default/" + name2, "POST", requestBody);
+ result = getJsonResult(baseURL + "default/route/" + name2, "POST", requestBody);
Assert.assertEquals(201, httpResponseCode.intValue());
// Test Get all static routes
}
// Test get specific static route
- result = getJsonResult(baseURL + "default/" + name1);
+ result = getJsonResult(baseURL + "default/route/" + name1);
jt = new JSONTokener(result);
json = new JSONObject(jt);
Assert.assertEquals(prefix1, json.getString("prefix"));
Assert.assertEquals(nextHop1, json.getString("nextHop"));
- result = getJsonResult(baseURL + "default/" + name2);
+ result = getJsonResult(baseURL + "default/route/" + name2);
jt = new JSONTokener(result);
json = new JSONObject(jt);
Assert.assertEquals(nextHop2, json.getString("nextHop"));
// Test delete static route
- result = getJsonResult(baseURL + "default/" + name1, "DELETE");
+ result = getJsonResult(baseURL + "default/route/" + name1, "DELETE");
Assert.assertEquals(200, httpResponseCode.intValue());
result = getJsonResult(baseURL + "default");
// Test add property to node
// Add Tier and Description property to node1
result = getJsonResult(baseURL + "node/STUB/" + nodeId_1 + "/property/tier/1001", "PUT");
- Assert.assertEquals(200, httpResponseCode.intValue());
+ Assert.assertEquals(201, httpResponseCode.intValue());
result = getJsonResult(baseURL + "node/STUB/" + nodeId_1 + "/property/description/node1", "PUT");
- Assert.assertEquals(200, httpResponseCode.intValue());
+ Assert.assertEquals(201, httpResponseCode.intValue());
// Test for first node
result = getJsonResult(baseURL + "nodes");
json = new JSONObject(jt);
node = getJsonInstance(json, "nodeProperties", nodeId_1);
Assert.assertNotNull(node);
- Assert.assertEquals(1001, node.getJSONObject("properties").getJSONObject("tier").getInt("tierValue"));
- Assert.assertEquals("node1", node.getJSONObject("properties").getJSONObject("description").getString("descriptionValue"));
+ Assert.assertEquals(1001, node.getJSONObject("properties").getJSONObject("tier").getInt("value"));
+ Assert.assertEquals("node1", node.getJSONObject("properties").getJSONObject("description").getString("value"));
// Test delete nodeConnector property
// Delete state property of nodeconnector1
String baseURL = "http://127.0.0.1:8080/controller/nb/v2/statistics/default/";
- String result = getJsonResult(baseURL + "flowstats");
+ String result = getJsonResult(baseURL + "flow");
JSONTokener jt = new JSONTokener(result);
JSONObject json = new JSONObject(jt);
JSONObject flowStatistics = getJsonInstance(json, "flowStatistics", 0xCAFE);
Assert.assertEquals(node.getString("@type"), "STUB");
// test that flow statistics results are correct
- JSONArray flowStats = flowStatistics.getJSONArray("flowStat");
+ JSONArray flowStats = flowStatistics.getJSONArray("flowStatistic");
for (int i = 0; i < flowStats.length(); i++) {
JSONObject flowStat = flowStats.getJSONObject(i);
}
- // for /controller/nb/v2/statistics/default/portstats
- result = getJsonResult(baseURL + "portstats");
+ // for /controller/nb/v2/statistics/default/port
+ result = getJsonResult(baseURL + "port");
jt = new JSONTokener(result);
json = new JSONObject(jt);
JSONObject portStatistics = getJsonInstance(json, "portStatistics", 0xCAFE);
Assert.assertEquals(node2.getString("@type"), "STUB");
// test that port statistic results are correct
- JSONObject portStat = portStatistics.getJSONObject("portStat");
+ JSONObject portStat = portStatistics.getJSONObject("portStatistic");
Assert.assertTrue(portStat.getInt("receivePackets") == 250);
Assert.assertTrue(portStat.getInt("transmitPackets") == 500);
Assert.assertTrue(portStat.getInt("receiveBytes") == 1000);
Assert.assertTrue(portStat.getInt("collisionCount") == 4);
// test for getting one specific node's stats
- result = getJsonResult(baseURL + "flowstats/STUB/51966");
+ result = getJsonResult(baseURL + "flow/node/STUB/51966");
jt = new JSONTokener(result);
json = new JSONObject(jt);
node = json.getJSONObject("node");
Assert.assertEquals(node.getString("@type"), "STUB");
// test that flow statistics results are correct
- flowStats = json.getJSONArray("flowStat");
+ flowStats = json.getJSONArray("flowStatistic");
for (int i = 0; i < flowStats.length(); i++) {
JSONObject flowStat = flowStats.getJSONObject(i);
testFlowStat(flowStat, actionTypes[i], i);
}
- result = getJsonResult(baseURL + "portstats/STUB/51966");
+ result = getJsonResult(baseURL + "port/node/STUB/51966");
jt = new JSONTokener(result);
json = new JSONObject(jt);
node2 = json.getJSONObject("node");
Assert.assertEquals(node2.getString("@type"), "STUB");
// test that port statistic results are correct
- portStat = json.getJSONObject("portStat");
+ portStat = json.getJSONObject("portStatistic");
Assert.assertTrue(portStat.getInt("receivePackets") == 250);
Assert.assertTrue(portStat.getInt("transmitPackets") == 500);
Assert.assertTrue(portStat.getInt("receiveBytes") == 1000);
String baseURL = "http://127.0.0.1:8080/controller/nb/v2/flow/default/";
// Attempt to get a flow that doesn't exit. Should return 404
// status.
- String result = getJsonResult(baseURL + "STUB/51966/test1", "GET");
+ String result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "GET");
Assert.assertTrue(result.equals("404"));
// test add flow1
String fc = "{\"dynamic\":\"false\", \"name\":\"test1\", \"node\":{\"@id\":\"51966\",\"@type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
- result = getJsonResult(baseURL + "STUB/51966/test1", "POST", fc);
+ result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "PUT", fc);
Assert.assertTrue(httpResponseCode == 201);
// test get returns flow that was added.
- result = getJsonResult(baseURL + "STUB/51966/test1", "GET");
+ result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "GET");
// check that result came out fine.
Assert.assertTrue(httpResponseCode == 200);
JSONTokener jt = new JSONTokener(result);
Assert.assertEquals(node.getString("@id"), "51966");
// test adding same flow again fails due to repeat name..return 409
// code
- result = getJsonResult(baseURL + "STUB/51966/test1", "POST", fc);
+ result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test1", "PUT", fc);
Assert.assertTrue(result.equals("409"));
fc = "{\"dynamic\":\"false\", \"name\":\"test2\", \"node\":{\"@id\":\"51966\",\"@type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
- result = getJsonResult(baseURL + "STUB/51966/test2", "POST", fc);
- // test should return 500 for error due to same flow being added.
- Assert.assertTrue(result.equals("500"));
+ result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "PUT", fc);
+ // test should return 409 for error due to same flow being added.
+ Assert.assertTrue(result.equals("409"));
// add second flow that's different
fc = "{\"dynamic\":\"false\", \"name\":\"test2\", \"nwSrc\":\"1.1.1.1\", \"node\":{\"@id\":\"51966\",\"@type\":\"STUB\"}, \"actions\":[\"DROP\"]}";
- result = getJsonResult(baseURL + "STUB/51966/test2", "POST", fc);
+ result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "PUT", fc);
Assert.assertTrue(httpResponseCode == 201);
// check that request returns both flows given node.
- result = getJsonResult(baseURL + "STUB/51966/", "GET");
+ result = getJsonResult(baseURL + "node/STUB/51966/", "GET");
jt = new JSONTokener(result);
json = new JSONObject(jt);
Assert.assertTrue(json.get("flowConfig") instanceof JSONArray);
Assert.assertTrue(count == 2);
// delete a flow, check that it's no longer in list.
- result = getJsonResult(baseURL + "STUB/51966/test2", "DELETE");
+ result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "DELETE");
Assert.assertTrue(httpResponseCode == 200);
- result = getJsonResult(baseURL + "STUB/51966/test2", "GET");
+ result = getJsonResult(baseURL + "node/STUB/51966/static-flow/test2", "GET");
Assert.assertTrue(result.equals("404"));
}
Assert.assertEquals(tailNode.getString("@type"),nodeType);
Assert.assertEquals(tailNode.getString("@type"), nodeConnType);
Assert.assertEquals(tailNC.getLong("@id"), tailNC1_nodeConnId);
- Assert.assertEquals(bandw.getLong("bandwidthValue"), bw_1);
- Assert.assertTrue((short) stt.getInt("stateValue") == state_1);
- Assert.assertEquals(ltc.getLong("latencyValue"), lat_1);
+ Assert.assertEquals(bandw.getLong("value"), bw_1);
+ Assert.assertTrue((short) stt.getInt("value") == state_1);
+ Assert.assertEquals(ltc.getLong("value"), lat_1);
} else if (headNC.getInt("@id") == headNC2_nodeConnId) {
Assert.assertEquals(headNode.getString("@type"),nodeType);
Assert.assertEquals(headNode.getLong("@id"), headNC2_nodeId);
Assert.assertTrue(tailNode.getInt("@id") == tailNC2_nodeId);
Assert.assertEquals(tailNC.getString("@type"), nodeConnType);
Assert.assertEquals(tailNC.getLong("@id"), tailNC2_nodeConnId);
- Assert.assertEquals(bandw.getLong("bandwidthValue"), bw_2);
- Assert.assertTrue((short) stt.getInt("stateValue") == state_2);
- Assert.assertEquals(ltc.getLong("latencyValue"), lat_2);
+ Assert.assertEquals(bandw.getLong("value"), bw_2);
+ Assert.assertTrue((short) stt.getInt("value") == state_2);
+ Assert.assertEquals(ltc.getLong("value"), lat_2);
}
}
.append("dstNodeConnector",
nodeConnectorType_2 + "|" + nodeConnectorId_2 + "@" + nodeType_2 + "|" + nodeId_2);
// execute HTTP request and verify response code
- result = getJsonResult(baseURL + "/userLink", "POST", jo.toString());
+ result = getJsonResult(baseURL + "/user-link", "POST", jo.toString());
Assert.assertTrue(httpResponseCode == 201);
// === test GET method for getUserLinks()
- result = getJsonResult(baseURL + "/userLink", "GET");
+ result = getJsonResult(baseURL + "/user-link", "GET");
Assert.assertTrue(httpResponseCode == 200);
if (debugMsg) {
System.out.println("result:" + result);
// === test DELETE method for deleteUserLink()
String userName = "userLink_1";
- result = getJsonResult(baseURL + "/userLink/" + userName, "DELETE");
+ result = getJsonResult(baseURL + "/user-link/" + userName, "DELETE");
Assert.assertTrue(httpResponseCode == 200);
// execute another getUserLinks() request to verify that userLink_1 is
// removed
- result = getJsonResult(baseURL + "/userLink", "GET");
+ result = getJsonResult(baseURL + "/user-link", "GET");
Assert.assertTrue(httpResponseCode == 200);
if (debugMsg) {
System.out.println("result:" + result);
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
-@XmlRootElement
-@XmlAccessorType(XmlAccessType.NONE)
/**
- * This class contains static route data that is returned as a response to the NorthBound GET request.
- *
- *
+ * StaticRoute represents the static route data that is returned as a response to
+ * the NorthBound GET request.
*
*/
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
public class StaticRoute {
+ /**
+ * The name of the static route.
+ */
@XmlElement
private String name;
+
+ /**
+ * The prefix for the route.
+ * Format: A.B.C.D/MM Where A.B.C.D is the Default Gateway IP (L3) or ARP Querier IP (L2)
+ */
@XmlElement
- private String prefix; // A.B.C.D/MM Where A.B.C.D is the Default Gateway IP (L3) or ARP Querier IP (L2)
+ private String prefix;
+
+ /**
+ * NextHop IP-Address (or) datapath ID/port list: xx:xx:xx:xx:xx:xx:xx:xx/a,b,c-m,r-t,y
+ */
@XmlElement
- private String nextHop; // NextHop IP-Address (or) datapath ID/port list: xx:xx:xx:xx:xx:xx:xx:xx/a,b,c-m,r-t,y
+ private String nextHop;
public StaticRoute() {
}
import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
-@XmlRootElement
+/**
+ * This class represents a list of static routes.
+ */
+@XmlRootElement(name = "list")
@XmlAccessorType(XmlAccessType.NONE)
-
public class StaticRoutes {
@XmlElement
List<StaticRoute> staticRoute;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBElement;
import org.codehaus.enunciate.jaxrs.ResponseCode;
import org.opendaylight.controller.sal.utils.Status;
/**
- * Static Routing Northbound APIs
+ * <p>Static Routing Northbound API allows for the management of the static
+ * routes.</p>
+ * </br>
+ * An example request/response for retrieving the static routes may look like this: </br>
+ * <pre>
+ * GET http://localhost:8080/controller/nb/v2/staticroute/default HTTP/1.1
+ * Accept: application/json
+ *
+ * HTTP/1.1 200 OK
+ * Content-Type: application/json
+ *
+ * {"staticRoute":{"name":"route-1","prefix":"10.10.1.0/24","nextHop":"1.1.1.1"}}
+ *
+ * </pre>
*
* <br><br>
* Authentication scheme : <b>HTTP Basic</b><br>
* Authentication realm : <b>opendaylight</b><br>
* Transport : <b>HTTP and HTTPS</b><br>
* <br>
- * HTTPS Authentication is disabled by default. Administrator can enable it in tomcat-server.xml after adding
- * a proper keystore / SSL certificate from a trusted authority.<br>
- * More info : http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
*/
@Path("/")
public class StaticRoutingNorthbound {
}
/**
- * Returns a list of static routes present on the given container
+ * Get a list of static routes present on the given container.
*
* @param containerName Name of the Container. The Container name for the base controller is "default".
* @return List of configured static routes on the given container
+ *
+ * <pre>
+ * Example:
+ *
+ * Request URL:
+ * GET http://localhost:8080/controller/nb/v2/staticroute/default
+ *
+ * Response in XML:
+ * <list>
+ * <staticRoute>
+ * <name>route-1</name>
+ * <prefix>10.10.1.0/24</prefix>
+ * <nextHop>1.1.1.1</nextHop>
+ * </staticRoute>
+ * </list>
+ *
+ * Response in JSON:
+ * {"staticRoute":{"name":"route-1","prefix":"10.10.1.0/24","nextHop":"1.1.1.1"}}
+ *
+ * </pre>
*/
@Path("/{containerName}")
@GET
* Returns the static route for the provided configuration name on a given container
*
* @param containerName Name of the Container. The Container name for the base controller is "default".
- * @param name Name of the Static Route configuration
+ * @param route Name of the Static Route configuration
* @return Static route configured with the supplied Name.
+ *
+ * <pre>
+ * Example:
+ *
+ * Request URL:
+ * GET http://localhost:8080/controller/nb/v2/staticroute/default/route/route-1
+ *
+ * Response in XML:
+ *
+ * <staticRoute>
+ * <name>route-1</name>
+ * <prefix>10.10.1.0/24</prefix>
+ * <nextHop>1.1.1.1</nextHop>
+ * </staticRoute>
+ *
+ * Response in JSON:
+ * {"name":"route-1","prefix":"10.10.1.0/24","nextHop":"1.1.1.1"}
+ *
+ * </pre>
*/
- @Path("/{containerName}/{name}")
+ @Path("/{containerName}/route/{route}")
@GET
@Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(StaticRoute.class)
@ResponseCode(code = 404, condition = "The Container Name or Static Route Configuration name passed was not found") })
public StaticRoute getStaticRoute(
@PathParam("containerName") String containerName,
- @PathParam("name") String name) {
+ @PathParam("route") String route) {
if(!NorthboundUtils.isAuthorized(getUserName(), containerName,
Privilege.WRITE, this)){
+ containerName);
}
List<StaticRoute> routes = this.getStaticRoutesInternal(containerName);
- for (StaticRoute route : routes) {
- if (route.getName().equalsIgnoreCase(name)) {
- return route;
+ for (StaticRoute r : routes) {
+ if (r.getName().equalsIgnoreCase(route)) {
+ return r;
}
}
* Add a new Static Route
*
* @param containerName Name of the Container. The Container name for the base controller is "default".
- * @param name Name of the Static Route configuration
+ * @param route Name of the Static Route configuration
* @return Response as dictated by the HTTP Response code
+ *
+ * <pre>
+ * Example:
+ *
+ * Request URL:
+ * POST http://localhost:8080/controller/nb/v2/staticroute/default/route/route-1
+ *
+ * Request payload in JSON:
+ * {"name":"route-1","prefix":"10.10.1.0/24","nextHop":"1.1.1.1"}
+ *
+ * </pre>
*/
- @Path("/{containerName}/{name}")
+ @Path("/{containerName}/route/{route}")
@POST
@Consumes( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes( {
@ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active"),
@ResponseCode(code = 409, condition = "Failed to create Static Route entry due to Conflicting Name or Prefix."), })
public Response addStaticRoute(
+ @Context UriInfo uriInfo,
@PathParam(value = "containerName") String containerName,
- @PathParam(value = "name") String name,
+ @PathParam(value = "route") String route,
@TypeHint(StaticRoute.class) JAXBElement<StaticRoute> staticRouteData) {
sRoute.getPrefix(), sRoute.getNextHop());
Status response = staticRouting.addStaticRoute(cfgObject);
if (response.isSuccess()) {
- NorthboundUtils.auditlog("Static Route", username, "added", name, containerName);
- return Response.status(Response.Status.CREATED).build();
+ NorthboundUtils.auditlog("Static Route", username, "added", route, containerName);
+ return Response.created(uriInfo.getRequestUri()).build();
}
throw new ResourceConflictException(response.getDescription());
}
* Delete a Static Route
*
* @param containerName Name of the Container. The Container name for the base controller is "default".
- * @param name Name of the Static Route configuration to be removed
+ * @param route Name of the Static Route configuration to be removed
*
* @return Response as dictated by the HTTP Response code
+ *
+ * <pre>
+ * Example:
+ *
+ * Request URL:
+ * DELETE http://localhost:8080/controller/nb/v2/staticroute/default/route/route-1
+ *
+ * </pre>
*/
-
- @Path("/{containerName}/{name}")
+ @Path("/{containerName}/route/{route}")
@DELETE
@StatusCodes( {
@ResponseCode(code = 200, condition = "Operation successful"),
@ResponseCode(code = 406, condition = "Cannot operate on Default Container when other Containers are active") })
public Response removeStaticRoute(
@PathParam(value = "containerName") String containerName,
- @PathParam(value = "name") String name) {
+ @PathParam(value = "route") String route) {
if(!NorthboundUtils.isAuthorized(getUserName(), containerName,
Privilege.WRITE, this)){
.toString());
}
- Status status = staticRouting.removeStaticRoute(name);
+ Status status = staticRouting.removeStaticRoute(route);
if (status.isSuccess()) {
- NorthboundUtils.auditlog("Static Route", username, "removed", name, containerName);
+ NorthboundUtils.auditlog("Static Route", username, "removed", route, containerName);
return Response.ok().build();
}
throw new ResourceNotFoundException(status.getDescription());
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
-@XmlRootElement
+@XmlRootElement(name = "list")
@XmlAccessorType(XmlAccessType.NONE)
-
public class AllFlowStatistics {
@XmlElement
List<FlowStatistics> flowStatistics;
//To satisfy JAXB
- private AllFlowStatistics() {
- }
+ @SuppressWarnings("unused")
+ private AllFlowStatistics() {}
public AllFlowStatistics(List<FlowStatistics> flowStatistics) {
this.flowStatistics = flowStatistics;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
-@XmlRootElement
+@XmlRootElement(name = "list")
@XmlAccessorType(XmlAccessType.NONE)
-
public class AllPortStatistics {
@XmlElement
List<PortStatistics> portStatistics;
//To satisfy JAXB
- private AllPortStatistics() {
- }
+ @SuppressWarnings("unused")
+ private AllPortStatistics() {}
public AllPortStatistics(List<PortStatistics> portStatistics) {
this.portStatistics = portStatistics;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
-@XmlRootElement
+@XmlRootElement(name = "list")
@XmlAccessorType(XmlAccessType.NONE)
public class AllTableStatistics {
@XmlElement
List<TableStatistics> tableStatistics;
//To satisfy JAXB
- private AllTableStatistics() {
- }
+ @SuppressWarnings("unused")
+ private AllTableStatistics() {}
public AllTableStatistics(List<TableStatistics> tableStatistics) {
this.tableStatistics = tableStatistics;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
+
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.reader.FlowOnNode;
-@XmlRootElement
+@XmlRootElement(name = "nodeFlowStatistics")
@XmlAccessorType(XmlAccessType.NONE)
public class FlowStatistics {
@XmlElement
private Node node;
- @XmlElement(name="flowStat")
- private List<FlowOnNode> flowStat;
+ @XmlElement
+ private List<FlowOnNode> flowStatistic;
// To satisfy JAXB
@SuppressWarnings("unused")
- private FlowStatistics() {
+ private FlowStatistics() {
}
public FlowStatistics(Node node, List<FlowOnNode> flowStat) {
super();
this.node = node;
- this.flowStat = flowStat;
+ this.flowStatistic = flowStat;
}
public Node getNode() {
}
public List<FlowOnNode> getFlowStats() {
- return flowStat;
+ return flowStatistic;
}
public void setFlowStats(List<FlowOnNode> flowStats) {
- this.flowStat = flowStats;
+ this.flowStatistic = flowStats;
}
}
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
-@XmlRootElement
+@XmlRootElement(name = "nodePortStatistics")
@XmlAccessorType(XmlAccessType.NONE)
public class PortStatistics {
@XmlElement
private Node node;
- @XmlElement(name="portStat")
- private List<NodeConnectorStatistics> portStats;
+ @XmlElement
+ private List<NodeConnectorStatistics> portStatistic;
// To satisfy JAXB
@SuppressWarnings("unused")
public PortStatistics(Node node, List<NodeConnectorStatistics> portStats) {
super();
this.node = node;
- this.portStats = portStats;
+ this.portStatistic = portStats;
}
public Node getNode() {
}
public List<NodeConnectorStatistics> getPortStats() {
- return portStats;
+ return portStatistic;
}
public void setFlowStats(List<NodeConnectorStatistics> portStats) {
- this.portStats = portStats;
+ this.portStatistic = portStats;
}
}
import org.codehaus.enunciate.jaxrs.StatusCodes;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.opendaylight.controller.containermanager.IContainerManager;
-
import org.opendaylight.controller.northbound.commons.RestMessages;
-import org.opendaylight.controller.northbound.commons.exception.*;
+import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
+import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
+import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
import org.opendaylight.controller.sal.authorization.Privilege;
import org.opendaylight.controller.sal.core.Node;
/**
* Northbound APIs that returns various Statistics exposed by the Southbound
- * plugins such as Openflow.
+ * protocol plugins such as Openflow.
*
* <br>
* <br>
* Name of the Container. The Container name for the base
* controller is "default".
* @return List of FlowStatistics from all the Nodes
+ *
+ * <pre>
+ * Example:
+ * Request URL: localhost:8080/controller/nb/v2/statistics/default/flow
+ *
+ * Response in JSON:
+ * {
+ * "flowStatistics": [
+ * {
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:02"
+ * },
+ * "flowStatistic": [
+ * {
+ * "flow": {
+ * "match": {
+ * "matchField": [
+ * {
+ * "type": "DL_TYPE",
+ * "value": "2048"
+ * },
+ * {
+ * "mask": "255.255.255.255",
+ * "type": "NW_DST",
+ * "value": "1.1.1.1"
+ * }
+ * ]
+ * },
+ * "actions": {
+ * "@type": "output",
+ * "port": {
+ * "@type": "OF",
+ * "@id": "3",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:02"
+ * }
+ * }
+ * },
+ * "priority": "1",
+ * "idleTimeout": "0",
+ * "hardTimeout": "0",
+ * "id": "0"
+ * },
+ * "tableId": "0",
+ * "durationSeconds": "1828",
+ * "durationNanoseconds": "397000000",
+ * "packetCount": "0",
+ * "byteCount": "0"
+ * }
+ * ]
+ * },
+ * { flow statistics of another node
+ * ............
+ * ................
+ * ......................
+ * }
+ *
+ * ]
+ * }
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <list>
+ * <flowStatistics>
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * <flowStatistic>
+ * <flow>
+ * <match>
+ * <matchField>
+ * <type>DL_TYPE</type>
+ * <value>2048</value>
+ * </matchField>
+ * <matchField>
+ * <mask>255.255.255.255</mask>
+ * <type>NW_DST</type>
+ * <value>1.1.1.2</value>
+ * </matchField>
+ * </match>
+ * <actions
+ * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="output">
+ * <port type="OF" id="3">
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * </port>
+ * </actions>
+ * <priority>1</priority>
+ * <idleTimeout>0</idleTimeout>
+ * <hardTimeout>0</hardTimeout>
+ * <id>0</id>
+ * </flow>
+ * <tableId>0</tableId>
+ * <durationSeconds>337</durationSeconds>
+ * <durationNanoseconds>149000000</durationNanoseconds>
+ * <packetCount>0</packetCount>
+ * <byteCount>0</byteCount>
+ * </flowStatistic>
+ * </flowStatistics>
+ * <flowStatistics>
+ * flow statistics for another node
+ * ..........
+ * ................
+ * .....................
+ * </flowStatistics>
+ * </list>
+ * </pre>
*/
- @Path("/{containerName}/flowstats")
+ @Path("/{containerName}/flow")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(AllFlowStatistics.class)
* Name of the Container. The Container name for the base
* controller is "default".
* @param nodeType
- * Node Type as specifid by Node class
+ * Node Type as specifid in {@link org.opendaylight.controller.sal.core.Node} class
* @param nodeId
* Node Identifier
- * @return List of Flow Statistics for a given Node.
+ * @return List of Flow Statistics for a given Node. *
+ *
+ * <pre>
+ * Example:
+ * Request URL:
+ * http://host:8080/controller/nb/v2/statistics/default/flow/node/OF/00:00:00:00:00:00:00:01
+ *
+ * Response in JSON:
+ * {
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * },
+ * "flowStatistic": [
+ * {
+ * "flow": {
+ * "match": {
+ * "matchField": [
+ * {
+ * "type": "DL_TYPE",
+ * "value": "2048"
+ * },
+ * {
+ * "mask": "255.255.255.255",
+ * "type": "NW_DST",
+ * "value": "1.1.1.2"
+ * }
+ * ]
+ * },
+ * "actions": [
+ * {
+ * "@type": "setDlDst",
+ * "address": "52d28b0f76ec"
+ * },
+ * {
+ * "@type": "output",
+ * "port": {
+ * "@type": "OF",
+ * "@id": "5",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * }
+ * }
+ * }
+ * ],
+ * "priority": "1",
+ * "idleTimeout": "0",
+ * "hardTimeout": "0",
+ * "id": "0"
+ * },
+ * "tableId": "0",
+ * "durationSeconds": "2089",
+ * "durationNanoseconds": "538000000",
+ * "packetCount": "0",
+ * "byteCount": "0"
+ * }
+ * ]
+ * }
+ *
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <nodeFlowStatistics>
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * <flowStatistic>
+ * <flow>
+ * <match>
+ * <matchField>
+ * <type>DL_TYPE</type>
+ * <value>2048</value>
+ * </matchField>
+ * <matchField>
+ * <mask>255.255.255.255</mask>
+ * <type>NW_DST</type>
+ * <value>1.1.1.2</value>
+ * </matchField>
+ * </match>
+ * <actions
+ * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="output">
+ * <port type="OF" id="3">
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * </port>
+ * </actions>
+ * <priority>1</priority>
+ * <idleTimeout>0</idleTimeout>
+ * <hardTimeout>0</hardTimeout>
+ * <id>0</id>
+ * </flow>
+ * <tableId>0</tableId>
+ * <durationSeconds>337</durationSeconds>
+ * <durationNanoseconds>149000000</durationNanoseconds>
+ * <packetCount>0</packetCount>
+ * <byteCount>0</byteCount>
+ * </flowStatistic>
+ * <flowStatistic>
+ * <flow>
+ * <match>
+ * <matchField>
+ * <type>DL_TYPE</type>
+ * <value>2048</value>
+ * </matchField>
+ * <matchField>
+ * <mask>255.255.255.255</mask>
+ * <type>NW_DST</type>
+ * <value>1.1.1.1</value>
+ * </matchField>
+ * </match>
+ * <actions
+ * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="output">
+ * <port type="OF" id="3">
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * </port>
+ * </actions>
+ * <priority>1</priority>
+ * <idleTimeout>0</idleTimeout>
+ * <hardTimeout>0</hardTimeout>
+ * <id>0</id>
+ * </flow>
+ * <tableId>0</tableId>
+ * <durationSeconds>337</durationSeconds>
+ * <durationNanoseconds>208000000</durationNanoseconds>
+ * <packetCount>0</packetCount>
+ * <byteCount>0</byteCount>
+ * </flowStatistic>
+ * </nodeFlowStatistics>
+ * </pre>
*/
- @Path("/{containerName}/flowstats/{nodeType}/{nodeId}")
+ @Path("/{containerName}/flow/node/{nodeType}/{nodeId}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(FlowStatistics.class)
* controller is "default".
* @return List of all the Port Statistics across all the NodeConnectors on
* all the Nodes.
+ *
+ * <pre>
+ * Example:
+ *
+ * Requset URL: http://host:8080/controller/nb/v2/statistics/default/port
+ *
+ * Response in JSON:
+ * {
+ * "portStatistics": [
+ * {
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:02"
+ * },
+ * "portStatistic": [
+ * {
+ * "nodeConnector": {
+ * "@type": "OF",
+ * "@id": "3",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:02"
+ * }
+ * },
+ * "receivePackets": "182",
+ * "transmitPackets": "173",
+ * "receiveBytes": "12740",
+ * "transmitBytes": "12110",
+ * "receiveDrops": "0",
+ * "transmitDrops": "0",
+ * "receiveErrors": "0",
+ * "transmitErrors": "0",
+ * "receiveFrameError": "0",
+ * "receiveOverRunError": "0",
+ * "receiveCrcError": "0",
+ * "collisionCount": "0"
+ * },
+ * {
+ * "nodeConnector": {
+ * "@type": "OF",
+ * "@id": "2",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:02"
+ * }
+ * },
+ * "receivePackets": "174",
+ * "transmitPackets": "181",
+ * "receiveBytes": "12180",
+ * "transmitBytes": "12670",
+ * "receiveDrops": "0",
+ * "transmitDrops": "0",
+ * "receiveErrors": "0",
+ * "transmitErrors": "0",
+ * "receiveFrameError": "0",
+ * "receiveOverRunError": "0",
+ * "receiveCrcError": "0",
+ * "collisionCount": "0"
+ * },
+ *
+ * ]
+ * },
+ * {
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:03"
+ * },
+ * "portStatistic": [
+ * ..................
+ * .......................
+ * ..........................
+ * ]
+ * }
+ * ]
+ * }
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <list>
+ * <portStatistics>
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * <portStatistic>
+ * <nodeConnector type="OF" id="3">
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * </nodeConnector>
+ * <receivePackets>181</receivePackets>
+ * <transmitPackets>172</transmitPackets>
+ * <receiveBytes>12670</receiveBytes>
+ * <transmitBytes>12040</transmitBytes>
+ * <receiveDrops>0</receiveDrops>
+ * <transmitDrops>0</transmitDrops>
+ * <receiveErrors>0</receiveErrors>
+ * <transmitErrors>0</transmitErrors>
+ * <receiveFrameError>0</receiveFrameError>
+ * <receiveOverRunError>0</receiveOverRunError>
+ * <receiveCrcError>0</receiveCrcError>
+ * <collisionCount>0</collisionCount>
+ * </portStatistic>
+ * <portStatistic>
+ * <nodeConnector type="OF" id="2">
+ * <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ * </nodeConnector>
+ * <receivePackets>173</receivePackets>
+ * <transmitPackets>180</transmitPackets>
+ * <receiveBytes>12110</receiveBytes>
+ * <transmitBytes>12600</transmitBytes>
+ * <receiveDrops>0</receiveDrops>
+ * <transmitDrops>0</transmitDrops>
+ * <receiveErrors>0</receiveErrors>
+ * <transmitErrors>0</transmitErrors>
+ * <receiveFrameError>0</receiveFrameError>
+ * <receiveOverRunError>0</receiveOverRunError>
+ * <receiveCrcError>0</receiveCrcError>
+ * <collisionCount>0</collisionCount>
+ * </portStatistic>
+ *
+ * </portStatistics>
+ * </list>
+ * </pre>
*/
- @Path("/{containerName}/portstats")
+ @Path("/{containerName}/port")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(AllPortStatistics.class)
* Name of the Container. The Container name for the base
* controller is "default".
* @param nodeType
- * Node Type as specifid by Node class
+ * Node Type as specifid in {@link org.opendaylight.controller.sal.core.Node} class
* @param Node
- * Identifier
+ * Identifier (e.g. MAC address)
* @return Returns a list of all the Port Statistics across all the
* NodeConnectors in a given Node.
+ *
+ * <pre>
+ * Example:
+ *
+ * Request URL:
+ * http://host:8080/controller/nb/v2/statistics/default/port/node/OF/00:00:00:00:00:00:00:01
+ *
+ * Response in JSON:
+ * {
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * },
+ * "portStatistic": [
+ * {
+ * "nodeConnector": {
+ * "@type": "OF",
+ * "@id": "3",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * }
+ * },
+ * "receivePackets": "171",
+ * "transmitPackets": "2451",
+ * "receiveBytes": "11970",
+ * "transmitBytes": "235186",
+ * "receiveDrops": "0",
+ * "transmitDrops": "0",
+ * "receiveErrors": "0",
+ * "transmitErrors": "0",
+ * "receiveFrameError": "0",
+ * "receiveOverRunError": "0",
+ * "receiveCrcError": "0",
+ * "collisionCount": "0"
+ * },
+ * {
+ * "nodeConnector": {
+ * "@type": "OF",
+ * "@id": "2",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * }
+ * },
+ * "receivePackets": "179",
+ * "transmitPackets": "2443",
+ * "receiveBytes": "12530",
+ * "transmitBytes": "234626",
+ * "receiveDrops": "0",
+ * "transmitDrops": "0",
+ * "receiveErrors": "0",
+ * "transmitErrors": "0",
+ * "receiveFrameError": "0",
+ * "receiveOverRunError": "0",
+ * "receiveCrcError": "0",
+ * "collisionCount": "0"
+ * }
+ * ]
+ * }
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <nodePortStatistics>
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * <portStatistic>
+ * <nodeConnector type="OF" id="2">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeConnector>
+ * <receivePackets>180</receivePackets>
+ * <transmitPackets>2594</transmitPackets>
+ * <receiveBytes>12600</receiveBytes>
+ * <transmitBytes>249396</transmitBytes>
+ * <receiveDrops>0</receiveDrops>
+ * <transmitDrops>0</transmitDrops>
+ * <receiveErrors>0</receiveErrors>
+ * <transmitErrors>0</transmitErrors>
+ * <receiveFrameError>0</receiveFrameError>
+ * <receiveOverRunError>0</receiveOverRunError>
+ * <receiveCrcError>0</receiveCrcError>
+ * <collisionCount>0</collisionCount>
+ * </portStatistic>
+ * <portStatistic>
+ * <nodeConnector type="OF" id="5">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeConnector>
+ * <receivePackets>2542</receivePackets>
+ * <transmitPackets>2719</transmitPackets>
+ * <receiveBytes>243012</receiveBytes>
+ * <transmitBytes>255374</transmitBytes>
+ * <receiveDrops>0</receiveDrops>
+ * <transmitDrops>0</transmitDrops>
+ * <receiveErrors>0</receiveErrors>
+ * <transmitErrors>0</transmitErrors>
+ * <receiveFrameError>0</receiveFrameError>
+ * <receiveOverRunError>0</receiveOverRunError>
+ * <receiveCrcError>0</receiveCrcError>
+ * <collisionCount>0</collisionCount>
+ * </portStatistic>
+ * </nodePortStatistics>
+ * </pre>
*/
- @Path("/{containerName}/portstats/{nodeType}/{nodeId}")
+ @Path("/{containerName}/port/node/{nodeType}/{nodeId}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(PortStatistics.class)
* controller is "default".
*
* @return Returns a list of all the Table Statistics in a given Node.
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://host:8080/controller/nb/v2/statistics/default/table
+ *
+ * Response in JSON:
+ *
+ * {
+ * "tableStatistics": [
+ * {
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:02"
+ * },
+ * "tableStatistic": [
+ * {
+ * "nodeTable": {
+ * "@id": "0",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:02"
+ * }
+ * },
+ * "activeCount": "11",
+ * "lookupCount": "816",
+ * "matchedCount": "220"
+ * },
+ * {
+ * ...another table
+ * .....
+ * ........
+ * }
+ *
+ * ]
+ * }
+ * ]
+ * }
+ *
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <list>
+ * <tableStatistics>
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * <tableStatistic>
+ * <nodeTable id="0">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeTable>
+ * <activeCount>12</activeCount>
+ * <lookupCount>10935</lookupCount>
+ * <matchedCount>10084</matchedCount>
+ * </tableStatistic>
+ * <tableStatistic>
+ * <nodeTable id="1">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeTable>
+ * <activeCount>0</activeCount>
+ * <lookupCount>0</lookupCount>
+ * <matchedCount>0</matchedCount>
+ * </tableStatistic>
+ * <tableStatistic>
+ * <nodeTable id="2">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeTable>
+ * <activeCount>0</activeCount>
+ * <lookupCount>0</lookupCount>
+ * <matchedCount>0</matchedCount>
+ * </tableStatistic>
+ * </tableStatistics>
+ * <tableStatistics>
+ * ...
+ * ......
+ * ........
+ * </tableStatistics>
+ * </list>
+ *
+ * </pre>
*/
- @Path("/{containerName}/tablestats")
+ @Path("/{containerName}/table")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
- @TypeHint(TableStatistics.class)
+ @TypeHint(AllTableStatistics.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
@ResponseCode(code = 404, condition = "The containerName is not found"),
public AllTableStatistics getTableStatistics(
@PathParam("containerName") String containerName) {
- if (!NorthboundUtils.isAuthorized(
- getUserName(), containerName, Privilege.READ, this)) {
- throw new UnauthorizedException(
- "User is not authorized to perform this operation on container "
- + containerName);
+ if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
+ throw new UnauthorizedException("User is not authorized to perform this operation on container "
+ + containerName);
}
handleDefaultDisabled(containerName);
IStatisticsManager statisticsManager = getStatisticsService(containerName);
if (statisticsManager == null) {
- throw new ServiceUnavailableException("Statistics "
+ throw new ServiceUnavailableException("Statistics manager"
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
}
/**
- * Returns a list of all the Table Statistics on all Nodes.
+ * Returns a list of all the Table Statistics on a specific node.
*
* @param containerName
* Name of the Container. The Container name for the base
* controller is "default".
* @param nodeType
- * Node Type as specifid by Node class
+ * Node Type as specified in {@link org.opendaylight.controller.sal.core.Node} class (e.g. OF for Openflow)
* @param Node
- * Identifier
+ * Identifier (e.g. MAC address)
* @return Returns a list of all the Table Statistics in a given Node.
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * Request URL:
+ * http://host:8080/controller/nb/v2/statistics/default/table/node/OF/00:00:00:00:00:00:00:01
+ *
+ * Response in JSON:
+ * {
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * },
+ * "tableStatistic": [
+ * {
+ * "nodeTable": {
+ * "@id": "0",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * }
+ * },
+ * "activeCount": "12",
+ * "lookupCount": "11382",
+ * "matchedCount": "10524"
+ * },
+ * {
+ * "nodeTable": {
+ * "@id": "1",
+ * "node": {
+ * "@type": "OF",
+ * "@id": "00:00:00:00:00:00:00:01"
+ * }
+ * },
+ * "activeCount": "0",
+ * "lookupCount": "0",
+ * "matchedCount": "0"
+ * }
+ * ]
+ * }
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ * <nodeTableStatistics>
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * <tableStatistic>
+ * <nodeTable id="0">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeTable>
+ * <activeCount>12</activeCount>
+ * <lookupCount>10935</lookupCount>
+ * <matchedCount>10084</matchedCount>
+ * </tableStatistic>
+ * <tableStatistic>
+ * <nodeTable id="1">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeTable>
+ * <activeCount>0</activeCount>
+ * <lookupCount>0</lookupCount>
+ * <matchedCount>0</matchedCount>
+ * </tableStatistic>
+ * <tableStatistic>
+ * <nodeTable id="2">
+ * <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ * </nodeTable>
+ * <activeCount>0</activeCount>
+ * <lookupCount>0</lookupCount>
+ * <matchedCount>0</matchedCount>
+ * </tableStatistic>
+ * </nodeTableStatistics>
+ *
+ *
+ * </pre>
*/
- @Path("/{containerName}/tablestats/{nodeType}/{nodeId}")
+ @Path("/{containerName}/table/node/{nodeType}/{nodeId}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(TableStatistics.class)
* 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.statistics.northbound;
+
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.reader.NodeTableStatistics;
-@XmlRootElement
+@XmlRootElement(name = "nodeTableStatistics")
@XmlAccessorType(XmlAccessType.NONE)
public class TableStatistics {
@XmlElement
private Node node;
- @XmlElement(name="tableStat")
- private List<NodeTableStatistics> tableStats;
+ @XmlElement
+ private List<NodeTableStatistics> tableStatistic;
// To satisfy JAXB
@SuppressWarnings("unused")
public TableStatistics(Node node, List<NodeTableStatistics> tableStats) {
super();
this.node = node;
- this.tableStats = tableStats;
+ this.tableStatistic = tableStats;
}
public Node getNode() {
}
public List<NodeTableStatistics> getTableStats() {
- return tableStats;
+ return tableStatistic;
}
public void setTableStats(List<NodeTableStatistics> tableStats) {
- this.tableStats = tableStats;
+ this.tableStatistic = tableStats;
}
}
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
-@XmlRootElement
+@XmlRootElement(name="list")
@XmlAccessorType(XmlAccessType.NONE)
public class NodeConnectors {
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
-@XmlRootElement
+@XmlRootElement(name="list")
@XmlAccessorType(XmlAccessType.NONE)
public class Nodes {
* Retrieve a list of all the nodes and their properties in the network
*
* @param containerName
- * The container for which we want to retrieve the list
+ * Name of the Container (Eg. 'default')
* @return A list of Pair each pair represents a
* {@link org.opendaylight.controller.sal.core.Node} and Set of
* {@link org.opendaylight.controller.sal.core.Property} attached to
* it.
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://.../default/nodes
+ *
+ * Response in XML:
+ * <list>
+ *    <nodeProperties>
+ *       <node type="OF" id="00:00:00:00:00:00:00:02"/>
+ *       <properties>
+ *          <tables>
+ *             <value>-1</value>
+ *          </tables>
+ *          <description>
+ *             <value>Switch2</value>
+ *          </description>
+ *          <actions>
+ *             <value>4095</value>
+ *          </actions>
+ *          <macAddress>
+ *             <value>00:00:00:00:00:02</value>
+ *          </macAddress>
+ *          <capabilities>
+ *             <value>199</value>
+ *          </capabilities>
+ *          <timeStamp>
+ *             <value>1377291227877</value>
+ *             <name>connectedSince</name>
+ *          </timeStamp>
+ *          <buffers>
+ *             <value>256</value>
+ *          </buffers>
+ *       </properties>
+ *    </nodeProperties>
+ * </list>
+ *
+ * Response in JSON:
+ * {"nodeProperties":[{"node":{"@type":"OF","@id":"00:00:00:00:00:00:00:02"},"properties":{"tables":{"value":"-1"},
+ * "description":{"value":"None"},"actions":{"value":"4095"},"macAddress":{"value":"00:00:00:00:00:02"},"capabilities"
+ * :{"value":"199"},"timeStamp":{"value":"1377291227877","name":"connectedSince"},"buffers":{"value":"256"}}}]}
+ *
+ * </pre>
*/
@Path("/{containerName}/nodes")
@GET
@TypeHint(Nodes.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The containerName is not found"),
@ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
public Nodes getNodes(@PathParam("containerName") String containerName) {
}
/**
- * Add a Name, Tier and Forwarding mode property to a node.
- *
- * <pre>
- * Example Request:
- * http://localhost:8080/controller/nb/v2/switch/red/node/OF/00:00:00:00:00:03/property/description/Switch3
- * (Valid properties that can be configured are: description, forwarding(only for default container) and tier)
- * </pre>
+ * Add a Description, Tier and Forwarding mode property to a node.
*
* @param containerName
- * Name of the Container
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
* Node Identifier as specified by
* {@link org.opendaylight.controller.sal.core.Node}
- * @param propName
- * Name of the Property specified by
- * {@link org.opendaylight.controller.sal.core.Property} and its
- * extended classes
- * @param propValue
- * Value of the Property specified by
- * {@link org.opendaylight.controller.sal.core.Property} and its
- * extended classes
+ * (Eg. '00:00:00:00:00:00:00:03')
+ * @param propertyName
+ * Name of the Property. Properties that can be
+ * configured are: description, forwarding(only for default
+ * container) and tier
+ * @param propertyValue
+ * Value of the Property. Description can be any string (Eg. 'Node1'),
+ * valid values for tier are 0, 1 and 2, and valid values for forwarding are 0 for
+ * reactive and 1 for proactive forwarding.
* @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://.../default/node/OF/00:00:00:00:00:00:00:03/property/description/Switch3
+ *
+ * </pre>
*/
- @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propName}/{propValue}")
+ @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}/{propertyValue}")
@PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(Response.class)
@StatusCodes({
- @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 201, condition = "Operation successful"),
@ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or node or configuration name is not found"),
@ResponseCode(code = 406, condition = "The property cannot be configured in non-default container"),
- @ResponseCode(code = 409, condition = "Unable to update configuration due to cluster conflict"),
+ @ResponseCode(code = 409, condition = "Unable to update configuration due to cluster conflict or conflicting description property"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
public Response addNodeProperty(
@PathParam("containerName") String containerName,
@PathParam("nodeType") String nodeType,
@PathParam("nodeId") String nodeId,
- @PathParam("propName") String propName,
- @PathParam("propValue") String propValue) {
+ @PathParam("propertyName") String propertyName,
+ @PathParam("propertyValue") String propertyValue) {
if (!isValidContainer(containerName)) {
throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
handleNodeAvailability(containerName, nodeType, nodeId);
Node node = Node.fromString(nodeType, nodeId);
- Property prop = switchManager.createProperty(propName, propValue);
+ Property prop = switchManager.createProperty(propertyName, propertyValue);
if (prop == null) {
- throw new ResourceNotFoundException("Property with name " + propName + " does not exist.");
+ throw new ResourceNotFoundException("Property with name " + propertyName + " does not exist.");
}
SwitchConfig switchConfig = switchManager.getSwitchConfig(node.toString());
Map<String, Property> nodeProperties = (switchConfig == null) ? new HashMap<String, Property>()
nodeProperties.put(prop.getName(), prop);
SwitchConfig newSwitchConfig = new SwitchConfig(node.toString(), nodeProperties);
Status status = switchManager.updateNodeConfig(newSwitchConfig);
+ if (status.isSuccess()) {
+ return Response.status(Response.Status.CREATED).build();
+ }
return NorthboundUtils.getResponse(status);
}
/**
* Delete a property of a node
*
- * <pre>
- * Example Request:
- * http://localhost:8080/controller/nb/v2/switch/default/node/OF/00:00:00:00:00:03/property/forwarding
- * </pre>
- *
* @param containerName
- * Name of the Container
+ * Name of the Container (Eg. 'SliceRed')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
* Node Identifier as specified by
* {@link org.opendaylight.controller.sal.core.Node}
+ * (Eg. '00:00:00:00:00:03:01:02')
* @param propertyName
- * Name of the Property specified by
- * {@link org.opendaylight.controller.sal.core.Property} and its
- * extended classes
+ * Name of the Property. Properties that can be deleted are
+ * description, forwarding(only in default container) and tier.
* @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://.../default/node/OF/00:00:00:00:00:00:00:03/property/forwarding
+ *
+ * </pre>
*/
@Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}")
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
@ResponseCode(code = 400, condition = "The nodeId or configuration is invalid"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
@ResponseCode(code = 409, condition = "Unable to delete property due to cluster conflict"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
/**
*
- * Retrieve a list of all the node connectors and their properties in a
+ * Retrieve a list of all the nodeconnectors and their properties in a
* given node
*
* @param containerName
- * The container for which we want to retrieve the list
+ * The container for which we want to retrieve the list (Eg.
+ * 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
* Node Identifier as specified by
- * {@link org.opendaylight.controller.sal.core.Node}
+ * {@link org.opendaylight.controller.sal.core.Node} (Eg.
+ * '00:00:00:00:00:00:00:03')
* @return A List of Pair each pair represents a
* {@link org.opendaylight.controller.sal.core.NodeConnector} and
* its corresponding
* {@link org.opendaylight.controller.sal.core.Property} attached to
* it.
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://.../default/node/OF/00:00:00:00:00:00:00:01
+ *
+ * Response in XML:
+ * <list>
+ *    <nodeConnectorProperties>
+ *       <nodeconnector type="OF" id="2">
+ *          <node type="OF" id="00:00:00:00:00:00:00:01"/>
+ *       </nodeconnector>
+ *       <properties>
+ *          <state>
+ *             <value>1</value>
+ *          </state>
+ *          <config>
+ *             <value>1</value>
+ *          </config>
+ *          <name>
+ *             <value>L1_2-C2_1</value>
+ *          </name>
+ *       </properties>
+ *    </nodeConnectorProperties>
+ * </list>
+ *
+ * Response in JSON:
+ * {"nodeConnectorProperties":[{"nodeconnector":{"@type":"OF","@id":"2","node":{"@type":"OF","@id":"00:00:00:00:00:00:00:01"}},
+ * "properties":{"state":{"value":"1"},"config":{"value":"1"},"name":{"value":"L1_2-C2_1"}}}]}
+ *
+ * </pre>
*/
@Path("/{containerName}/node/{nodeType}/{nodeId}")
@GET
@TypeHint(NodeConnectors.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The containerName is not found"),
@ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
public NodeConnectors getNodeConnectors(
}
/**
- * Add a Name/Bandwidth property to a node connector
+ * Add Bandwidth property to a node connector
*
* @param containerName
- * Name of the Container
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
* Node Identifier as specified by
* {@link org.opendaylight.controller.sal.core.Node}
+ * (Eg. '00:00:00:00:00:00:00:03')
* @param nodeConnectorType
- * Type of the node connector being programmed
+ * Type of the node connector being programmed (Eg. 'OF')
* @param nodeConnectorId
* NodeConnector Identifier as specified by
* {@link org.opendaylight.controller.sal.core.NodeConnector}
- * @param propName
+ * (Eg. '2')
+ * @param propertyName
* Name of the Property specified by
* {@link org.opendaylight.controller.sal.core.Property} and its
* extended classes
- * @param propValue
+ * Property that can be configured is bandwidth
+ * @param propertyValue
* Value of the Property specified by
* {@link org.opendaylight.controller.sal.core.Property} and its
* extended classes
* @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://.../default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth/1
+ *
+ * </pre>
*/
- @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propName}/{propValue}")
+ @Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}/{propertyValue}")
@PUT
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
- @ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 201, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
+ @ResponseCode(code = 409, condition = "Unable to add property due to cluster conflict"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
public Response addNodeConnectorProperty(
@PathParam("containerName") String containerName,
@PathParam("nodeId") String nodeId,
@PathParam("nodeConnectorType") String nodeConnectorType,
@PathParam("nodeConnectorId") String nodeConnectorId,
- @PathParam("propName") String propName,
- @PathParam("propValue") String propValue) {
+ @PathParam("propertyName") String propertyName,
+ @PathParam("propertyValue") String propertyValue) {
if (!isValidContainer(containerName)) {
throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
NodeConnector nc = NodeConnector
.fromStringNoNode(nodeConnectorType, nodeConnectorId, node);
- Property prop = switchManager.createProperty(propName, propValue);
+ Property prop = switchManager.createProperty(propertyName, propertyValue);
if (prop == null) {
throw new ResourceNotFoundException(
RestMessages.INVALIDDATA.toString());
* Delete a property of a node connector
*
* @param containerName
- * Name of the Container
+ * Name of the Container (Eg. 'default')
* @param nodeType
- * Type of the node being programmed
+ * Type of the node being programmed (Eg. 'OF')
* @param nodeId
* Node Identifier as specified by
* {@link org.opendaylight.controller.sal.core.Node}
+ * (Eg. '00:00:00:00:00:00:00:01')
* @param nodeConnectorType
- * Type of the node connector being programmed
+ * Type of the node connector being programmed (Eg. 'OF')
* @param nodeConnectorId
* NodeConnector Identifier as specified by
* {@link org.opendaylight.controller.sal.core.NodeConnector}
+ * (Eg. '1')
* @param propertyName
* Name of the Property specified by
* {@link org.opendaylight.controller.sal.core.Property} and its
- * extended classes
+ * extended classes. Property that can be deleted is bandwidth
* @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://.../default/nodeconnector/OF/00:00:00:00:00:00:00:01/OF/2/property/bandwidth
+ *
+ * </pre>
*/
@Path("/{containerName}/nodeconnector/{nodeType}/{nodeId}/{nodeConnectorType}/{nodeConnectorId}/property/{propertyName}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The Container Name or nodeId or configuration name is not found"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
public Response deleteNodeConnectorProperty(
throw new ResourceNotFoundException(ret.getDescription());
}
- /* *//**
- * Retrieve a list of Span ports that were configured previously.
- *
- * @param containerName
- * Name of the Container
- * @return list of
- * {@link org.opendaylight.controller.switchmanager.SpanConfig}
- * resources
- */
- /*
- * @Path("/span-config/{containerName}")
- *
- * @GET
- *
- * @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
- *
- * @StatusCodes( {
- *
- * @ResponseCode(code = 200, condition = "Operation successful"),
- *
- * @ResponseCode(code = 404, condition = "The containerName is not found"),
- *
- * @ResponseCode(code = 503, condition =
- * "One or more of Controller Services are unavailable") }) public
- * List<SpanConfig> getSpanConfigList(@PathParam("containerName") String
- * containerName) { ISwitchManager switchManager = (ISwitchManager)
- * getIfSwitchManagerService(containerName); if (switchManager == null) {
- * throw new ServiceUnavailableException("Switch Manager " +
- * RestMessages.SERVICEUNAVAILABLE.toString()); }
- *
- * return switchManager.getSpanConfigList(); }
- *//**
- * Add a span configuration
- *
- * @param containerName
- * Name of the Container
- * @param config
- * {@link org.opendaylight.controller.switchmanager.SpanConfig}
- * in JSON or XML format
- * @return Response as dictated by the HTTP Response Status code
- */
- /*
- * @Path("/span-config/{containerName}")
- *
- * @PUT
- *
- * @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
- *
- * @StatusCodes( {
- *
- * @ResponseCode(code = 200, condition = "Operation successful"),
- *
- * @ResponseCode(code = 404, condition = "The containerName is not found"),
- *
- * @ResponseCode(code = 503, condition =
- * "One or more of Controller Services are unavailable") }) public Response
- * addSpanConfig(@PathParam("containerName") String containerName,
- *
- * @TypeHint(SubnetConfig.class) JAXBElement<SpanConfig> config) {
- * ISwitchManager switchManager = (ISwitchManager)
- * getIfSwitchManagerService(containerName); if (switchManager == null) {
- * throw new ServiceUnavailableException("Switch Manager " +
- * RestMessages.SERVICEUNAVAILABLE.toString()); }
- *
- * String ret = switchManager.addSpanConfig(config.getValue()); if
- * (ret.equals(ReturnString.SUCCESS.toString())) { return
- * Response.status(Response.Status.CREATED).build(); } throw new
- * InternalServerErrorException(ret); }
- *//**
- * Delete a span configuration
+ /**
+ * Save the current switch configurations
*
* @param containerName
- * Name of the Container
- * @param config
- * {@link org.opendaylight.controller.switchmanager.SpanConfig}
- * in JSON or XML format
+ * Name of the Container (Eg. 'default')
* @return Response as dictated by the HTTP Response Status code
- */
- /*
- * @Path("/span-config/{containerName}")
- *
- * @DELETE
- *
- * @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
*
- * @StatusCodes( {
- *
- * @ResponseCode(code = 200, condition = "Operation successful"),
- *
- * @ResponseCode(code = 404, condition = "The containerName is not found"),
+ * <pre>
*
- * @ResponseCode(code = 503, condition =
- * "One or more of Controller Services are unavailable") }) public Response
- * deleteSpanConfig(@PathParam("containerName") String containerName,
+ * Example:
*
- * @TypeHint(SubnetConfig.class) JAXBElement<SpanConfig> config) {
- * ISwitchManager switchManager = (ISwitchManager)
- * getIfSwitchManagerService(containerName); if (switchManager == null) {
- * throw new ServiceUnavailableException("Switch Manager " +
- * RestMessages.SERVICEUNAVAILABLE.toString()); }
+ * RequestURL:
+ * http://.../default/switch-config
*
- * String ret = switchManager.removeSpanConfig(config.getValue()); if
- * (ret.equals(ReturnString.SUCCESS.toString())) { return
- * Response.ok().build(); } throw new ResourceNotFoundException(ret); }
- */
-
- /**
- * Save the current switch configurations
- *
- * @param containerName
- * Name of the Container
- * @return Response as dictated by the HTTP Response Status code
+ * </pre>
*/
@Path("/{containerName}/switch-config")
@POST
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
+ @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
@ResponseCode(code = 404, condition = "The containerName is not found"),
+ @ResponseCode(code = 500, condition = "Failed to save switch configuration. Failure Reason included in HTTP Error response"),
@ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
public Response saveSwitchConfig(
@PathParam("containerName") String containerName) {
* Retrieve the Topology
*
* @param containerName
- * The container for which we want to retrieve the topology
+ * The container for which we want to retrieve the topology (Eg. 'default')
*
* @return A List of EdgeProps each EdgeProp represent an Edge of the grap
* with the corresponding properties attached to it.
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/topology/default
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?><topology><edgeProperties><edge><tailNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:02" type="OF"/></tailNodeConnector><headNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:51" type="OF"/></headNodeConnector></edge><properties><state><value>1</value></state><config><value>1</value></config><name><value>C1_2-L2_2</value></name><timeStamp><value>1377279422032</value><name>creation</name></timeStamp></properties></edgeProperties><edgeProperties><edge><tailNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:51" type="OF"/></tailNodeConnector><headNodeConnector id="2" type="OF"><node id="00:00:00:00:00:00:00:02" type="OF"/></headNodeConnector></edge><properties><state><value>1</value></state><name><value>L2_2-C1_2</value></name><config><value>1</value></config><timeStamp><value>1377279423564</value><name>creation</name></timeStamp></properties></edgeProperties></topology>
+ *
+ * Response in JSON:
+ * {"edgeProperties":[{"edge":{"tailNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:02","@type":"OF"}},"headNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:51","@type":"OF"}}},"properties":{"timeStamp":{"value":"1377278961017","name":"creation"}}},{"edge":{"tailNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:51","@type":"OF"}},"headNodeConnector":{"@id":"2","@type":"OF","node":{"@id":"00:00:00:00:00:00:00:02","@type":"OF"}}},"properties":{"timeStamp":{"value":"1377278961018","name":"creation"}}}]}
+ *
+ * </pre>
*/
@Path("/{containerName}")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(Topology.class)
- @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name passed was not found") })
+ @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") })
public Topology getTopology(@PathParam("containerName") String containerName) {
if (!NorthboundUtils.isAuthorized(
* Retrieve the user configured links
*
* @param containerName
- * The container for which we want to retrieve the user links
+ * The container for which we want to retrieve the user links (Eg. 'default')
*
* @return A List of user configured links
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/topology/default/user-link
+ *
+ * Response in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?><topologyUserLinks><userLinks><status>Success</status><name>link1</name><srcNodeConnector>OF|2@OF|00:00:00:00:00:00:00:02</srcNodeConnector><dstNodeConnector>OF|2@OF|00:00:00:00:00:00:00:51</dstNodeConnector></userLinks></topologyUserLinks>
+ *
+ * Response in JSON:
+ * {"userLinks":{"status":"Success","name":"link1","srcNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:02","dstNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:51"}}
+ *
+ * </pre>
*/
- @Path("/{containerName}/userLink")
+ @Path("/{containerName}/user-link")
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@TypeHint(TopologyUserLinks.class)
- @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name passed was not found") })
+ @StatusCodes({ @ResponseCode(code = 404, condition = "The Container Name was not found") })
public TopologyUserLinks getUserLinks(
@PathParam("containerName") String containerName) {
* Add an User Link
*
* @param containerName
- * Name of the Container. The base Container is "default".
+ * Name of the Container (Eg. 'default')
* @param TopologyUserLinkConfig
* in JSON or XML format
* @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1-content
+ *
+ * Request in XML:
+ * <?xml version="1.0" encoding="UTF-8" standalone="yes"?><topologyUserLinks><status>Success</status><name>link1</name><srcNodeConnector>OF|2@OF|00:00:00:00:00:00:00:02</srcNodeConnector><dstNodeConnector>OF|2@OF|00:00:00:00:00:00:00:51</dstNodeConnector></topologyUserLinks>
+ *
+ * Request in JSON:
+ * {"status":"Success","name":"link1","srcNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:02","dstNodeConnector":"OF|2@OF|00:00:00:00:00:00:00:51"}
+ *
+ * </pre>
*/
-
- @Path("/{containerName}/userLink")
+ @Path("/{containerName}/user-link")
@POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 201, condition = "User Link added successfully"),
- @ResponseCode(code = 404, condition = "The Container Name passed was not found"),
+ @ResponseCode(code = 404, condition = "The Container Name was not found"),
@ResponseCode(code = 409, condition = "Failed to add User Link due to Conflicting Name"),
@ResponseCode(code = 500, condition = "Failed to add User Link. Failure Reason included in HTTP Error response"),
@ResponseCode(code = 503, condition = "One or more of Controller services are unavailable") })
* Delete an User Link
*
* @param containerName
- * Name of the Container. The base Container is "default".
+ * Name of the Container (Eg. 'default')
* @param name
- * Name of the Link Configuration
+ * Name of the Link Configuration (Eg. 'config1')
* @return Response as dictated by the HTTP Response Status code
+ *
+ * <pre>
+ *
+ * Example:
+ *
+ * RequestURL:
+ * http://localhost:8080/controller/nb/v2/topology/default/user-link/config1
+ *
+ * </pre>
*/
-
- @Path("/{containerName}/userLink/{name}")
+ @Path("/{containerName}/user-link/{name}")
@DELETE
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@StatusCodes({
@ResponseCode(code = 200, condition = "Operation successful"),
@ResponseCode(code = 404, condition = "The Container Name or Link Configuration Name was not found"),
}
throw new ResourceNotFoundException(ret.getDescription());
}
-
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
/**
* Updates node and its properties
*
- * @param node {@link org.opendaylight.controller.sal.core.Node} being updated
- * @param type {@link org.opendaylight.controller.sal.core.UpdateType}
- * @param props set of {@link org.opendaylight.controller.sal.core.Property} such as
- * {@link org.opendaylight.controller.sal.core.Description} and/or
- * {@link org.opendaylight.controller.sal.core.Tier} etc.
+ * @param node
+ * {@link org.opendaylight.controller.sal.core.Node} being
+ * updated
+ * @param type
+ * update type {@link org.opendaylight.controller.sal.core.UpdateType}
+ * @param props
+ * set of {@link org.opendaylight.controller.sal.core.Property}
+ * such as
+ * {@link org.opendaylight.controller.sal.core.Description}
+ * and/or {@link org.opendaylight.controller.sal.core.Tier} etc.
*/
public void updateNode(Node node, UpdateType type, Set<Property> props);
/**
* Updates node connector and its properties
*
- * @param nodeConnector {@link org.opendaylight.controller.sal.core.NodeConnector} being updated
- * @param type {@link org.opendaylight.controller.sal.core.UpdateType}
- * @param props set of {@link org.opendaylight.controller.sal.core.Property} such as
- * {@link org.opendaylight.controller.sal.core.Description} and/or
- * {@link org.opendaylight.controller.sal.core.State} etc.
+ * @param nodeConnector
+ * {@link org.opendaylight.controller.sal.core.NodeConnector}
+ * being updated
+ * @param type
+ * update type {@link org.opendaylight.controller.sal.core.UpdateType}
+ * @param props
+ * set of {@link org.opendaylight.controller.sal.core.Property}
+ * such as
+ * {@link org.opendaylight.controller.sal.core.Description}
+ * and/or {@link org.opendaylight.controller.sal.core.State} etc.
*/
public void updateNodeConnector(NodeConnector nodeConnector,
UpdateType type, Set<Property> props);
import org.openflow.protocol.statistics.OFStatistics;
/**
- * Interface defines the api which gets called when the information
- * contained in the OF statistics reply message from a network is updated with
- * new one.
+ * Interface which defines the notification functions which will get called when
+ * the information contained in the OF statistics reply message received from a
+ * network node is different from the cached one.
*/
public interface IOFStatisticsListener {
+ /**
+ * Notifies that a new list of description statistics objects for the given
+ * switch is available
+ *
+ * @param switchId
+ * The datapath id of the openflow switch
+ * @param description
+ * The new list of description statistics objects
+ */
public void descriptionStatisticsRefreshed(Long switchId, List<OFStatistics> description);
+ /**
+ * Notifies that a new list of flows statistics objects for the given switch
+ * is available
+ *
+ * @param switchId
+ * The datapath id of the openflow switch
+ * @param flows
+ * The new list of flow statistics objects
+ */
public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows);
+ /**
+ * Notifies that a new list of port statistics objects for the given switch
+ * is available
+ *
+ * @param switchId
+ * The datapath id of the openflow switch
+ * @param flows
+ * The new list of port statistics objects
+ */
public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports);
+ /**
+ * Notifies that a new list of table statistics objects for the given switch
+ * is available
+ *
+ * @param switchId
+ * The datapath id of the openflow switch
+ * @param flows
+ * The new list of table statistics objects
+ */
public void tableStatisticsRefreshed(Long switchId, List<OFStatistics> tables);
}
import org.opendaylight.controller.sal.reader.NodeTableStatistics;
/**
- * The Interface provides statistics updates to ReaderFilter listeners within
- * the protocol plugin
+ * The Interface provides notification of statistics (hardware view) updates to
+ * ReaderFilter listeners within the protocol plugin
*/
public interface IReadFilterInternalListener {
/**
- * Notifies the hardware view of all the flow installed on the specified network node
+ * Notifies the hardware view of all the flow installed on the specified
+ * network node was updated
+ *
* @param node
- * @return
+ * the network node
+ * @param flowStatsList
*/
public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList);
/**
- * Notifies the hardware view of the specified network node connector
+ * Notifies the hardware view of the specified network node connectors was
+ * updated
+ *
* @param node
- * @return
+ * the network node
*/
public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList);
/**
- * Notifies all the table statistics for a node
+ * Notifies the hardware view of the specified network node tables was
+ * updated
+ *
* @param node
- * @return
+ * the network node
*/
public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList);
/**
- * Notifies the hardware view of all the flow installed on the specified network node
+ * Notifies the hardware view of all the flow installed on the specified
+ * network node was updated
+ *
* @param node
- * @return
+ * the network node
*/
public void nodeDescriptionStatisticsUpdated(Node node, NodeDescription nodeDescription);
-
-
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
import org.opendaylight.controller.sal.reader.NodeTableStatistics;
/**
- * Interface to serve the hardware information requests coming from SAL
- * It is implemented by the respective OF1.0 plugin component
+ * Interface to serve the hardware information requests coming from SAL It is
+ * implemented by the respective OF1.0 plugin component
*
*/
public interface IReadServiceFilter {
/**
- * Returns the hardware image for the specified flow
- * on the specified network node for the passed container
+ * Returns the hardware image for the specified flow on the specified
+ * network node for the passed container
*
* @param container
+ * the container for which the request is originated
* @param node
+ * the network node
* @param flow
+ * the target flow
* @param cached
- * @return
+ * specify if entry has to be queried from the cached hardware
+ * information maintained in the protocol plugin or directly from
+ * the network node.
+ * @return The FlowOnNode object containing the information present in
+ * hardware for the passed flow on the specified network node
*/
- public FlowOnNode readFlow(String container, Node node, Flow flow,
- boolean cached);
+ public FlowOnNode readFlow(String container, Node node, Flow flow, boolean cached);
/**
- * Returns the hardware view of all the flow installed
- * on the specified network node for the passed container
+ * Returns the hardware view of all the flow installed on the specified
+ * network node for the passed container
*
* @param container
+ * the container for which the request is originated
* @param node
+ * the network node
* @param cached
- * @return
+ * specify if entries have to be queried from the cached hardware
+ * information maintained in the protocol plugin or directly from
+ * the network node.
+ * @return The list of FlowOnNode objects containing the information present
+ * in hardware on the specified network node for all its flows
*/
- public List<FlowOnNode> readAllFlow(String container, Node node,
- boolean cached);
+ public List<FlowOnNode> readAllFlow(String container, Node node, boolean cached);
/**
- * Returns the description of the network node as provided by the node itself
+ * Returns the description of the network node as provided by the node
+ * itself
*
* @param node
+ * the network node
* @param cached
- * @return
+ * specify if entry has to be queried from the cached hardware
+ * information maintained in the protocol plugin or directly from
+ * the network node.
+ * @return The NodeDescription object containing the description information
+ * for the specified network node
*/
public NodeDescription readDescription(Node node, boolean cached);
/**
- * Returns the hardware view of the specified network node connector
- * for the given container
- * @param node
- * @return
+ * Returns the hardware view of the specified network node connector for the
+ * given container
+ *
+ * @param container
+ * the container for which the request is originated
+ * @param nodeConnector
+ * the target nodeConnector
+ * @param cached
+ * specify if entry has to be queried from the cached hardware
+ * information maintained in the protocol plugin or directly from
+ * the corresponding network node.
+ * @return The NodeConnectorStatistics object containing the statistics
+ * present in hardware for the corresponding network node port
*/
- public NodeConnectorStatistics readNodeConnector(String container,
- NodeConnector nodeConnector, boolean cached);
+ public NodeConnectorStatistics readNodeConnector(String container, NodeConnector nodeConnector, boolean cached);
/**
- * Returns the hardware info for all the node connectors on the
- * specified network node for the given container
+ * Returns the hardware info for all the node connectors on the specified
+ * network node for the given container
*
+ * @param container
+ * the container for which the request is originated
* @param node
- * @return
+ * the target node
+ * @param cached
+ * specify if entries have to be queried from the cached hardware
+ * information maintained in the protocol plugin or directly from
+ * the corresponding network node.
+ * @return The list of NodeConnectorStatistics objects containing the
+ * statistics present in hardware for all the network node ports
*/
- public List<NodeConnectorStatistics> readAllNodeConnector(String container,
- Node node, boolean cached);
+ public List<NodeConnectorStatistics> readAllNodeConnector(String container, Node node, boolean cached);
/**
- * Returns the table statistics of the node as specified by the given container
- * @param node
+ * Returns the table statistics of the node as specified by the given
+ * container
+ *
+ * @param container
+ * the container for which the request is originated
+ * @param nodeTable
+ * the target network node table
* @param cached
- * @return
+ * specify if entry has to be queried from the cached hardware
+ * information maintained in the protocol plugin or directly from
+ * the corresponding network node.
+ * @return The NodeTableStatistics object containing the statistics present
+ * in hardware for the corresponding network node table
*/
- public NodeTableStatistics readNodeTable(String container,
- NodeTable nodeTable, boolean cached);
+ public NodeTableStatistics readNodeTable(String container, NodeTable nodeTable, boolean cached);
/**
* Returns the table statistics of all the tables for the specified node
*
+ * @param container
+ * the container for which the request is originated
* @param node
- * @return
+ * the target node
+ * @param cached
+ * specify if entries have to be queried from the cached hardware
+ * information maintained in the protocol plugin or directly from
+ * the corresponding network node.
+ * @return The list of NodeTableStatistics objects containing the statistics
+ * present in hardware for all the network node tables
*/
- public List<NodeTableStatistics> readAllNodeTable(String containerName,
- Node node, boolean cached);
+ public List<NodeTableStatistics> readAllNodeTable(String containerName, Node node, boolean cached);
/**
- * Returns the average transmit rate for the specified node conenctor on
- * the given container. If the node connector does not belong to the passed
+ * Returns the average transmit rate for the specified node connector on the
+ * given container. If the node connector does not belong to the passed
* container a zero value is returned
*
* @param container
+ * the container for which the request is originated
* @param nodeConnector
- * @return tx rate [bps]
+ * the target node connector
+ * @return The average tx rate in bps
*/
public long getTransmitRate(String container, NodeConnector nodeConnector);
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
* to be fetched from the plugin
*
* For example, an application that has been started late, will want to
- * be up to date with the latest topology. Hence, it requests for a
+ * be up to date with the latest topology. Hence, it requests for a
* topology refresh from the plugin.
*/
/**
* @param containerName
+ * Name of the container for the topology
*/
public void requestRefresh(String containerName);
}
*
* @param msg
* The OF message to be sent
+ * @param xid
+ * The XID to be used in the message
* @return The XID used
*/
public Integer asyncFastSend(OFMessage msg, int xid);
* Returns True if the port is enabled,
*
* @param portNumber
+ * the port ID
* @return True if the port is enabled
*/
public boolean isPortEnabled(short portNumber);
* Returns True if the port is enabled.
*
* @param port
+ * the OpenFlow port
* @return True if the port is enabled
*/
public boolean isPortEnabled(OFPhysicalPort port);
* Send Barrier message synchronously. The caller will be blocked until the
* Barrier reply arrives.
*/
- Object syncSendBarrierMessage();
+ public Object syncSendBarrierMessage();
/**
* Send Barrier message asynchronously. The caller is not blocked. The
* Barrier message will be sent in a transmit thread which will be blocked
* until the Barrier reply arrives.
*/
- Object asyncSendBarrierMessage();
+ public Object asyncSendBarrierMessage();
}
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
+import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitchStateListener;
import org.opendaylight.controller.sal.connection.ConnectionConstants;
import org.opendaylight.controller.sal.connection.IPluginInConnectionService;
-import org.opendaylight.controller.sal.connection.IPluginOutConnectionService;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.utils.Status;
import org.opendaylight.controller.sal.utils.StatusCode;
private ControllerIO controllerIO;
private Thread switchEventThread;
private ConcurrentHashMap<Long, ISwitch> switches;
- private BlockingQueue<SwitchEvent> switchEvents;
+ private PriorityBlockingQueue<SwitchEvent> switchEvents;
// only 1 message listener per OFType
private ConcurrentMap<OFType, IMessageListener> messageListeners;
// only 1 switch state listener
private AtomicInteger switchInstanceNumber;
private int MAXQUEUESIZE = 50000;
+ private static enum SwitchEventPriority { LOW, NORMAL, HIGH }
+
/*
* this thread monitors the switchEvents queue for new incoming events from
* switch
public void init() {
logger.debug("Initializing!");
this.switches = new ConcurrentHashMap<Long, ISwitch>();
- this.switchEvents = new LinkedBlockingQueue<SwitchEvent>(MAXQUEUESIZE);
+ this.switchEvents = new PriorityBlockingQueue<SwitchEvent>(MAXQUEUESIZE, new Comparator<SwitchEvent>() {
+ @Override
+ public int compare(SwitchEvent p1, SwitchEvent p2) {
+ return p2.getPriority() - p1.getPriority();
+ }
+ });
this.messageListeners = new ConcurrentHashMap<OFType, IMessageListener>();
this.switchStateListener = null;
this.switchInstanceNumber = new AtomicInteger(0);
if (((SwitchHandler) sw).isOperational()) {
Long sid = sw.getId();
if (this.switches.remove(sid, sw)) {
- logger.warn("{} is Disconnected", sw);
+ logger.info("{} is removed", sw);
notifySwitchDeleted(sw);
}
}
}
private synchronized void addSwitchEvent(SwitchEvent event) {
- try {
- this.switchEvents.put(event);
- } catch (InterruptedException e) {
- logger.debug("SwitchEvent caught Interrupt Exception");
- }
+ this.switchEvents.put(event);
}
public void takeSwitchEventAdd(ISwitch sw) {
- SwitchEvent ev = new SwitchEvent(
- SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
+ SwitchEvent ev = new SwitchEvent(SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null,
+ SwitchEventPriority.HIGH.ordinal());
addSwitchEvent(ev);
}
public void takeSwitchEventDelete(ISwitch sw) {
- SwitchEvent ev = new SwitchEvent(
- SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
+ SwitchEvent ev = new SwitchEvent(SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null,
+ SwitchEventPriority.HIGH.ordinal());
addSwitchEvent(ev);
}
public void takeSwitchEventError(ISwitch sw) {
- SwitchEvent ev = new SwitchEvent(
- SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
+ SwitchEvent ev = new SwitchEvent(SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null,
+ SwitchEventPriority.NORMAL.ordinal());
addSwitchEvent(ev);
}
public void takeSwitchEventMsg(ISwitch sw, OFMessage msg) {
if (messageListeners.get(msg.getType()) != null) {
- SwitchEvent ev = new SwitchEvent(
- SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg);
+ SwitchEvent ev = new SwitchEvent(SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg,
+ SwitchEventPriority.LOW.ordinal());
addSwitchEvent(ev);
}
}
return this.switches.get(switchId);
}
+ public void _controllerShowQueueSize(CommandInterpreter ci) {
+ ci.print("switchEvents queue size: " + switchEvents.size() + "\n");
+ }
+
public void _controllerShowSwitches(CommandInterpreter ci) {
Set<Long> sids = switches.keySet();
StringBuffer s = new StringBuffer();
help.append("\t controllerShowSwitches\n");
help.append("\t controllerReset\n");
help.append("\t controllerShowConnConfig\n");
+ help.append("\t controllerShowQueueSize\n");
return help.toString();
}
* @throws Exception
*/
@Override
- public void asyncSend(OFMessage msg) throws IOException {
+ public void asyncSend(OFMessage msg) throws Exception {
synchronized (outBuffer) {
int msgLen = msg.getLengthU();
if (outBuffer.remaining() < msgLen) {
* @throws Exception
*/
@Override
- public void resumeSend() throws IOException {
+ public void resumeSend() throws Exception {
synchronized (outBuffer) {
if (!socket.isOpen()) {
return;
* @throws Exception
*/
@Override
- public List<OFMessage> readMessages() throws IOException {
+ public List<OFMessage> readMessages() throws Exception {
if (!socket.isOpen()) {
return null;
}
.getLogger(SecureMessageReadWriteService.class);
private Selector selector;
- private SelectionKey clientSelectionKey;
private SocketChannel socket;
private BasicFactory factory;
sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
sslEngine.setNeedClientAuth(true);
+ sslEngine.setEnabledCipherSuites(new String[] {
+ "SSL_RSA_WITH_RC4_128_MD5",
+ "SSL_RSA_WITH_RC4_128_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"});
// Do initial handshake
doHandshake(socket, sslEngine);
- this.clientSelectionKey = this.socket.register(this.selector,
- SelectionKey.OP_READ);
+ this.socket.register(this.selector, SelectionKey.OP_READ);
}
/**
if (myAppData.hasRemaining()) {
myAppData.compact();
- this.clientSelectionKey = this.socket.register(this.selector,
- SelectionKey.OP_WRITE, this);
+ this.socket.register(this.selector, SelectionKey.OP_WRITE, this);
} else {
myAppData.clear();
- this.clientSelectionKey = this.socket.register(this.selector,
- SelectionKey.OP_READ, this);
+ this.socket.register(this.selector, SelectionKey.OP_READ, this);
}
logger.trace("Message sent: {}", msg);
if (myAppData.hasRemaining()) {
myAppData.compact();
- this.clientSelectionKey = this.socket.register(this.selector,
- SelectionKey.OP_WRITE, this);
+ this.socket.register(this.selector, SelectionKey.OP_WRITE, this);
} else {
myAppData.clear();
- this.clientSelectionKey = this.socket.register(this.selector,
- SelectionKey.OP_READ, this);
+ this.socket.register(this.selector, SelectionKey.OP_READ, this);
}
}
}
peerAppData.clear();
}
- this.clientSelectionKey = this.socket.register(this.selector,
- SelectionKey.OP_READ, this);
+ this.socket.register(this.selector, SelectionKey.OP_READ, this);
return msgs;
}
private SwitchEventType eventType;
private ISwitch sw;
private OFMessage msg;
+ private int priority;
- public SwitchEvent(SwitchEventType type, ISwitch sw, OFMessage msg) {
+ public SwitchEvent(SwitchEventType type, ISwitch sw, OFMessage msg, int priority) {
this.eventType = type;
this.sw = sw;
this.msg = msg;
+ this.priority = priority;
}
public SwitchEventType getEventType() {
return this.msg;
}
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
@Override
public String toString() {
String s;
package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
-import java.io.IOException;
-import java.net.SocketException;
-import java.nio.channels.AsynchronousCloseException;
-import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.PriorityBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.LoggerFactory;
public class SwitchHandler implements ISwitch {
- private static final Logger logger = LoggerFactory
- .getLogger(SwitchHandler.class);
+ private static final Logger logger = LoggerFactory.getLogger(SwitchHandler.class);
private static final int SWITCH_LIVENESS_TIMER = 5000;
private static final int switchLivenessTimeout = getSwitchLivenessTimeout();
private final int MESSAGE_RESPONSE_TIMER = 2000;
private Thread transmitThread;
private enum SwitchState {
- NON_OPERATIONAL(0), WAIT_FEATURES_REPLY(1), WAIT_CONFIG_REPLY(2), OPERATIONAL(
- 3);
+ NON_OPERATIONAL(0), WAIT_FEATURES_REPLY(1), WAIT_CONFIG_REPLY(2), OPERATIONAL(3);
private int value;
try {
responseTimerValue = Integer.decode(rTimer);
} catch (NumberFormatException e) {
- logger.warn(
- "Invalid of.messageResponseTimer: {} use default({})",
- rTimer, MESSAGE_RESPONSE_TIMER);
+ logger.warn("Invalid of.messageResponseTimer: {} use default({})", rTimer, MESSAGE_RESPONSE_TIMER);
}
}
}
try {
// wait for an incoming connection
selector.select(0);
- Iterator<SelectionKey> selectedKeys = selector
- .selectedKeys().iterator();
+ Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey skey = selectedKeys.next();
selectedKeys.remove();
switchHandlerThread.start();
}
- public void stop() {
+ private void stopInternal() {
+ logger.debug("{} receives stop signal",
+ (isOperational() ? HexString.toHexString(sid) : "unknown"));
running = false;
cancelSwitchTimer();
try {
msgReadWriteService.stop();
} catch (Exception e) {
}
- executor.shutdown();
+ logger.debug("executor shutdown now");
+ executor.shutdownNow();
msgReadWriteService = null;
+ }
+
+ public void stop() {
+ stopInternal();
if (switchHandlerThread != null) {
switchHandlerThread.interrupt();
*/
private void asyncSendNow(OFMessage msg) {
if (msgReadWriteService == null) {
- logger.warn(
- "asyncSendNow: {} is not sent because Message ReadWrite Service is not available.",
- msg);
+ logger.warn("asyncSendNow: {} is not sent because Message ReadWrite Service is not available.", msg);
return;
}
}
if (msgs == null) {
- logger.debug("{} is down", this);
- // the connection is down, inform core
+ logger.info("{} is down", this);
reportSwitchStateChange(false);
return;
}
switch (type) {
case HELLO:
// send feature request
- OFMessage featureRequest = factory
- .getMessage(OFType.FEATURES_REQUEST);
+ OFMessage featureRequest = factory.getMessage(OFType.FEATURES_REQUEST);
asyncFastSend(featureRequest);
// delete all pre-existing flows
OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
- OFFlowMod flowMod = (OFFlowMod) factory
- .getMessage(OFType.FLOW_MOD);
- flowMod.setMatch(match).setCommand(OFFlowMod.OFPFC_DELETE)
- .setOutPort(OFPort.OFPP_NONE)
+ OFFlowMod flowMod = (OFFlowMod) factory.getMessage(OFType.FLOW_MOD);
+ flowMod.setMatch(match).setCommand(OFFlowMod.OFPFC_DELETE).setOutPort(OFPort.OFPP_NONE)
.setLength((short) OFFlowMod.MINIMUM_LENGTH);
asyncFastSend(flowMod);
this.state = SwitchState.WAIT_FEATURES_REPLY;
startSwitchTimer();
break;
case ECHO_REQUEST:
- OFEchoReply echoReply = (OFEchoReply) factory
- .getMessage(OFType.ECHO_REPLY);
+ OFEchoReply echoReply = (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
// respond immediately
asyncSendNow(echoReply, msg.getXid());
break;
updatePhysicalPort(port);
} else if (msg.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
updatePhysicalPort(port);
- } else if (msg.getReason() == (byte) OFPortReason.OFPPR_DELETE
- .ordinal()) {
+ } else if (msg.getReason() == (byte) OFPortReason.OFPPR_DELETE.ordinal()) {
deletePhysicalPort(port);
}
if (probeSent) {
// switch failed to respond to our probe, consider
// it down
- logger.warn("{} is idle for too long, disconnect",
- toString());
+ logger.warn("{} sid {} is idle for too long, disconnect", socket.socket()
+ .getRemoteSocketAddress().toString().split("/")[1], (sid == 0) ? "unknown"
+ : HexString.toHexString(sid));
reportSwitchStateChange(false);
} else {
// send a probe to see if the switch is still alive
- logger.debug(
- "Send idle probe (Echo Request) to {}",
- this);
+ logger.debug("Send idle probe (Echo Request) to {}", this);
probeSent = true;
- OFMessage echo = factory
- .getMessage(OFType.ECHO_REQUEST);
+ OFMessage echo = factory.getMessage(OFType.ECHO_REQUEST);
asyncFastSend(echo);
}
} else {
if (state == SwitchState.WAIT_FEATURES_REPLY) {
// send another features request
- OFMessage request = factory
- .getMessage(OFType.FEATURES_REQUEST);
+ OFMessage request = factory.getMessage(OFType.FEATURES_REQUEST);
asyncFastSend(request);
} else {
if (state == SwitchState.WAIT_CONFIG_REPLY) {
// send another config request
- OFSetConfig config = (OFSetConfig) factory
- .getMessage(OFType.SET_CONFIG);
- config.setMissSendLength((short) 0xffff)
- .setLengthU(OFSetConfig.MINIMUM_LENGTH);
+ OFSetConfig config = (OFSetConfig) factory.getMessage(OFType.SET_CONFIG);
+ config.setMissSendLength((short) 0xffff).setLengthU(OFSetConfig.MINIMUM_LENGTH);
asyncFastSend(config);
- OFMessage getConfig = factory
- .getMessage(OFType.GET_CONFIG_REQUEST);
+ OFMessage getConfig = factory.getMessage(OFType.GET_CONFIG_REQUEST);
asyncFastSend(getConfig);
}
}
}
private void reportError(Exception e) {
- if (e instanceof AsynchronousCloseException
- || e instanceof InterruptedException
- || e instanceof SocketException || e instanceof IOException
- || e instanceof ClosedSelectorException) {
- if (logger.isDebugEnabled()) {
- logger.debug("Caught exception {}", e.getMessage());
- }
- } else {
- logger.warn("Caught exception ", e);
+ if (!running) {
+ logger.debug("Caught exception {} while switch {} is shutting down. Skip", e.getMessage(),
+ (isOperational() ? HexString.toHexString(sid) : "unknown"));
+ return;
}
+ logger.debug("Caught exception: ", e);
+
// notify core of this error event and disconnect the switch
((Controller) core).takeSwitchEventError(this);
+
+ // clean up some internal states immediately
+ stopInternal();
}
private void reportSwitchStateChange(boolean added) {
updatePhysicalPort(port);
}
// config the switch to send full data packet
- OFSetConfig config = (OFSetConfig) factory
- .getMessage(OFType.SET_CONFIG);
- config.setMissSendLength((short) 0xffff).setLengthU(
- OFSetConfig.MINIMUM_LENGTH);
+ OFSetConfig config = (OFSetConfig) factory.getMessage(OFType.SET_CONFIG);
+ config.setMissSendLength((short) 0xffff).setLengthU(OFSetConfig.MINIMUM_LENGTH);
asyncFastSend(config);
// send config request to make sure the switch can handle the set
// config
portBandwidth
.put(portNumber,
port.getCurrentFeatures()
- & (OFPortFeatures.OFPPF_10MB_FD.getValue()
- | OFPortFeatures.OFPPF_10MB_HD
- .getValue()
- | OFPortFeatures.OFPPF_100MB_FD
- .getValue()
- | OFPortFeatures.OFPPF_100MB_HD
- .getValue()
- | OFPortFeatures.OFPPF_1GB_FD
- .getValue()
- | OFPortFeatures.OFPPF_1GB_HD
- .getValue() | OFPortFeatures.OFPPF_10GB_FD
+ & (OFPortFeatures.OFPPF_10MB_FD.getValue() | OFPortFeatures.OFPPF_10MB_HD.getValue()
+ | OFPortFeatures.OFPPF_100MB_FD.getValue()
+ | OFPortFeatures.OFPPF_100MB_HD.getValue()
+ | OFPortFeatures.OFPPF_1GB_FD.getValue()
+ | OFPortFeatures.OFPPF_1GB_HD.getValue() | OFPortFeatures.OFPPF_10GB_FD
.getValue()));
}
@Override
public String toString() {
try {
- return ("Switch:"
- + socket.socket().getRemoteSocketAddress().toString().split("/")[1]
- + " SWID:" + (isOperational() ? HexString
+ return ("Switch:" + socket.socket().getRemoteSocketAddress().toString().split("/")[1] + " SWID:" + (isOperational() ? HexString
.toHexString(this.sid) : "unknown"));
} catch (Exception e) {
- return (isOperational() ? HexString.toHexString(this.sid)
- : "unknown");
+ return (isOperational() ? HexString.toHexString(this.sid) : "unknown");
}
}
int xid = getNextXid();
StatisticsCollector worker = new StatisticsCollector(this, xid, req);
messageWaitingDone.put(xid, worker);
- Future<Object> submit = executor.submit(worker);
+ Future<Object> submit;
Object result = null;
+ try {
+ submit = executor.submit(worker);
+ } catch (RejectedExecutionException re) {
+ messageWaitingDone.remove(xid);
+ return result;
+ }
try {
result = submit.get(responseTimerValue, TimeUnit.MILLISECONDS);
return result;
} catch (Exception e) {
- logger.warn("Timeout while waiting for {} replies", req.getType());
+ logger.warn("Timeout while waiting for {} replies from {}",
+ req.getType(), (isOperational() ? HexString.toHexString(sid) : "unknown"));
result = null; // to indicate timeout has occurred
worker.wakeup();
return result;
@Override
public Object syncSend(OFMessage msg) {
+ if (!running) {
+ logger.debug("Switch is going down, ignore syncSend");
+ return null;
+ }
int xid = getNextXid();
return syncSend(msg, xid);
}
*/
private void processBarrierReply(OFBarrierReply msg) {
Integer xid = msg.getXid();
- SynchronousMessage worker = (SynchronousMessage) messageWaitingDone
- .remove(xid);
+ SynchronousMessage worker = (SynchronousMessage) messageWaitingDone.remove(xid);
if (worker == null) {
return;
}
private void processStatsReply(OFStatisticsReply reply) {
Integer xid = reply.getXid();
- StatisticsCollector worker = (StatisticsCollector) messageWaitingDone
- .get(xid);
+ StatisticsCollector worker = (StatisticsCollector) messageWaitingDone.get(xid);
if (worker == null) {
return;
}
if ((portState & OFPortState.OFPPS_LINK_DOWN.getValue()) > 0) {
return false;
}
- if ((portState & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK
- .getValue()) {
+ if ((portState & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue()) {
return false;
}
return true;
syncMessageInternal(pmsg.msg, pmsg.msg.getXid(), false);
}
} catch (InterruptedException ie) {
- reportError(new InterruptedException(
- "PriorityMessageTransmit thread interrupted"));
+ reportError(new InterruptedException("PriorityMessageTransmit thread interrupted"));
} catch (Exception e) {
reportError(e);
}
* Setup and start the transmit thread
*/
private void startTransmitThread() {
- this.transmitQ = new PriorityBlockingQueue<PriorityMessage>(11,
- new Comparator<PriorityMessage>() {
- @Override
- public int compare(PriorityMessage p1, PriorityMessage p2) {
- if (p2.priority != p1.priority) {
- return p2.priority - p1.priority;
- } else {
- return (p2.seqNum < p1.seqNum) ? 1 : -1;
- }
- }
- });
+ this.transmitQ = new PriorityBlockingQueue<PriorityMessage>(11, new Comparator<PriorityMessage>() {
+ @Override
+ public int compare(PriorityMessage p1, PriorityMessage p2) {
+ if (p2.priority != p1.priority) {
+ return p2.priority - p1.priority;
+ } else {
+ return (p2.seqNum < p1.seqNum) ? 1 : -1;
+ }
+ }
+ });
this.transmitThread = new Thread(new PriorityMessageTransmit());
this.transmitThread.start();
}
private IMessageReadWrite getMessageReadWriteService() throws Exception {
String str = System.getProperty("secureChannelEnabled");
- return ((str != null) && (str.trim().equalsIgnoreCase("true"))) ? new SecureMessageReadWriteService(
- socket, selector) : new MessageReadWriteService(socket,
- selector);
+ return ((str != null) && (str.trim().equalsIgnoreCase("true"))) ? new SecureMessageReadWriteService(socket,
+ selector) : new MessageReadWriteService(socket, selector);
}
/**
messageWaitingDone.put(xid, worker);
Object result = null;
Boolean status = false;
- Future<Object> submit = executor.submit(worker);
+ Future<Object> submit;
+ try {
+ submit = executor.submit(worker);
+ } catch (RejectedExecutionException re) {
+ messageWaitingDone.remove(xid);
+ return result;
+ }
try {
result = submit.get(responseTimerValue, TimeUnit.MILLISECONDS);
messageWaitingDone.remove(xid);
// this message
// the result if OFError already
if (logger.isDebugEnabled()) {
- logger.debug("Send {} failed --> {}", msg.getType(),
- (result));
+ logger.debug("Send {} failed --> {}", msg.getType(), (result));
}
}
return result;
} catch (Exception e) {
- logger.warn("Timeout while waiting for {} reply", msg.getType()
- .toString());
+ logger.warn("Timeout while waiting for {} reply", msg.getType().toString());
// convert the result into a Boolean with value false
status = false;
result = status;
import org.opendaylight.controller.sal.utils.NetUtils;
import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
import org.opendaylight.controller.sal.utils.NodeCreator;
-import org.opendaylight.controller.sal.utils.Status;
-import org.opendaylight.controller.sal.utils.StatusCode;
/**
* The class describes neighbor discovery service for an OpenFlow network.
readyListHi.add(nodeConnector);
}
+ private void removeNodeConnector(NodeConnector nodeConnector) {
+ readyListLo.remove(nodeConnector);
+ readyListHi.remove(nodeConnector);
+ stagingList.remove(nodeConnector);
+ holdTime.remove(nodeConnector);
+ elapsedTime.remove(nodeConnector);
+ }
+
private Set<NodeConnector> getRemoveSet(Collection<NodeConnector> c, Node node) {
Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
if (c == null) {
}
for (NodeConnector nodeConnector : c) {
if (node.equals(nodeConnector.getNode())) {
- Edge edge1 = edgeMap.get(nodeConnector);
- if (edge1 != null) {
- removeSet.add(nodeConnector);
-
- // check reverse direction
- Edge edge2 = edgeMap.get(edge1.getTailNodeConnector());
- if ((edge2 != null) && node.equals(edge2.getTailNodeConnector().getNode())) {
- removeSet.add(edge2.getHeadNodeConnector());
- }
- }
+ removeSet.add(nodeConnector);
}
}
return removeSet;
private void removeDiscovery(Node node) {
Set<NodeConnector> removeSet;
+ removeSet = getRemoveSet(edgeMap.keySet(), node);
+ NodeConnector peerConnector;
+ Edge edge1, edge2;
+ for (NodeConnector nodeConnector : removeSet) {
+ // get the peer for fast removal of the edge in reverse direction
+ peerConnector = null;
+ edge1 = edgeMap.get(nodeConnector);
+ if (edge1 != null) {
+ edge2 = edgeMap.get(edge1.getTailNodeConnector());
+ if ((edge2 != null) && node.equals(edge2.getTailNodeConnector().getNode())) {
+ peerConnector = edge2.getHeadNodeConnector();
+ }
+ }
+
+ removeEdge(nodeConnector, false);
+ removeEdge(peerConnector, isEnabled(peerConnector));
+ }
+
+ removeSet = getRemoveSet(prodMap.keySet(), node);
+ for (NodeConnector nodeConnector : removeSet) {
+ removeProdEdge(nodeConnector);
+ }
+
removeSet = getRemoveSet(readyListHi, node);
readyListHi.removeAll(removeSet);
holdTime.remove(nodeConnector);
}
- removeSet = getRemoveSet(edgeMap.keySet(), node);
+ removeSet = getRemoveSet(elapsedTime.keySet(), node);
for (NodeConnector nodeConnector : removeSet) {
- removeEdge(nodeConnector, false);
- }
-
- removeSet = getRemoveSet(prodMap.keySet(), node);
- for (NodeConnector nodeConnector : removeSet) {
- removeProdEdge(nodeConnector);
+ elapsedTime.remove(nodeConnector);
}
}
private void removeDiscovery(NodeConnector nodeConnector) {
- readyListHi.remove(nodeConnector);
- readyListLo.remove(nodeConnector);
- stagingList.remove(nodeConnector);
- holdTime.remove(nodeConnector);
+ removeNodeConnector(nodeConnector);
removeEdge(nodeConnector, false);
removeProdEdge(nodeConnector);
}
for (NodeConnector nodeConnector : retrySet) {
// Allow one more retry
- readyListLo.add(nodeConnector);
elapsedTime.remove(nodeConnector);
if (connectionOutService.isLocal(nodeConnector.getNode())) {
transmitQ.add(nodeConnector);
}
elapsedTime.remove(src);
+ // fast discovery of the edge in reverse direction
+ if (!edgeMap.containsKey(dst) && !readyListHi.contains(dst) && !elapsedTime.keySet().contains(dst)) {
+ moveToReadyListHi(dst);
+ }
+
// notify
updateEdge(edge, UpdateType.ADDED, props);
logger.trace("Add edge {}", edge);
* Remove OpenFlow edge
*/
private void removeEdge(NodeConnector nodeConnector, boolean stillEnabled) {
- holdTime.remove(nodeConnector);
- readyListLo.remove(nodeConnector);
- readyListHi.remove(nodeConnector);
+ if (nodeConnector == null) {
+ return;
+ }
+
+ removeNodeConnector(nodeConnector);
if (stillEnabled) {
// keep discovering
- if (!stagingList.contains(nodeConnector)) {
- stagingList.add(nodeConnector);
- }
- } else {
- // stop it
- stagingList.remove(nodeConnector);
+ stagingList.add(nodeConnector);
}
Edge edge = null;
if (val != null) {
try {
- int ticks = Integer.parseInt(val);
+ int ticks;
+ Set<NodeConnector> monitorSet = holdTime.keySet();
+ if (monitorSet != null) {
+ for (NodeConnector nodeConnector : monitorSet) {
+ holdTime.put(nodeConnector, 0);
+ }
+ }
+
+ ticks = Integer.parseInt(val);
DiscoveryPeriod.INTERVAL.setTick(ticks);
discoveryBatchRestartTicks = getDiscoveryInterval();
discoveryBatchPauseTicks = getDiscoveryPauseInterval();
bulkNotifyQ.add(containerName);
}
+ /**
+ * Retrieve/construct edge map for a given container
+ *
+ * @param containerName
+ * the container name
+ * @return the edges and their properties
+ */
+ private Map<NodeConnector, Pair<Edge, Set<Property>>> getEdgeMap(String containerName) {
+ Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropMap = null;
+
+ /*
+ * When container is freshly created, need to construct map based on global map.
+ */
+ edgePropMap = edgeMap.get(containerName);
+
+ return edgePropMap;
+ }
+
/**
* Reading the current topology database, the method will replay all the
* edge updates for the ITopologyServiceShimListener instance in the given
* container, which will in turn publish them toward SAL.
*
* @param containerName
+ * the container name
*/
private void TopologyBulkUpdate(String containerName) {
Map<NodeConnector, Pair<Edge, Set<Property>>> edgePropMap = null;
logger.debug("Try bulk update for container:{}", containerName);
- edgePropMap = edgeMap.get(containerName);
+ edgePropMap = getEdgeMap(containerName);
if (edgePropMap == null) {
logger.debug("No edges known for container:{}", containerName);
return;
* are done by the ComponentActivatorAbstractBase.
*
*/
+ @Override
public void init() {
Node.NodeIDType.registerIDType("STUB", Integer.class);
NodeConnector.NodeConnectorIDType.registerIDType("STUB", Integer.class, "STUB");
* ComponentActivatorAbstractBase
*
*/
+ @Override
public void destroy() {
Node.NodeIDType.unRegisterIDType("STUB");
NodeConnector.NodeConnectorIDType.unRegisterIDType("STUB");
* instantiated in order to get an fully working implementation
* Object
*/
+ @Override
public Object[] getImplementations() {
Object[] res = { ReadService.class, InventoryService.class };
return res;
* 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(ReadService.class)) {
// export the service to be used by SAL
}
}
+ @Override
public Object[] getGlobalImplementations() {
- Object[] res = { FlowProgrammerService.class, StubNodeFactory.class, StubNodeConnectorFactory.class };
+ Object[] res =
+ {
+ FlowProgrammerService.class,
+ StubNodeFactory.class,
+ StubNodeConnectorFactory.class,
+ InventoryService.class };
return res;
}
+ @Override
public void configureGlobalInstance(Component c, Object imp){
if (imp.equals(FlowProgrammerService.class)) {
// export the service to be used by SAL
props.put("protocolName", "STUB");
c.setInterface(INodeConnectorFactory.class.getName(), props);
}
-
+ if (imp.equals(InventoryService.class)) {
+ // export the service to be used by SAL
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ // Set the protocolPluginType property which will be used
+ // by SAL
+ props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), "STUB");
+ props.put("scope", "Global");
+ c.setInterface(IPluginInInventoryService.class.getName(), props);
+ c.add(createServiceDependency().setService(IPluginOutInventoryService.class, "(scope=Global)")
+ .setCallbacks("setPluginOutInventoryServices", "unsetPluginOutInventoryServices")
+ .setRequired(true));
+ }
}
}
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
import org.apache.felix.dm.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import org.opendaylight.controller.sal.core.Actions;
import org.opendaylight.controller.sal.core.Bandwidth;
import org.opendaylight.controller.sal.core.Buffers;
import org.opendaylight.controller.sal.core.State;
import org.opendaylight.controller.sal.core.Tables;
import org.opendaylight.controller.sal.core.TimeStamp;
+import org.opendaylight.controller.sal.core.UpdateType;
import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
+import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
import org.opendaylight.controller.sal.utils.NodeCreator;
import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
// global
// container
// only
+ private final Set<IPluginOutInventoryService> pluginOutInventoryServices =
+ new CopyOnWriteArraySet<IPluginOutInventoryService>();
+
+ public void setPluginOutInventoryServices(IPluginOutInventoryService service) {
+ logger.trace("Got a service set request {}", service);
+ if (this.pluginOutInventoryServices != null) {
+ this.pluginOutInventoryServices.add(service);
+ }
+ }
+
+ public void unsetPluginOutInventoryServices(IPluginOutInventoryService service) {
+ logger.trace("Got a service UNset request");
+ if (this.pluginOutInventoryServices != null) {
+ this.pluginOutInventoryServices.remove(service);
+ }
+ }
/**
* Function called by the dependency manager when all the required
void start() {
}
+ /**
+ * Method called when the plugin has exposed it's services, this will be
+ * used to publish the updates so connection manager can think the
+ * connection is local
+ */
+ void started() {
+ // update sal and discovery
+ for (IPluginOutInventoryService service : pluginOutInventoryServices) {
+ for (Node node : nodeProps.keySet()) {
+ Set<Property> props = new HashSet<Property>(nodeProps.get(node)
+ .values());
+ service.updateNode(node, UpdateType.ADDED, props);
+ logger.trace("Adding Node {} with props {}", node, props);
+ }
+ for (NodeConnector nc : nodeConnectorProps.keySet()) {
+ Set<Property> props = new HashSet<Property>(nodeConnectorProps.get(nc)
+ .values());
+ service.updateNodeConnector(nc, UpdateType.ADDED, props);
+ logger.trace("Adding NodeConnectors {} with props {}", nc, props);
+ }
+ }
+ }
+
/**
* Function called by the dependency manager before the services exported by
* the component are unregistered, this will be followed by a "destroy ()"
*
*/
void stop() {
+ pluginOutInventoryServices.clear();
}
/**
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.controller.topologymanager.ITopologyManager;
import org.opendaylight.controller.topologymanager.ITopologyManagerClusterWideAware;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
public class Activator extends ComponentActivatorAbstractBase {
public List<String> getRoles();
/**
- * Returns the application role level for the specified role
- * If the role is not known to this application <code>NOUSER<code>
- * will be returned as specified in {@link AppRoleLevel}
+ * Returns the application role level for the specified role. If the role is
+ * not known to this application NOUSER will be returned as specified in
+ * {@link AppRoleLevel}
*
- * @param roleName the role name to query
- * @return the application level of the given role in the application context as specified by {@link AppRoleLevel}
- * if the role is not part of this application's roles, <code>NOUSER<code> is returned
+ * @param roleName
+ * the role name to query
+ * @return the application level of the given role in the application
+ * context as specified by {@link AppRoleLevel}. If the role is not
+ * part of this application's roles, NOUSER is returned.
*/
public AppRoleLevel getApplicationRoleLevel(String roleName);
/**
* Unassign the passed resource group from the specified role
*
- * @param group
- * @param role
+ * @param groupName the name of the resource group
+ * @param role the role name
* @return the status of the request
*/
- public Status unassignResourceGroupFromRole(String group, String role);
+ public Status unassignResourceGroupFromRole(String groupName, String role);
/**
* Returns the list of resource groups the given Role is authorized to use
* The returning object expresses the resource group name and the access
* its privilege for the given user role
*
- * @param role
+ * @param role the role name
* @return list of resources
*/
public List<ResourceGroup> getAuthorizedGroups(String role);
/**
* Returns the list of authorized resources for the given role
* For each resource only the highest privilege occurrence is returned
- * @param role
+ * @param role the role name
* @return the list of Resource
*/
public List<Resource> getAuthorizedResources(String role);
* Returns the highest privilege that the user has on the specified
* resource in this application context
*
- * @param userName
- * @param resource
+ * @param userName the user name
+ * @param resource the given resource
* @return the privilege the user has on the passed resource
*/
public Privilege getResourcePrivilege(String userName, Object resource);
@XmlRootElement
public class Actions extends Property {
private static final long serialVersionUID = 1L;
- @XmlElement
+ @XmlElement(name="value")
private int actionsValue;
public enum ActionType {
this.actionsValue = 0;
}
+ @Override
public Actions clone() {
return new Actions(this.actionsValue);
}
public class Bandwidth extends Property {
private static final long serialVersionUID = 1L;
- @XmlElement
+ @XmlElement(name="value")
protected long bandwidthValue;
public static final long BWUNK = 0;
super(name);
}
+ @Override
public Bandwidth clone() {
return new Bandwidth(this.bandwidthValue);
}
@XmlRootElement
public class Buffers extends Property {
private static final long serialVersionUID = 1L;
- @XmlElement
+ @XmlElement(name="value")
private int buffersValue;
public static final String BuffersPropName = "buffers";
this.buffersValue = 0;
}
+ @Override
public Buffers clone() {
return new Buffers(this.buffersValue);
}
@XmlRootElement
public class Capabilities extends Property {
private static final long serialVersionUID = 1L;
- @XmlElement
+ @XmlElement(name="value")
private int capabilitiesValue;
public enum CapabilitiesType {
this.capabilitiesValue = 0;
}
+ @Override
public Capabilities clone() {
return new Capabilities(this.capabilitiesValue);
}
@XmlRootElement
@SuppressWarnings("serial")
public class Config extends Property {
- @XmlElement
+ @XmlElement(name="value")
private short configValue;
public static final short ADMIN_DOWN = 0;
this.configValue = config;
}
+ @Override
public Config clone() {
return new Config(this.configValue);
}
@XmlRootElement
@SuppressWarnings("serial")
public class Description extends Property {
- @XmlElement
+ @XmlElement(name="value")
private String descriptionValue;
public static final String propertyName = "description";
this.descriptionValue = description;
}
+ @Override
public Description clone() {
return new Description(this.descriptionValue);
}
@XmlRootElement
@SuppressWarnings("serial")
public class ForwardingMode extends Property {
- @XmlElement
+ @XmlElement(name="value")
private final int modeValue;
public static final int REACTIVE_FORWARDING = 0;
public static final int PROACTIVE_FORWARDING = 1;
package org.opendaylight.controller.sal.core;
/**
- * @file IContainerAware.java
- *
- * @brief Define the interface to be called when the Container is being
+ * The interface describes methods to be called when the Container is being
* created/destroyed
- *
- *
*/
public interface IContainerAware {
package org.opendaylight.controller.sal.core;
/**
- *
- * Interface used to retrieve the status of a given Container
+ * The interface describes methods used to retrieve the status of a given
+ * Container
*/
public interface IContainerListener {
/**
/**
* Notification raised when the container flow layout changes
*
- * @param containerName container for which the update has been raised
- * @param previousFlow previous value of the container flow under
- * update, differs from the currentFlow only and only if it's an
- * update operation
- * @param currentFlow current version of the container flow differs from
- * the previousFlow only in case of update
- * @param t type of update
+ * @param containerName
+ * container for which the update has been raised
+ * @param previousFlow
+ * previous value of the container flow
+ * {@link org.opendaylight.controller.sal.core.ContainerFlow}
+ * under update, differs from the currentFlow only and only if
+ * it's an update operation
+ * @param currentFlow
+ * current version of the container flow
+ * {@link org.opendaylight.controller.sal.core.ContainerFlow}
+ * differs from the previousFlow only in case of update
+ * @param t
+ * type of update
+ * {@link org.opendaylight.controller.sal.core.UpdateType}
*/
public void containerFlowUpdated(String containerName,
ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t);
/**
- * Notification raised when a NodeConnector is added or removed in
- * the container.
+ * Notification raised when a NodeConnector is added or removed in the
+ * container.
*
- * @param containerName container for which the update has been raised
- * @param p NodeConnector being updated
- * @param t type of modification, but among the types the modify
- * operation is not expected to be raised because the
- * nodeConnectors are anyway immutable so this is only used to
- * add/delete
+ * @param containerName
+ * container for which the update has been raised
+ * @param p
+ * NodeConnector
+ * {@link org.opendaylight.controller.sal.core.NodeConnector}
+ * being updated
+ * @param t
+ * type of modification
+ * {@link org.opendaylight.controller.sal.core.UpdateType}, but
+ * among the types the modify operation is not expected to be
+ * raised because the nodeConnectors are anyway immutable so this
+ * is only used to add/delete
*/
public void nodeConnectorUpdated(String containerName, NodeConnector p,
UpdateType t);
/**
- * Notification raised when the container mode has changed
- * This notification is needed for some bundle in the default container
- * to cleanup some HW state when switching from non-slicing to
- * slicing case and vice-versa
+ * Notification raised when the container mode has changed This notification
+ * is needed for some bundle in the default container to cleanup some HW
+ * state when switching from non-slicing to slicing case and vice-versa
*
- * @param t ADDED when first container is created, REMOVED when last container is removed
+ * @param t
+ * type of modification
+ * {@link org.opendaylight.controller.sal.core.UpdateType}. ADDED
+ * when first container is created, REMOVED when last container
+ * is removed
*/
public void containerModeUpdated(UpdateType t);
}
public class Latency extends Property {
private static final long serialVersionUID = 1L;
- @XmlElement
+ @XmlElement(name="value")
private long latencyValue;
public static final long LATENCYUNK = 0;
this.latencyValue = (long) latency;
}
+ @Override
public Latency clone() {
return new Latency(this.latencyValue);
}
@XmlAccessorType(XmlAccessType.NONE)
public class MacAddress extends Property implements Cloneable {
private static final long serialVersionUID = 1L;
- @XmlElement(name="macAddress")
+ @XmlElement(name="value")
private final String address;
public static final String name = "macAddress";
@XmlRootElement
@SuppressWarnings("serial")
public class Name extends Property {
- @XmlElement
+ @XmlElement(name="value")
private String nameValue;
public static final String NamePropName = "name";
this.nameValue = name;
}
+ @Override
public Name clone() {
return new Name(this.nameValue);
}
/**
* Private constructor used for JAXB mapping
*/
+ @SuppressWarnings("unused")
private NodeTable() {
this.nodeTableIDString = null;
this.nodeTableID = null;
*/
@XmlAttribute(name = "id")
public String getNodeTableIDString() {
- return this.nodeTableIDString.toString();
+ return this.nodeTableIDString != null? this.nodeTableIDString : nodeTableID.toString();
}
/**
@XmlRootElement
@SuppressWarnings("serial")
public class State extends Property {
- @XmlElement
+ @XmlElement(name="value")
private short stateValue;
public static final short EDGE_DOWN = 0;
this.stateValue = state;
}
+ @Override
public State clone() {
return new State(this.stateValue);
}
@XmlRootElement
public class Tables extends Property {
private static final long serialVersionUID = 1L;
- @XmlElement
+ @XmlElement(name="value")
private byte tablesValue;
public static final String TablesPropName = "tables";
this.tablesValue = 0;
}
+ @Override
public Tables clone() {
return new Tables(this.tablesValue);
}
@XmlRootElement
@SuppressWarnings("serial")
public class Tier extends Property {
- @XmlElement
+ @XmlElement(name="value")
private int tierValue;
public static final String TierPropName = "tier";
this.tierValue = 0;
}
+ @Override
public Tier clone() {
return new Tier(this.tierValue);
}
@XmlRootElement
public class TimeStamp extends Property {
private static final long serialVersionUID = 1L;
- @XmlElement
+ @XmlElement(name = "value")
private long timestamp;
- @XmlElement
+ @XmlElement(name = "name")
private String timestampName;
public static final String TimeStampPropName = "timeStamp";
this.timestampName = null;
}
+ @Override
public TimeStamp clone() {
return new TimeStamp(this.timestamp, this.timestampName);
}
import org.opendaylight.controller.sal.utils.Status;
/**
- * @file IPluginOutFlowProgrammer.java
- *
- * @brief Flow programmer interface to be implemented by protocol plugins
+ * This interface defines the flow programmer methods to be implemented by
+ * protocol plugins
*/
public interface IPluginInFlowProgrammerService {
/**
* Synchronously add a flow to the network node
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node} on which the
+ * flow got added
* @param flow
+ * the flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * that got added
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status addFlow(Node node, Flow flow);
* Synchronously modify existing flow on the switch
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node} on which the
+ * flow got modified
* @param flow
+ * the flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * that got modified
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status modifyFlow(Node node, Flow oldFlow, Flow newFlow);
* Synchronously remove the flow from the network node
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node} on which the
+ * flow got removed
* @param flow
+ * the flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * that got removed
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status removeFlow(Node node, Flow flow);
* Asynchronously add a flow to the network node
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node} on which the
+ * flow got added
* @param flow
+ * the flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * that got added
* @param rid
+ * the request id
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status addFlowAsync(Node node, Flow flow, long rid);
* Asynchronously modify existing flow on the switch
*
* @param node
- * @param flow
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node} on which the
+ * flow got modified
+ * @param oldFlow
+ * the original flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * @param newFlow
+ * the new flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
* @param rid
+ * the request id
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow, long rid);
* Asynchronously remove the flow from the network node
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node} on which the
+ * flow got removed
* @param flow
+ * the flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * that got removed
* @param rid
+ * the request id
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status removeFlowAsync(Node node, Flow flow, long rid);
* Remove all flows present on the network node
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node} on which the
+ * flow got removed
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status removeAllFlows(Node node);
* Barrier reply arrives.
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status syncSendBarrierMessage(Node node);
* Send Barrier message asynchronously. The caller is not blocked.
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return Status the operation status
+ * {@link org.opendaylight.controller.sal.utils.Status}
*/
Status asyncSendBarrierMessage(Node node);
}
/**
* Check whether the current match conflicts with the passed filter match
* This match conflicts with the filter if for at least a MatchType defined
- * in the filter match, the respective MatchFields differ or are not compatible
+ * in the filter match, the respective MatchFields differ or are not
+ * compatible
+ *
+ * In other words the function returns true if the set of packets described
+ * by one match and the set of packets described by the other match are
+ * disjoint. Equivalently, if the intersection of the two sets of packets
+ * described by the two matches is an empty.
*
* For example, Let's suppose the filter has the following MatchFields:
* DL_TYPE = 0x800
- * NW_DST = 172.20.30.110/24
+ * NW_DST = 172.20.30.110/24
*
* while this match has the following MatchFields:
+ * DL_TYPE = 0x800
* NW_DST = 172.20.30.45/24
* TP_DST = 80
*
- * Then the function would return false as the two Match are not conflicting
+ * Then the function would return false as the two Match are not
+ * conflicting.
*
- * Note: the mask value is taken into account only for MatchType.NW_SRC and MatchType.NW_DST
+ * Note: the mask value is taken into account only for MatchType.NW_SRC and
+ * MatchType.NW_DST
*
- * @param match the MAtch describing the filter
- * @return true if the match is conflicting with the filter, false otherwise
+ * @param match
+ * the Match describing the filter
+ * @return true if the set of packets described by one match and the set of
+ * packets described by the other match are disjoint, false
+ * otherwise
*/
public boolean conflictWithFilter(Match filter) {
- // Iterate through the MatchType defined in the filter
- for (MatchType type : filter.getMatchesList()) {
+ return !this.intersetcs(filter);
+ }
+
+ /**
+ * Merge the current Match fields with the fields of the filter Match. A
+ * check is first run to see if this Match is compatible with the filter
+ * Match. If it is not, the merge is not attempted.
+ *
+ * The result is the match object representing the intersection of the set
+ * of packets described by this match with the set of packets described by
+ * the filter match. If the intersection of the two sets is empty, the
+ * return match will be null.
+ *
+ * @param filter
+ * the match with which attempting the merge
+ * @return a new Match object describing the set of packets represented by
+ * the intersection of this and the filter matches. null if the
+ * intersection is empty.
+ */
+ public Match mergeWithFilter(Match filter) {
+ return this.getIntersection(filter);
+ }
+
+ /**
+ * Return the match representing the intersection of the set of packets
+ * described by this match with the set of packets described by the other
+ * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and
+ * m.getIntersection(o) == o where u is an empty match (universal set, all
+ * packets) and o is the null match (empty set).
+ *
+ * @param other
+ * the match with which computing the intersection
+ * @return a new Match object representing the intersection of the set of
+ * packets described by this match with the set of packets described
+ * by the other match. null when the intersection is the empty set.
+ */
+ public Match getIntersection(Match other) {
+ // If no intersection, return the empty set
+ if (!this.intersetcs(other)) {
+ return null;
+ }
+ // Check if any of the two is the universal match
+ if (this.getMatches() == 0) {
+ return other.clone();
+ }
+ if (other.getMatches() == 0) {
+ return this.clone();
+ }
+ // Derive the intersection
+ Match intersection = new Match();
+ for (MatchType type : MatchType.values()) {
+ if (this.isAny(type) && other.isAny(type)) {
+ continue;
+ }
if (this.isAny(type)) {
+ intersection.setField(other.getField(type).clone());
+ continue;
+ } else if (other.isAny(type)) {
+ intersection.setField(this.getField(type).clone());
+ continue;
+ }
+ // Either they are equal or it is about IP address
+ switch (type) {
+ // When it is about IP address, take the wider prefix address
+ // between the twos
+ case NW_SRC:
+ case NW_DST:
+ MatchField thisField = this.getField(type);
+ MatchField otherField = other.getField(type);
+ InetAddress thisAddress = (InetAddress) thisField.getValue();
+ InetAddress otherAddress = (InetAddress) otherField.getValue();
+ InetAddress thisMask = (InetAddress) thisField.getMask();
+ InetAddress otherMask = (InetAddress) otherField.getMask();
+
+ int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(thisMask);
+ int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(otherMask);
+ if (otherMaskLen < thisMaskLen) {
+ intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(otherAddress, otherMaskLen),
+ otherMask));
+ } else {
+ intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(thisAddress, thisMaskLen),
+ thisMask));
+ }
+ break;
+ default:
+ // this and other match field are equal for this type, pick this
+ // match field
+ intersection.setField(this.getField(type).clone());
+ }
+ }
+ return intersection;
+ }
+
+ /**
+ * Checks whether the intersection of the set of packets described by this
+ * match with the set of packets described by the other match is non empty
+ *
+ * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22
+ *
+ * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4
+ *
+ * then their respective matching packets set intersection is non empty:
+ * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4
+ *
+ * @param other
+ * the other match with which testing the intersection
+ * @return true if the intersection of the respective matching packets sets
+ * is non empty
+ */
+ public boolean intersetcs(Match other) {
+ // No intersection with the empty set
+ if (other == null) {
+ return false;
+ }
+ // Always intersection with the universal set
+ if (this.getMatches() == 0 || other.getMatches() == 0) {
+ return true;
+ }
+ // Iterate through the MatchType defined in the filter
+ for (MatchType type : MatchType.values()) {
+ if (this.isAny(type) || other.isAny(type)) {
continue;
}
MatchField thisField = this.getField(type);
- MatchField filterField = filter.getField(type);
+ MatchField otherField = other.getField(type);
switch (type) {
case DL_SRC:
case DL_DST:
- if (Arrays.equals((byte[]) thisField.getValue(),
- (byte[]) filterField.getValue())) {
+ if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) {
return false;
}
break;
case NW_SRC:
case NW_DST:
InetAddress thisAddress = (InetAddress) thisField.getValue();
- InetAddress filterAddress = (InetAddress) filterField
- .getValue();
+ InetAddress otherAddress = (InetAddress) otherField.getValue();
// Validity check
- if (thisAddress instanceof Inet4Address
- && filterAddress instanceof Inet6Address
- || thisAddress instanceof Inet6Address
- && filterAddress instanceof Inet4Address) {
- return true;
+ if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address
+ || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) {
+ return false;
}
InetAddress thisMask = (InetAddress) thisField.getMask();
- InetAddress filterMask = (InetAddress) filterField.getMask();
- // thisAddress has to be in same subnet of filterAddress
- if (NetUtils.inetAddressConflict(thisAddress, filterAddress,
- thisMask, filterMask)) {
- return true;
+ InetAddress otherMask = (InetAddress) otherField.getMask();
+ if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask)
+ && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) {
+ return false;
}
break;
default:
- if (!thisField.getValue().equals(filterField.getValue())) {
- return true;
- }
- }
- //TODO: check v4 v6 incompatibility
- }
- return false;
- }
-
- /**
- * Merge the current Match fields with the fields of the filter Match
- * A check is first run to see if this Match is compatible with the
- * filter Match. If it is not, the merge is not attempted.
- *
- *
- * @param filter
- * @return
- */
- public Match mergeWithFilter(Match filter) {
- if (!this.conflictWithFilter(filter)) {
- /*
- * No conflict with the filter
- * We can copy over the fields which this match does not have
- */
- for (MatchType type : filter.getMatchesList()) {
- if (this.isAny(type)) {
- this.setField(filter.getField(type).clone());
+ if (!thisField.getValue().equals(otherField.getValue())) {
+ return false;
}
}
}
- return this;
+ return true;
}
@Override
@Override
public String toString() {
- return type + "(" + getValueString() + "," + getMaskString() + ")";
+ return (mask == null) ? String.format("%s(%s)", getTypeString(), getValueString()) :
+ String.format("%s(%s,%s)", getTypeString(), getValueString(), getMaskString());
}
@Override
import org.opendaylight.controller.sal.flowprogrammer.Flow;
/**
- * @file IPluginInReadService.java
- *
- * @brief Hardware view interface to be implemented by protocol plugins
- *
- *
- *
+ * The interface defines hardware view read methods to be implemented by protocol plugins
*/
public interface IPluginInReadService {
/**
- * Returns the hardware image for the specified flow on the specified network node
+ * Returns the hardware image for the specified flow on the specified
+ * network node
+ *
* @param node
+ * the network node
* @param flow
- * @return
+ * the target flow
+ * @param cached
+ * specify if entry has to be queried from the cached hardware
+ * information maintained locally or directly from the network
+ * node.
+ * @return The FlowOnNode object containing the information present in
+ * hardware for the passed flow on the specified network node
*/
public FlowOnNode readFlow(Node node, Flow flow, boolean cached);
/**
- * Returns the hardware view of all the flow installed on the specified network node
+ * Returns the hardware view of all the flow installed on the specified
+ * network node
+ *
* @param node
- * @return
+ * the network node
+ * @param cached
+ * specify if entries have to be queried from the cached hardware
+ * information maintained locally or directly from the network
+ * node.
+ * @return The list of FlowOnNode objects containing the information present
+ * in hardware on the specified network node for all its flows
*/
public List<FlowOnNode> readAllFlow(Node node, boolean cached);
/**
- * Returns the description of the network node as provided by the node itself
+ * Returns the description of the network node as provided by the node
+ * itself
+ *
* @param node
- * @return
+ * the network node
+ * @param cached
+ * specify if entry has to be queried from the cached hardware
+ * information maintained locally or directly from the network
+ * node.
+ * @return The NodeDescription object containing the description information
+ * for the specified network node
*/
public NodeDescription readDescription(Node node, boolean cached);
/**
* Returns the hardware view of the specified network node connector
- * @param node
- * @return
+ *
+ * @param connector
+ * the target nodeConnector
+ * @param cached
+ * specify if entry has to be queried from the cached hardware
+ * information maintained locally or directly from the
+ * corresponding network node.
+ * @return The NodeConnectorStatistics object containing the statistics
+ * present in hardware for the corresponding network node port
*/
public NodeConnectorStatistics readNodeConnector(NodeConnector connector,
boolean cached);
/**
- * Returns the hardware info for all the node connectors on the specified network node
+ * Returns the hardware info for all the node connectors on the specified
+ * network node
+ *
* @param node
- * @return
+ * the target node
+ * @param cached
+ * specify if entries have to be queried from the cached hardware
+ * information maintained locally or directly from the
+ * corresponding network node.
+ * @return The list of NodeConnectorStatistics objects containing the
+ * statistics present in hardware for all the network node ports
*/
public List<NodeConnectorStatistics> readAllNodeConnector(Node node,
boolean cached);
/**
* Returns the table statistics for the node
- * @param node
- * @return
+ * @param table
+ * the target network node table
+ * @param cached
+ * specify if entry has to be queried from the cached hardware
+ * information maintained locally or directly from
+ * the corresponding network node.
+ * @return The NodeTableStatistics object containing the statistics present
+ * in hardware for the corresponding network node table
*/
public NodeTableStatistics readNodeTable(NodeTable table, boolean cached);
/**
* Returns all the table statistics for the node
+ *
* @param node
- * @return
+ * the target node
+ * @param cached
+ * specify if entries have to be queried from the cached hardware
+ * information maintained locally or directly from the
+ * corresponding network node.
+ * @return The list of NodeTableStatistics objects containing the statistics
+ * present in hardware for all the network node tables
*/
public List<NodeTableStatistics> readAllNodeTable(Node node, boolean cached);
/**
* Returns the averaged transmit rate for the specified node connector
* @param connector
+ * the target nodeConnector
* @return tx rate [bps]
*/
public long getTransmitRate(NodeConnector connector);
import org.opendaylight.controller.sal.core.Node;
/**
- * @file IPluginOutReadService.java
- *
- * @brief Hardware statistics updates service to be offered by protocol plugins
+ * The interface defines hardware statistics updates service to be offered by
+ * protocol plugins
*/
public interface IPluginOutReadService {
/**
- * Notifies the hardware view of all the flow installed on the specified network node
+ * Notifies the hardware view of all the flow installed on the specified
+ * network node
+ *
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @param flowStatsList
+ * the hardware view of all the flow
+ * {@link org.opendaylight.controller.sal.reader.FlowOnNode}
+ * installed on the specified network node
*/
public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList);
/**
- * Notifies the hardware view of the specified network node connector
+ * Notifies the hardware view of the specified network node
+ *
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @param ncStatsList
+ * the statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeConnectorStatistics}
+ * for all node connectors in a given node
*/
public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList);
/**
* Notifies all the table statistics for a node
+ *
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @param tableStatsList
+ * the statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeTableStatistics}
+ * for all the tables in a given node
*/
public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList);
+
/**
* Notifies the hardware view of node description changes
+ *
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @param nodeDescription
+ * the node description
+ * {@link org.opendaylight.controller.sal.reader.NodeDescription}
*/
public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription );
import org.opendaylight.controller.sal.flowprogrammer.Flow;
/**
- * Interface for retrieving the network node's flow/port/queue hardware view
- *
- *
- *
+ * This interface defines methods for retrieving the network node's
+ * flow/port/queue hardware view
*/
public interface IReadService {
/**
- * Get the hardware view for the specified flow on the specified network node
+ * Get the hardware view for the specified flow on the specified network
+ * node
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
* @param flow
+ * the given flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * @return the flow
+ * {@link org.opendaylight.controller.sal.reader.FlowOnNode}
+ * installed on the node
*/
FlowOnNode readFlow(Node node, Flow flow);
* Caller will be blocked until node replies or request times out
*
* @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
* @param flow
+ * the given flow
+ * {@link org.opendaylight.controller.sal.flowprogrammer.Flow}
+ * @return the flow
+ * {@link org.opendaylight.controller.sal.reader.FlowOnNode}
+ * installed on the node
*/
FlowOnNode nonCachedReadFlow(Node node, Flow flow);
* Get the hardware view for all the flows installed on the network node
*
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return all the flows
+ * {@link org.opendaylight.controller.sal.reader.FlowOnNode}
+ * installed on the node
*/
List<FlowOnNode> readAllFlows(Node node);
* Caller will be blocked until node replies or request times out
*
* @param node
- * @param flow
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return the hardware view of all the flows
+ * {@link org.opendaylight.controller.sal.reader.FlowOnNode}
+ * installed on the node
*/
List<FlowOnNode> nonCachedReadAllFlows(Node node);
/**
* Get the description information for the network node
+ *
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return the node description
+ * {@link org.opendaylight.controller.sal.reader.NodeDescription}
*/
NodeDescription readDescription(Node node);
* Caller will be blocked until node replies or request times out
*
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return the node description
+ * {@link org.opendaylight.controller.sal.reader.NodeDescription}
*/
NodeDescription nonCachedReadDescription(Node node);
/**
* Get the hardware view for the specified node connector
+ *
* @param connector
+ * the given node connector
+ * {@link org.opendaylight.controller.sal.core.NodeConnector}
+ * @return the node connector statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeConnectorStatistics}
*/
NodeConnectorStatistics readNodeConnector(NodeConnector connector);
/**
* Get the hardware view for all the node connectors
* present on the specified network node
- * @param connector
+ *
+ * @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return the statistics for all the node connectors
+ * {@link org.opendaylight.controller.sal.reader.NodeConnectorStatistics}
*/
List<NodeConnectorStatistics> readNodeConnectors(Node node);
/**
* Read the Table statistics for the given node table
+ *
* @param table
+ * the table
+ * {@link org.opendaylight.controller.sal.core.NodeTable}
+ * @return the table statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeTableStatistics}
*/
NodeTableStatistics readNodeTable(NodeTable table);
/**
- * Read the Table statistics for the given node
- * This is not used. Querying all tables on a node is not currently a feature.
- * @param table
+ * Read the Table statistics for the given node This is not used. Querying
+ * all tables on a node is not currently a feature.
+ *
+ * @param node
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return the table statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeTableStatistics}
+ * for all tables in a given node
*/
List<NodeTableStatistics> readNodeTable(Node node);
* Caller will be blocked until the node replies or request times out
*
* @param table
+ * the table
+ * {@link org.opendaylight.controller.sal.core.NodeTable}
+ * @return the table statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeTableStatistics}
*/
NodeTableStatistics nonCachedReadNodeTable(NodeTable table);
* Caller will be blocked until node replies or request times out
*
* @param node
- * @return
+ * the network node
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return the statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeConnectorStatistics}
+ * for all node connectors in a given node
*/
List<NodeConnectorStatistics> nonCachedReadNodeConnectors(Node node);
/**
* Get the node connectors statistics information for the network node
*
- * @param node
- * @return
+ * @param connector
+ * the given node connector
+ * {@link org.opendaylight.controller.sal.core.NodeConnector}
+ * @return the node connector statistics
+ * {@link org.opendaylight.controller.sal.reader.NodeConnectorStatistics}
*/
NodeConnectorStatistics nonCachedReadNodeConnector(NodeConnector connector);
* Get the transmit rate for the specified node connector
*
* @param connector
+ * the given node connector
+ * {@link org.opendaylight.controller.sal.core.NodeConnector}
* @return tx rate [bps]
*/
long getTransmitRate(NodeConnector connector);
package org.opendaylight.controller.sal.reader;
-
/**
- * @file IReadServiceListener.java
- *
- * @brief SAL service to be consumed by functional modules that are interested in reader updates
+ * The interface describes SAL service to be consumed by functional modules that
+ * are interested in reader updates
*/
public interface IReadServiceListener extends IPluginOutReadService {
/**
* This interface provides APIs to manage and query the routing information
- *
+ *
*/
public interface IRouting {
/**
* Returns a Path leading from the source to the destination
- * @param src: source Node
- * @param dst: destination Node
- * @return: Path
+ *
+ * @param src
+ * source {@link org.opendaylight.controller.sal.core.Node}
+ *
+ * @param dst
+ * destination
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return: the {@link org.opendaylight.controller.sal.core.Path}
*/
public Path getRoute(Node src, Node dst);
/**
* Returns a Max ThroughPut Path leading from the source to the destination
- * @param src: source Node
- * @param dst: destination Node
- * @return: MTPath
+ *
+ * @param src
+ * source {@link org.opendaylight.controller.sal.core.Node}
+ *
+ * @param dst
+ * destination
+ * {@link org.opendaylight.controller.sal.core.Node}
+ * @return: the max throughput {@link org.opendaylight.controller.sal.core.Path}
*/
public Path getMaxThroughputRoute(Node src, Node dst);
/**
- * Returns a Path leading from the source to the destination that meets the specified bandwidth
- * @param src: source Node
- * @param dst: destination Node
- * @param Bw: bandwidth
- * @return: Path
+ * Returns a Path leading from the source to the destination that meets the
+ * specified bandwidth
+ *
+ * @param src
+ * source {@link org.opendaylight.controller.sal.core.Node}
+ *
+ * @param dst
+ * destination {@link org.opendaylight.controller.sal.core.Node}
+ * @param Bw
+ * the bandwidth
+ * @return: the {@link org.opendaylight.controller.sal.core.Path}
*/
public Path getRoute(Node src, Node dst, Short Bw);
/**
* Initialization For Max Throughput
- * @param EdgeWeightMap: Map containing Edge and Corresponding
- * Weight. Optional Param - if null, implementation specific weight
- * calculation will be used.
+ *
+ * @param EdgeWeightMap
+ * Map containing
+ * {@link org.opendaylight.controller.sal.core.Edge} and
+ * Corresponding Weight. Optional Param - if null, implementation
+ * specific weight calculation will be used.
*/
public void initMaxThroughput(Map<Edge, Number> EdgeWeightMap);
import org.opendaylight.controller.sal.core.Edge;
/**
- * @file IListenTopoUpdates.java
- *
- * @brief Topology notifications provided by SAL toward the application
- *
- * For example an application that wants to keep up to date with the
- * updates coming from SAL it will register in the OSGi service
- * registry this interface (on a per-container base) and SAL will call it
- * providing the update
- */
-
-/**
- * Topology notifications provided by SAL toward the application
- *
+ * This interface defines the methods for topology notifications provided by SAL
+ * toward the application. For example an application that wants to keep up to
+ * date with the updates coming from SAL it will register in the OSGi service
+ * registry. This interface (on a per-container base) and SAL will call it
+ * providing the update.
*/
public interface IListenTopoUpdates {
/**
* threshold level configured on the controller
*
* @param edge
+ * The edge which bandwidth usage is back to normal
*/
public void edgeUtilBackToNormal(Edge edge);
}
package org.opendaylight.controller.sal.topology;
import java.util.List;
-import java.util.Set;
-
import org.opendaylight.controller.sal.core.Edge;
-import org.opendaylight.controller.sal.core.Property;
-import org.opendaylight.controller.sal.core.UpdateType;
-
-/**
- * @file IPluginOutTopologyService.java
- *
- * @brief Methods that are invoked from Protocol Plugin toward SAL
- *
- * Every time a protocol plugin update the topology, it will call this
- * service provided by SAL so the update can migrate upward toward the
- * applications
- */
/**
- * Methods that are invoked from Protocol Plugin toward SAL
- *
+ * This interface defines the methods that are invoked from Protocol Plugin
+ * toward SAL. Every time a protocol plugin update the topology, it will call
+ * this service provided by SAL so the update can migrate upward toward the
+ * applications.
*/
public interface IPluginOutTopologyService {
* on the controller
*
* @param edge
- */
+ * The edge which bandwidth usage is above the safety level
+ */
public void edgeOverUtilized(Edge edge);
/**
* threshold level configured on the controller
*
* @param edge
+ * The edge which bandwidth usage is back to normal
*/
public void edgeUtilBackToNormal(Edge edge);
}
package org.opendaylight.controller.sal.utils;
+/**
+ * This interface defines the methods for configuration object
+ *
+ */
public interface ConfigurationObject {
}
package org.opendaylight.controller.sal.utils;
+/**
+ * This interface defines the methods for callback ordering
+ *
+ */
+
public interface IListener<T> {
public enum Command {
CONTINUE, STOP
/**
* The name assigned to this listener
*
- * @return
+ * @return the name string
*/
public String getName();
import org.opendaylight.controller.sal.core.NodeConnector;
/**
- * @file INodeFactory.java
- *
- * @brief Define the interface to be called when looking up custom node types
+ * This interface defines the methods to be called when looking up custom node types
*
*/
/**
* Method to get custom NodeConnector types from protocol plugins
*
+ * @param typeStr
+ * {@Link
+ * org.opendaylight.controller.sal.core.NodeConnector} type
+ * string
+ * @param IDStr
+ * {@Link
+ * org.opendaylight.controller.sal.core.NodeConnector} ID string
+ * @return the custom {@Link
+ * org.opendaylight.controller.sal.core.NodeConnector}
*/
public NodeConnector fromStringNoNode(String typeStr, String IDStr,
Node n);
import org.opendaylight.controller.sal.core.Node;
/**
- * @file INodeFactory.java
- *
- * @brief Define the interface to be called when looking up custom node types
+ * This interface defines the methods to be called when looking up custom node types
*
*/
/**
* Method to get custom node types from protocol plugins
*
+ * @param nodeType
+ * {@Link org.opendaylight.controller.sal.core.Node} type
+ * string
+ * @param nodeId
+ * {@Link org.opendaylight.controller.sal.core.Node} ID
+ * string
+ * @return the custom {@Link
+ * org.opendaylight.controller.sal.core.Node}
*/
public Node fromString(String nodeType, String nodeId);
}
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
* Checks if the test address and mask conflicts with the filter address and
* mask
*
- * For example: testAddress: 172.28.2.23 testMask: 255.255.255.0
- * filtAddress: 172.28.1.10 testMask: 255.255.255.0 conflict
+ * For example:
+ * testAddress: 172.28.2.23
+ * testMask: 255.255.255.0
+ * filterAddress: 172.28.1.10
+ * testMask: 255.255.255.0
+ * do conflict
*
- * testAddress: 172.28.2.23 testMask: 255.255.255.0 filtAddress: 172.28.1.10
- * testMask: 255.255.0.0 do not conflict
+ * testAddress: 172.28.2.23
+ * testMask: 255.255.255.0
+ * filterAddress: 172.28.1.10
+ * testMask: 255.255.0.0
+ * do not conflict
*
* Null parameters are permitted
*
int testMaskLen = (testMask == null) ? ((testAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
.getSubnetMaskLength(testMask);
- int filterMaskLen = NetUtils.getSubnetMaskLength(filterMask);
+ int filterMaskLen = (filterMask == null) ? ((testAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(filterMask);
// Mask length check. Test mask has to be more specific than filter one
if (testMaskLen < filterMaskLen) {
/*
* Following utilities are useful when you need to compare or bit shift java
- * primitive type variable which are inerently signed
+ * primitive type variable which are inherently signed
*/
/**
* Returns the unsigned value of the passed byte variable
return null;
}
}
+
+ /**
+ * Returns Broadcast MAC Address
+ *
+ * @return the byte array containing broadcaset mac address
+ */
+ public static byte[] getBroadcastMACAddr() {
+ return Arrays.copyOf(BroadcastMACAddr, BroadcastMACAddr.length);
+ }
+
}
Assert.assertTrue(field.getValue().equals(new Short(vlan)));
Assert.assertTrue(field.isValid());
}
+
+ @Test
+ public void testIntersection() throws UnknownHostException {
+ Short ethType = Short.valueOf((short)0x800);
+ InetAddress ip1 = InetAddress.getByName("1.1.1.1");
+ InetAddress ip2 = InetAddress.getByName("1.1.1.0");
+ InetAddress ipm2 = InetAddress.getByName("255.255.255.0");
+ InetAddress ip3 = InetAddress.getByName("1.3.0.0");
+ InetAddress ipm3 = InetAddress.getByName("255.255.0.0");
+
+ Match m1 = new Match();
+ m1.setField(MatchType.DL_TYPE, ethType);
+ m1.setField(MatchType.NW_SRC, ip1);
+
+ Match m2 = new Match();
+ m2.setField(MatchType.DL_TYPE, ethType);
+ m2.setField(MatchType.NW_SRC, ip2, ipm2);
+
+ Match m3 = new Match();
+ m3.setField(MatchType.DL_TYPE, ethType);
+ m3.setField(MatchType.NW_SRC, ip3, ipm3);
+ m3.setField(MatchType.NW_PROTO, IPProtocols.TCP.byteValue());
+
+ Match m3r = m3.reverse();
+ Assert.assertTrue(m3.intersetcs(m3r));
+
+ Assert.assertTrue(m1.intersetcs(m2));
+ Assert.assertTrue(m2.intersetcs(m1));
+ Assert.assertFalse(m1.intersetcs(m3));
+ Assert.assertTrue(m1.intersetcs(m3r));
+ Assert.assertFalse(m3.intersetcs(m1));
+ Assert.assertTrue(m3.intersetcs(m1.reverse()));
+ Assert.assertFalse(m2.intersetcs(m3));
+ Assert.assertFalse(m3.intersetcs(m2));
+ Assert.assertTrue(m2.intersetcs(m3r));
+
+
+ Match i = m1.getIntersection(m2);
+ Assert.assertTrue(((Short)i.getField(MatchType.DL_TYPE).getValue()).equals(ethType));
+ Assert.assertTrue(((InetAddress)i.getField(MatchType.NW_SRC).getValue()).equals(ip2));
+ Assert.assertTrue(((InetAddress)i.getField(MatchType.NW_SRC).getMask()).equals(ipm2));
+
+ // Empty set
+ i = m2.getIntersection(m3);
+ Assert.assertNull(i);
+
+ Match m4 = new Match();
+ m4.setField(MatchType.DL_TYPE, ethType);
+ m4.setField(MatchType.NW_PROTO, IPProtocols.TCP.byteValue());
+ Assert.assertTrue(m4.intersetcs(m3));
+
+ Match m5 = new Match();
+ m5.setField(MatchType.DL_TYPE, ethType);
+ m3.setField(MatchType.NW_SRC, ip3, ipm3);
+ m5.setField(MatchType.NW_PROTO, IPProtocols.UDP.byteValue());
+ Assert.assertFalse(m5.intersetcs(m3));
+ Assert.assertFalse(m5.intersetcs(m4));
+ Assert.assertTrue(m5.intersetcs(m5));
+ Assert.assertFalse(m3.intersetcs(m5));
+ Assert.assertFalse(m4.intersetcs(m5));
+
+
+ Match i2 = m4.getIntersection(m3);
+ Assert.assertFalse(i2.getMatches() == 0);
+ Assert.assertFalse(i2.getMatchesList().isEmpty());
+ Assert.assertTrue(((InetAddress)i2.getField(MatchType.NW_SRC).getValue()).equals(ip3));
+ Assert.assertTrue(((InetAddress)i2.getField(MatchType.NW_SRC).getMask()).equals(ipm3));
+ Assert.assertTrue(((Byte)i2.getField(MatchType.NW_PROTO).getValue()).equals(IPProtocols.TCP.byteValue()));
+
+ byte src[] = {(byte)0, (byte)0xab,(byte)0xbc,(byte)0xcd,(byte)0xde,(byte)0xef};
+ byte dst[] = {(byte)0x10, (byte)0x11,(byte)0x12,(byte)0x13,(byte)0x14,(byte)0x15};
+ Short srcPort = (short)1024;
+ Short dstPort = (short)80;
+
+ // Check identity
+ Match m6 = new Match();
+ m6.setField(MatchType.DL_SRC, src);
+ m6.setField(MatchType.DL_DST, dst);
+ m6.setField(MatchType.NW_SRC, ip2, ipm2);
+ m6.setField(MatchType.NW_DST, ip3, ipm3);
+ m6.setField(MatchType.NW_PROTO, IPProtocols.UDP.byteValue());
+ m6.setField(MatchType.TP_SRC, srcPort);
+ m6.setField(MatchType.TP_DST, dstPort);
+ Assert.assertTrue(m6.intersetcs(m6));
+ Assert.assertTrue(m6.getIntersection(m6).equals(m6));
+
+ // Empty match, represents the universal set (all packets)
+ Match u = new Match();
+ Assert.assertTrue(m6.getIntersection(u).equals(m6));
+ Assert.assertTrue(u.getIntersection(m6).equals(m6));
+
+ // No intersection with null match, empty set
+ Assert.assertNull(m6.getIntersection(null));
+ }
}
* plugins and is an opaque value for SAL. Typical values keyed inside this params are
* Management IP-Address, Username, Password, Security Keys, etc...
*
- * @return Node
+ * @return Node {@link org.opendaylight.controller.sal.core.Node}
*/
public Node connect (String type, String connectionIdentifier, Map<ConnectionConstants, String> params);
-
/**
* Discover the node type and Connect to the first plugin that is able to connect with the specified parameters.
*
* plugins and is an opaque value for SAL. Typical values keyed inside this params are
* Management IP-Address, Username, Password, Security Keys, etc...
*
- * @return Node
+ * @return Node {@link org.opendaylight.controller.sal.core.Node}
*/
public Node connect (String connectionIdentifier, Map<ConnectionConstants, String> params);
* Disconnect a Node that is connected to this Controller.
*
* @param node
- * @param flow
+ * the node {@link org.opendaylight.controller.sal.core.Node}
+ * @return Status {@link org.opendaylight.controller.sal.utils.Status}
*/
public Status disconnect(Node node);
/**
* View Change notification
+ *
+ * @param node
+ * the node {@link org.opendaylight.controller.sal.core.Node}
*/
public void notifyNodeDisconnectFromMaster(Node node);
import org.opendaylight.controller.sal.utils.Status;
/**
- * @file IPluginInConnectionService.java
- *
- * @brief Connection interface to be implemented by protocol plugins
+ * The interface describes methods to be implemented by protocol plugins
*/
public interface IPluginInConnectionService {
/**
* Disconnect a Node that is connected to this Controller.
*
* @param node
- * @param flow
+ * the given node {@link org.opendaylight.controller.sal.core.Node}
*/
public Status disconnect(Node node);
/**
* Node Disconnected from the node's master controller.
+ *
+ * @param node
+ * the given node {@link org.opendaylight.controller.sal.core.Node}
*/
public void notifyNodeDisconnectFromMaster(Node node);
import org.opendaylight.controller.sal.core.Node;
+/**
+ * The interface describes methods to be implemented by SAL connection service
+ */
public interface IPluginOutConnectionService {
/**
* Method to test if a node is local to a controller.
*
+ * @param node
+ * the given node {@link org.opendaylight.controller.sal.core.Node}
* @return true if node is local to this controller. false otherwise.
*/
public boolean isLocal(Node node);
package org.opendaylight.controller.sal.networkconfig.bridgedomain;
+/**
+ * This interface is just a wrapper of IPluginInBridgeDomainConfigService
+ */
public interface IBridgeDomainConfigService extends IPluginInBridgeDomainConfigService {
}
\ No newline at end of file
import org.opendaylight.controller.sal.utils.Status;
/**
- * @file IPluginInConfigurationService.java
- *
+ * This interface defines bridge domain configuration service methods to be
+ * implemented by protocol plugins
*/
public interface IPluginInBridgeDomainConfigService {
/**
</description>
<modules>
+ <module>sal</module>
<module>concepts-lang</module>
</modules>
</properties>
<pluginRepositories>
+ <pluginRepository>
+ <id>central</id>
+ <name>central</name>
+ <url>${nexusproxy}/repositories/central/</url>
+ </pluginRepository>
<pluginRepository>
<id>central2</id>
<name>central2</name>
--- /dev/null
+<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">
+
+ <parent>
+ <artifactId>model-parent</artifactId>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>model-flow-statistics</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>model-inventory</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>model-flow</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ <packaging>bundle</packaging>
+</project>
\ No newline at end of file
--- /dev/null
+module opendaylight-flow-statistics {
+ namespace "urn:opendaylight:flow:statistics";
+ prefix flowstat;
+
+ import yang-ext {prefix ext;}
+ import ietf-inet-types {prefix inet;}
+ import ietf-yang-types {prefix yang;}
+ import opendaylight-flow {prefix flow;}
+ import opendaylight-inventory {prefix inv;}
+
+ revision "2013-08-19" {
+ description "Initial revision of flow service";
+ }
+
+ augment "/flow:flows/flow:flow" {
+ ext:augment-identifier "flow-statistics";
+
+ leaf packet-count {
+ type uint64;
+ }
+
+ leaf byte-count {
+ type uint64;
+ }
+
+ container duration {
+ leaf second {
+ type uint64;
+ }
+ leaf nanosecond {
+ type uint64;
+ }
+ }
+ }
+
+ augment "/inv:nodes/inv:node/inv:node-connector" {
+ ext:augment-identifier "node-connector-statistics";
+
+ container packets {
+ leaf received {
+ type uint64;
+ }
+ leaf transmitted {
+ type uint64;
+ }
+ }
+ container bytes {
+ leaf received {
+ type uint64;
+ }
+ leaf transmitted {
+ type uint64;
+ }
+ }
+ leaf receive-drops {
+ type uint64;
+ }
+ leaf transmit-drops {
+ type uint64;
+ }
+ leaf receive-errors {
+ type uint64;
+ }
+ leaf transmit-errors {
+ type uint64;
+ }
+ leaf receive-frame-error {
+ type uint64;
+ }
+ leaf receive-over-run-error {
+ type uint64;
+ }
+ leaf receive-crc-error {
+ type uint64;
+ }
+ leaf collision-count {
+ type uint64;
+ }
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<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">
+
+ <parent>
+ <artifactId>model-parent</artifactId>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>model-flow</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>model-inventory</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ <packaging>bundle</packaging>
+</project>
\ No newline at end of file
--- /dev/null
+module opendaylight-flow {
+ namespace "urn:opendaylight:flow:service";
+ prefix flow;
+
+ import yang-ext {prefix ext;}
+ import ietf-inet-types {prefix inet;}
+ import ietf-yang-types {prefix yang;}
+ import opendaylight-inventory {prefix inv;}
+
+ revision "2013-08-19" {
+ description "Initial revision of flow service";
+ }
+
+ /** Base structure **/
+ container flows {
+ list flow {
+ leaf node {
+ type inv:node-id;
+ }
+ container match {
+ // Match is empty
+ leaf input-node-connector {
+ type inv:node-connector-id; //
+ }
+ }
+ list action {
+ key id;
+ leaf id {
+ type string;
+ }
+ choice action {
+
+ }
+ }
+ }
+ }
+
+ /** Matches **/
+ augment "/flows/flow/match" {
+ ext:augment-identifier "ethernet-match";
+ container ethernet-source {
+ description "Ethernet source address.";
+ leaf address {
+ type yang:mac-address;
+ }
+ leaf mask {
+ type binary;
+ }
+ }
+ container ethernet-destination {
+ description "Ethernet destination address.";
+ leaf address {
+ type yang:mac-address;
+ }
+ }
+ container ethernet-type {
+ description "Ethernet frame type.";
+ leaf type {
+ type uint16; // Needs to define that as general model
+ }
+ leaf mask {
+ type binary;
+ }
+ }
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "vlan-match";
+
+ container vlan-id {
+ description "VLAN id.";
+ leaf vlan-id {
+ type uint16; // TODO: Define proper vlan id type.
+ }
+ leaf mask {
+ type binary;
+ }
+ }
+ leaf vlan-pcp {
+ description "VLAN priority.";
+ type uint8; // TODO: Define PCP type
+ }
+
+
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "ip-match";
+
+ leaf ip-protocol {
+ description "IP protocol.";
+ type uint8; // TODO define IP protocol number
+ }
+
+ leaf ip-dscp {
+ description "IP DSCP (6 bits in ToS field).";
+ type inet:dscp; // TODO: Define DSCP type
+ }
+ leaf ip-ecn {
+ description "IP ECN (2 bits in ToS field).";
+ type uint8; // TODO define ECN
+ }
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "ipv4-match";
+ leaf ipv4-source {
+ description "IPv4 source address.";
+ type inet:ipv4-prefix;
+ }
+ leaf ipv4-destination {
+ description "IPv4 destination address.";
+ type inet:ipv4-prefix;
+ }
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "ipv6-match";
+ leaf ipv6-source {
+ description "IPv6 source address.";
+ type inet:ipv6-prefix;
+ }
+ leaf ipv6-destination {
+ description "IPv6 destination address.";
+ type inet:ipv6-prefix;
+ }
+ }
+
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "udp-match";
+
+ leaf udp-source-port {
+ description "UDP source port.";
+ type inet:port-number;
+ }
+ leaf udp-destination-port {
+ description "UDP destination port.";
+ type inet:port-number;
+ }
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "tcp-match";
+ leaf tcp-source-port {
+ description "TCP source port.";
+ type inet:port-number;
+ }
+ leaf tcp-destination-port {
+ description "TCP destination port.";
+ type inet:port-number;
+ }
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "sctp-match";
+ leaf sctp-source-port {
+ description "SCTP source port.";
+ type inet:port-number;
+ }
+ leaf sctp-destination-dst {
+ description "SCTP destination port.";
+ type inet:port-number;
+ }
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "icmpv4-match";
+ leaf icmpv4-type {
+ description "ICMP type.";
+ type uint8; // Define ICMP Type
+ }
+ description "ICMP code.";
+ leaf icmpv4-code {
+ type uint8; // Define ICMP Code
+ }
+ }
+
+ augment "/flows/flow/match" {
+ ext:augment-identifier "arp-match";
+
+ leaf arp-source-transport-address {
+ description "ARP source IPv4 address.";
+ type inet:ipv4-prefix;
+ }
+
+ leaf arp-target-transport-address {
+ description "ARP target IPv4 address.";
+ type inet:ipv4-prefix;
+ }
+ container arp-source-hardware-address {
+ description "ARP source hardware address.";
+ leaf address {
+ type yang:mac-address;
+ }
+ leaf mask {
+ type binary;
+ }
+ }
+ container arp-target-hardware-address {
+ description "ARP target hardware address.";
+ leaf address {
+ type yang:mac-address;
+ }
+ leaf mask {
+ type binary;
+ }
+ }
+ }
+
+ /** Actions **/
+ augment "/flows/flow/action/action" {
+ case output-action {
+ leaf output-node-connector {
+ type string;
+ }
+ }
+
+ case controller-action {
+ leaf max-length {
+ type uint16 {
+ range "0..65294";
+ }
+ }
+ }
+
+ case set-queue-action {
+ leaf queue {
+ type string; // TODO: define queues
+ }
+ }
+
+ case pop-mpls-action {
+ container pop-mpls {
+ leaf ethernet-type {
+ type uint16; // TODO: define ethertype type
+ }
+ }
+ }
+
+ case set-mpls-ttl-action {
+ leaf mpls-ttl {
+ type uint8;
+ }
+ }
+
+ case set-nw-ttl-action {
+ leaf nw-ttl {
+ type uint8;
+ }
+ }
+
+ case push-pbb-action {
+
+ }
+
+ case push-mpls-action {
+
+ }
+
+ case push-vlan-action {
+
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<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">
+
+ <parent>
+ <artifactId>model-parent</artifactId>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>model-inventory</artifactId>
+ <packaging>bundle</packaging>
+</project>
\ No newline at end of file
--- /dev/null
+module opendaylight-inventory {
+ namespace "urn:opendaylight:inventory";
+ prefix flow;
+
+ import yang-ext {prefix ext;}
+ import ietf-inet-types {prefix inet;}
+ import ietf-yang-types {prefix yang;}
+
+
+ revision "2013-08-19" {
+ description "Initial revision of Inventory model";
+ }
+
+ typedef node-id {
+ type inet:uri;
+ }
+
+ typedef node-connector-id {
+ type inet:uri;
+ }
+
+ /** Base structure **/
+ container nodes {
+ list node {
+ key id;
+ leaf id {
+ type node-id;
+ }
+ list node-connector {
+ type node-connector-id;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<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">
+
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <properties>
+ <yang.version>0.5.7-SNAPSHOT</yang.version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.bundle.version>2.4.0</maven.bundle.version>
+ </properties>
+
+ <modules>
+ <module>model-inventory</module>
+ <module>model-flow</module>
+ <module>model-flow-statistics</module>
+ <!-- <module>model-topology-bgp</module> -->
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${maven.bundle.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yang.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ target/generated-sources/sal
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>0.5.7-SNAPSHOT</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>target/generated-sources/sal</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse
+ m2e settings only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>
+ org.opendaylight.yangtools
+ </groupId>
+ <artifactId>
+ yang-maven-plugin
+ </artifactId>
+ <versionRange>
+ [0.5,)
+ </versionRange>
+ <goals>
+ <goal>
+ generate-sources
+ </goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ <version>2010.09.24-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ <version>2010.09.24-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-ext</artifactId>
+ <version>2013.09.07-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+*.yang
\ No newline at end of file
<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>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- <version>1.0-SNAPSHOT</version>
- <packaging>pom</packaging>
+ 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>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
- <modules>
- <module>sal-common</module>
- <module>sal-common-util</module>
- <module>sal-core-api</module>
- <module>sal-data-api</module>
- <module>sal-binding-api</module>
- <module>sal-binding-spi</module>
- <module>sal-binding-broker-impl</module>
- <module>sal-schema-repository-api</module>
- <module>sal-core-spi</module>
- <module>sal-broker-impl</module>
- </modules>
+ <modules>
+ <module>sal-common</module>
+ <module>sal-common-util</module>
+ <module>sal-data-api</module>
+ <module>sal-binding-api</module>
+ <module>sal-binding-broker-impl</module>
+ <module>samples</module>
+ <module>model</module>
+ </modules>
- <properties>
- <yang.version>0.5.5-SNAPSHOT</yang.version>
- </properties>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <slf4j.version>1.7.2</slf4j.version>
+ <nexusproxy>http://nexus.opendaylight.org/content</nexusproxy>
+ <yang.version>0.5.7-SNAPSHOT</yang.version>
+ <maven.bundle.version>2.4.0</maven.bundle.version>
+ </properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>14.0.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.2</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.10</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-binding</artifactId>
- <version>${yang.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- <version>${yang.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-api</artifactId>
- <version>${yang.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-model-api</artifactId>
- <version>${yang.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-util</artifactId>
- <version>${yang.version}</version>
- </dependency>
- </dependencies>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>central</id>
+ <name>central</name>
+ <url>${nexusproxy}/repositories/central/</url>
+ </pluginRepository>
+ <pluginRepository>
+ <id>central2</id>
+ <name>central2</name>
+ <url>${nexusproxy}/repositories/central2/</url>
+ </pluginRepository>
+ <pluginRepository>
+ <id>opendaylight.snapshot</id>
+ <name>opendaylight.snapshot</name>
+ <url>${nexusproxy}/repositories/opendaylight.snapshot/</url>
+ </pluginRepository>
+ </pluginRepositories>
- </dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- <version>1.9.5</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.0</version>
- <inherited>true</inherited>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <version>2.8.1</version>
- <configuration>
- <stylesheet>maven</stylesheet>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>aggregate</goal>
- </goals>
- <phase>site</phase>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- <reporting>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>findbugs-maven-plugin</artifactId>
- <version>2.4.0</version>
- <configuration>
- <effort>Max</effort>
- <threshold>Low</threshold>
- <goal>site</goal>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>jdepend-maven-plugin</artifactId>
- <version>2.0-beta-2</version>
- </plugin>
- </plugins>
- </reporting>
+ <repositories>
+ <!-- EBR release -->
+ <!-- http://repository.springsource.com/maven/bundles/release -->
+ <repository>
+ <id>ebr-bundles-release</id>
+ <name>ebr-bundles-release</name>
+ <url>${nexusproxy}/repositories/ebr-bundles-release/</url>
+ </repository>
+ <!-- EBR external -->
+ <!-- http://repository.springsource.com/maven/bundles/external -->
+ <repository>
+ <id>ebr-bundles-external</id>
+ <name>ebr-bundles-external</name>
+ <url>${nexusproxy}/repositories/ebr-bundles-external/</url>
+ </repository>
+ <!-- Maven repo2 mirror -->
+ <!-- http://repo2.maven.org/maven2 -->
+ <repository>
+ <id>central2</id>
+ <name>central2</name>
+ <url>${nexusproxy}/repositories/central2/</url>
+ </repository>
+ <!-- Maven repo1 mirror -->
+ <!-- http://repo1.maven.org/maven2 -->
+ <repository>
+ <id>central</id>
+ <name>central</name>
+ <url>${nexusproxy}/repositories/central/</url>
+ </repository>
+ <!-- Pax mirror -->
+ <!-- https://oss.sonatype.org/content/repositories/ops4j-releases -->
+ <repository>
+ <id>ops4j-releases</id>
+ <name>ops4j-releases</name>
+ <url>${nexusproxy}/repositories/ops4j-releases/</url>
+ </repository>
+ <!-- Third Packages hosted in local maven because not available in other
+ places -->
+ <repository>
+ <id>thirdparty</id>
+ <name>thirdparty</name>
+ <url>${nexusproxy}/repositories/thirdparty/</url>
+ </repository>
+ <!-- Jboss mirror -->
+ <!-- https://repository.jboss.org/nexus/content/repositories/releases -->
+ <repository>
+ <id>jboss.releases</id>
+ <name>jboss.releases</name>
+ <url>${nexusproxy}/repositories/jboss.releases/</url>
+ </repository>
+ <!-- OpenDayLight Released artifact -->
+ <repository>
+ <id>opendaylight-release</id>
+ <name>opendaylight-release</name>
+ <url>${nexusproxy}/repositories/opendaylight.release/</url>
+ </repository>
+ <!-- OpenDayLight Snapshot artifact -->
+ <repository>
+ <id>opendaylight-snapshot</id>
+ <name>opendaylight-snapshot</name>
+ <url>${nexusproxy}/repositories/opendaylight.snapshot/</url>
+ </repository>
+ </repositories>
+
+ <distributionManagement>
+ <!-- OpenDayLight Released artifact -->
+ <repository>
+ <id>opendaylight-release</id>
+ <url>${nexusproxy}/repositories/opendaylight.release/</url>
+ </repository>
+ <!-- OpenDayLight Snapshot artifact -->
+ <snapshotRepository>
+ <id>opendaylight-snapshot</id>
+ <url>${nexusproxy}/repositories/opendaylight.snapshot/</url>
+ </snapshotRepository>
+ <!-- Site deployment -->
+ <site>
+ <id>website</id>
+ <url>${sitedeploy}</url>
+ </site>
+ </distributionManagement>
+
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>14.0.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.2</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.10</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-util</artifactId>
+ <version>${yang.version}</version>
+ </dependency>
+ </dependencies>
+
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${maven.bundle.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.0</version>
+ <inherited>true</inherited>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.8.1</version>
+ <configuration>
+ <stylesheet>maven</stylesheet>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>aggregate</goal>
+ </goals>
+ <phase>site</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>2.4.0</version>
+ <configuration>
+ <effort>Max</effort>
+ <threshold>Low</threshold>
+ <goal>site</goal>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jdepend-maven-plugin</artifactId>
+ <version>2.0-beta-2</version>
+ </plugin>
+ </plugins>
+ </reporting>
</project>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
- <modelVersion>4.0.0</modelVersion>\r
- <parent>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </parent>\r
- <artifactId>sal-binding-api</artifactId>\r
-\r
- <dependencies>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>yang-common</artifactId>\r
-\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>yang-binding</artifactId>\r
-\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-common</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
+<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.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-binding-api</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>5.0.0</version>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.binding.api;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public abstract class AbstractBindingAwareConsumer implements BindingAwareConsumer,BundleActivator {
+
+ @Override
+ public final void start(BundleContext context) throws Exception {
+ ServiceReference<BindingAwareBroker> brokerRef = context.getServiceReference(BindingAwareBroker.class);
+ BindingAwareBroker broker = context.getService(brokerRef);
+ broker.registerConsumer(this, context);
+ startImpl(context);
+ //context.ungetService(brokerRef);
+ }
+
+ @Deprecated
+ abstract protected void startImpl(BundleContext context);
+
+ @Override
+ public final void stop(BundleContext context) throws Exception {
+ // TODO Auto-generated method stub
+
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.api;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public abstract class AbstractBindingAwareProvider implements BindingAwareProvider, BundleActivator {
+
+ @Override
+ public final void start(BundleContext context) throws Exception {
+ ServiceReference<BindingAwareBroker> brokerRef = context.getServiceReference(BindingAwareBroker.class);
+ BindingAwareBroker broker = context.getService(brokerRef);
+ broker.registerProvider(this, context);
+ startImpl(context);
+ }
+
+ @Deprecated
+ abstract protected void startImpl(BundleContext context);
+
+ @Override
+ public final void stop(BundleContext context) throws Exception {
+
+
+ }
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import org.opendaylight.controller.yang.binding.RpcService;\r
-\r
-/**\r
- * Binding-aware core of the SAL layer responsible for wiring the SAL consumers.\r
- * \r
- * The responsibility of the broker is to maintain registration of SAL\r
- * functionality {@link Consumer}s and {@link Provider}s, store provider and\r
- * consumer specific context and functionality registration via\r
- * {@link ConsumerSession} and provide access to infrastructure services, which\r
- * removes direct dependencies between providers and consumers.\r
- * \r
- * The Binding-aware broker is also responsible for translation from Java\r
- * classes modeling the functionality and data to binding-indpenedent form which\r
- * is used in SAL Core.\r
- * \r
- * \r
- * <h3>Infrastructure services</h3> Some examples of infrastructure services:\r
- * \r
- * <ul>\r
- * <li>YANG Module service - see {@link ConsumerSession#getRpcService(Class)},\r
- * {@link ProviderSession}\r
- * <li>Notification Service - see {@link NotificationService} and\r
- * {@link NotificationProviderService}\r
- * <li>Functionality and Data model\r
- * <li>Data Store access and modification - see {@link DataBrokerService} and\r
- * {@link DataProviderService}\r
- * </ul>\r
- * \r
- * The services are exposed via session.\r
- * \r
- * <h3>Session-based access</h3>\r
- * \r
- * The providers and consumers needs to register in order to use the\r
- * binding-independent SAL layer and to expose functionality via SAL layer.\r
- * \r
- * For more information about session-based access see {@link ConsumerSession}\r
- * and {@link ProviderSession}\r
- * \r
- * \r
-\r
- * \r
- */\r
-public interface BindingAwareBroker {\r
- /**\r
- * Registers the {@link BindingAwareConsumer}, which will use the SAL layer.\r
- * \r
- * <p>\r
- * Note that consumer could register additional functionality at later point\r
- * by using service and functionality specific APIs.\r
- * \r
- * <p>\r
- * The consumer is required to use returned session for all communication\r
- * with broker or one of the broker services. The session is announced to\r
- * the consumer by invoking\r
- * {@link Consumer#onSessionInitiated(ConsumerSession)}.\r
- * \r
- * @param cons\r
- * Consumer to be registered.\r
- * @return a session specific to consumer registration\r
- * @throws IllegalArgumentException\r
- * If the consumer is <code>null</code>.\r
- * @throws IllegalStateException\r
- * If the consumer is already registered.\r
- */\r
- ConsumerSession registerConsumer(BindingAwareConsumer consumer);\r
-\r
- /**\r
- * Registers the {@link BindingAwareProvider}, which will use the SAL layer.\r
- * \r
- * <p>\r
- * During the registration, the broker obtains the initial functionality\r
- * from consumer, using the\r
- * {@link BindingAwareProvider#getImplementations()}, and register that\r
- * functionality into system and concrete infrastructure services.\r
- * \r
- * <p>\r
- * Note that provider could register additional functionality at later point\r
- * by using service and functionality specific APIs.\r
- * \r
- * <p>\r
- * The consumer is <b>required to use</b> returned session for all\r
- * communication with broker or one of the broker services. The session is\r
- * announced to the consumer by invoking\r
- * {@link BindingAwareProvider#onSessionInitiated(ProviderSession)}.\r
- * \r
- * \r
- * @param prov\r
- * Provider to be registered.\r
- * @return a session unique to the provider registration.\r
- * @throws IllegalArgumentException\r
- * If the provider is <code>null</code>.\r
- * @throws IllegalStateException\r
- * If the consumer is already registered.\r
- */\r
- ProviderSession registerProvider(BindingAwareProvider provider);\r
-\r
- /**\r
- * {@link BindingAwareConsumer} specific access to the SAL functionality.\r
- * \r
- * <p>\r
- * ConsumerSession is {@link BindingAwareConsumer}-specific access to the\r
- * SAL functionality and infrastructure services.\r
- * \r
- * <p>\r
- * The session serves to store SAL context (e.g. registration of\r
- * functionality) for the consumer and provides access to the SAL\r
- * infrastructure services and other functionality provided by\r
- * {@link Provider}s.\r
- * \r
-\r
- * \r
- */\r
- public interface ConsumerSession {\r
-\r
- /**\r
- * Returns a session specific instance (implementation) of requested\r
- * binding-aware infrastructural service\r
- * \r
- * @param service\r
- * Broker service\r
- * @return Session specific implementation of service\r
- */\r
- <T extends BindingAwareService> T getSALService(Class<T> service);\r
-\r
- /**\r
- * Returns a session specific instance (implementation) of requested\r
- * YANG module implentation / service provided by consumer.\r
- * \r
- * @param service\r
- * Broker service\r
- * @return Session specific implementation of service\r
- */\r
- <T extends RpcService> T getRpcService(Class<T> module);\r
- }\r
-\r
- /**\r
- * {@link BindingAwareProvider} specific access to the SAL functionality.\r
- * \r
- * <p>\r
- * ProviderSession is {@link BindingAwareProvider}-specific access to the\r
- * SAL functionality and infrastructure services, which also allows for\r
- * exposing the provider's functionality to the other\r
- * {@link BindingAwareConsumer}s.\r
- * \r
- * <p>\r
- * The session serves to store SAL context (e.g. registration of\r
- * functionality) for the providers and exposes access to the SAL\r
- * infrastructure services, dynamic functionality registration and any other\r
- * functionality provided by other {@link BindingAwareConsumer}s.\r
- * \r
- */\r
- public interface ProviderSession extends ConsumerSession {\r
-\r
- void addRpcImplementation(RpcService implementation);\r
-\r
- void removeRpcImplementation(RpcService implementation);\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+
+/**
+ * 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-indpenedent 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>Notification Service - see {@link NotificationService} and
+ * {@link NotificationProviderService}
+ * <li>Functionality and Data model
+ * <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
+ * @throws IllegalArgumentException
+ * If the consumer is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ConsumerContext registerConsumer(BindingAwareConsumer consumer, BundleContext ctx);
+
+ /**
+ * 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.
+ * @throws IllegalArgumentException
+ * If the provider is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ProviderContext registerProvider(BindingAwareProvider provider, BundleContext ctx);
+
+ /**
+ * {@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 {
+
+ /**
+ * Returns a session specific instance (implementation) of requested
+ * binding-aware infrastructural service
+ *
+ * @param service
+ * Broker service
+ * @return Session specific implementation of service
+ */
+ <T extends BindingAwareService> T getSALService(Class<T> service);
+
+ /**
+ * Returns a session specific instance (implementation) of requested
+ * YANG module implentation / service provided by consumer.
+ *
+ * @param service
+ * Broker service
+ * @return Session specific implementation of service
+ */
+ <T extends RpcService> T getRpcService(Class<T> module);
+ }
+
+ /**
+ * {@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 {
+
+ <T extends RpcService> RpcServiceRegistration<T> addRpcImplementation(Class<T> type, T implementation);
+ }
+
+ public interface RpcServiceRegistration<T extends RpcService> {
+
+ T getService();
+
+ void unregister();
+ }
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;\r
-\r
-/**\r
- * \r
- * Defines the component of controller and supplies additional metadata. A\r
- * component of the controller or application supplies a concrete implementation\r
- * of this interface.\r
- * \r
- * A user-implemented component (application) which faciliates the SAL and SAL\r
- * services to access infrastructure services or providers' functionality.\r
- * \r
-\r
- * \r
- */\r
-public interface BindingAwareConsumer {\r
-\r
- /**\r
- * Callback signaling initialization of the consumer session to the SAL.\r
- * \r
- * The consumer MUST use the session for all communication with SAL or\r
- * retrieving SAL infrastructure services.\r
- * \r
- * This method is invoked by\r
- * {@link BindingAwareBroker#registerConsumer(BindingAwareConsumer)}\r
- * \r
- * @param session\r
- * Unique session between consumer and SAL.\r
- */\r
- void onSessionInitialized(ConsumerSession session);\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * A user-implemented component (application) which faciliates the SAL and SAL
+ * services to access infrastructure services or providers' functionality.
+ *
+ *
+ *
+ */
+public interface BindingAwareConsumer {
+
+ /**
+ * Callback signaling initialization of the consumer session to the SAL.
+ *
+ * The consumer MUST use the session for all communication with SAL or
+ * retrieving SAL infrastructure services.
+ *
+ * This method is invoked by
+ * {@link BindingAwareBroker#registerConsumer(BindingAwareConsumer)}
+ *
+ * @param session
+ * Unique session between consumer and SAL.
+ */
+ void onSessionInitialized(ConsumerContext session);
+
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import java.util.Collection;\r
-\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderSession;\r
-import org.opendaylight.controller.yang.binding.RpcService;\r
-\r
-\r
-/**\r
- * \r
- * Defines the component of controller and supplies additional metadata. A\r
- * component of the controller or application supplies a concrete implementation\r
- * of this interface.\r
- * \r
- * <p>\r
- * A user-implemented component (application) which facilitates the SAL and SAL\r
- * services to access infrastructure services and to provide functionality to\r
- * {@link Consumer}s and other providers.\r
- * \r
-\r
- * \r
- */\r
-public interface BindingAwareProvider {\r
-\r
- void onSessionInitialized(ConsumerSession session);\r
-\r
- /**\r
- * Returns a set of provided implementations of YANG modules and their rpcs.\r
- * \r
- * \r
- * @return Set of provided implementation of YANG modules and their Rpcs\r
- */\r
- Collection<? extends RpcService> getImplementations();\r
-\r
- /**\r
- * Gets a set of implementations of provider functionality to be registered\r
- * into system during the provider registration to the SAL.\r
- * \r
- * <p>\r
- * This method is invoked by {@link Broker#registerProvider(Provider)} to\r
- * learn the initial provided functionality\r
- * \r
- * @return Set of provider's functionality.\r
- */\r
- Collection<? extends ProviderFunctionality> getFunctionality();\r
-\r
- /**\r
- * Functionality provided by the {@link BindingAwareProvider}\r
- * \r
- * <p>\r
- * Marker interface used to mark the interfaces describing specific\r
- * functionality which could be exposed by providers to other components.\r
- * \r
-\r
- * \r
- */\r
- public interface ProviderFunctionality {\r
-\r
- }\r
-\r
- void onSessionInitiated(ProviderSession session);\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+import java.util.Collection;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * <p>
+ * A user-implemented component (application) which facilitates the SAL and SAL
+ * services to access infrastructure services and to provide functionality to
+ * {@link Consumer}s and other providers.
+ *
+ *
+ */
+public interface BindingAwareProvider {
+
+ void onSessionInitialized(ConsumerContext session);
+
+ /**
+ * Returns a set of provided implementations of YANG modules and their rpcs.
+ *
+ *
+ * @return Set of provided implementation of YANG modules and their Rpcs
+ */
+ Collection<? extends RpcService> getImplementations();
+
+ /**
+ * Gets a set of implementations of provider functionality to be registered
+ * into system during the provider registration to the SAL.
+ *
+ * <p>
+ * This method is invoked by {@link Broker#registerProvider(Provider)} to
+ * learn the initial provided functionality
+ *
+ * @return Set of provider's functionality.
+ */
+ Collection<? extends ProviderFunctionality> getFunctionality();
+
+ /**
+ * Functionality provided by the {@link BindingAwareProvider}
+ *
+ * <p>
+ * Marker interface used to mark the interfaces describing specific
+ * functionality which could be exposed by providers to other components.
+ *
+ *
+ *
+ */
+ public interface ProviderFunctionality {
+
+ }
+
+ void onSessionInitiated(ProviderContext session);
+
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;\r
-\r
-/**\r
- * \r
- * Session-specific instance of the broker functionality.\r
- * \r
- * <p>\r
- * BindingAwareService is marker interface for infrastructure services provided\r
- * by the SAL. These services are session-specific, each\r
- * {@link BindingAwareConsumer} and {@link BindingAwareProvider} usually has own\r
- * instance of the service with it's own context.\r
- * \r
- * <p>\r
- * The consumer's (or provider's) instance of specific service could be obtained\r
- * by invoking {@link ConsumerSession#getSALService(Class)} method on session\r
- * assigned to the consumer.\r
- * \r
- * <p>\r
- * {@link BindingAwareService} and {@link BindingAwareProvider} may seem\r
- * similar, but provider provides YANG model-based functionality and\r
- * {@link BindingAwareProvider} exposes the necessary supporting functionality\r
- * to implement specific functionality of YANG and to reuse it in the\r
- * development of {@link BindingAwareConsumer}s and {@link BindingAwareProvider}\r
- * s.\r
- * \r
-\r
- * \r
- */\r
-public interface BindingAwareService {\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+
+/**
+ *
+ * Session-specific instance of the broker functionality.
+ *
+ * <p>
+ * BindingAwareService is marker interface for infrastructure services provided
+ * by the SAL. These services are session-specific, each
+ * {@link BindingAwareConsumer} and {@link BindingAwareProvider} usually has own
+ * instance of the service with it's own context.
+ *
+ * <p>
+ * The consumer's (or provider's) instance of specific service could be obtained
+ * by invoking {@link ConsumerContext#getSALService(Class)} method on session
+ * assigned to the consumer.
+ *
+ * <p>
+ * {@link BindingAwareService} and {@link BindingAwareProvider} may seem
+ * similar, but provider provides YANG model-based functionality and
+ * {@link BindingAwareProvider} exposes the necessary supporting functionality
+ * to implement specific functionality of YANG and to reuse it in the
+ * development of {@link BindingAwareConsumer}s and {@link BindingAwareProvider}
+ * s.
+ *
+ *
+ *
+ */
+public interface BindingAwareService {
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.api;
+
+public interface NotificationListener<T> {
+
+ void onNotification(T notification);
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import org.opendaylight.controller.yang.binding.Notification;\r
-\r
-public interface NotificationProviderService extends NotificationService {\r
-\r
- void notify(Notification notification);\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface NotificationProviderService extends NotificationService {
+
+ void notify(Notification notification);
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import org.opendaylight.controller.yang.binding.Notification;\r
-import org.opendaylight.controller.yang.binding.NotificationListener;\r
-\r
-public interface NotificationService extends BindingAwareService {\r
-\r
- void addNotificationListener(\r
- Class<? extends Notification> notificationType,\r
- NotificationListener listener);\r
-\r
- void removeNotificationListener(\r
- Class<? extends Notification> notificationType,\r
- NotificationListener listener);\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface NotificationService extends BindingAwareService {
+
+ <T extends Notification> void addNotificationListener(Class<T> notificationType, NotificationListener<T> listener);
+
+ <T extends Notification> void removeNotificationListener(Class<T> notificationType, NotificationListener<T> listener);
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import java.util.concurrent.Future;\r
-\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-import org.opendaylight.controller.yang.binding.DataRoot;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-\r
-\r
-/**\r
- * DataBrokerService provides unified access to the data stores available in the\r
- * system.\r
- * \r
- * \r
- * @see DataProviderService\r
-\r
- */\r
-public interface DataBrokerService extends BindingAwareService {\r
-\r
- /**\r
- * Returns a data from specified Data Store.\r
- * \r
- * Returns all the data visible to the consumer from specified Data Store.\r
- * \r
- * @param <T>\r
- * Interface generated from YANG module representing root of data\r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @return data visible to the consumer\r
- */\r
- <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType);\r
-\r
- /**\r
- * Returns a filtered subset of data from specified Data Store.\r
- * \r
- * <p>\r
- * The filter is modeled as an hierarchy of Java TOs starting with\r
- * implementation of {@link DataRoot} representing data root. The semantics\r
- * of the filter tree is the same as filter semantics defined in the NETCONF\r
- * protocol for rpc operations <code>get</code> and <code>get-config</code>\r
- * in Section 6 of RFC6241.\r
- * \r
- * \r
- * @see http://tools.ietf.org/html/rfc6241#section-6\r
- * @param <T>\r
- * Interface generated from YANG module representing root of data\r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @param filter\r
- * Data tree filter similar to the NETCONF filter\r
- * @return\r
- */\r
- <T extends DataRoot> T getData(DataStoreIdentifier store, T filter);\r
-\r
- /**\r
- * Returns a candidate data which are not yet commited.\r
- * \r
- * \r
- * @param <T>\r
- * Interface generated from YANG module representing root of data\r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @return\r
- */\r
- <T extends DataRoot> T getCandidateData(DataStoreIdentifier store,\r
- Class<T> rootType);\r
-\r
- /**\r
- * Returns a filtered subset of candidate data from specified Data Store.\r
- * \r
- * <p>\r
- * The filter is modeled as an hierarchy of {@link Node} starting with\r
- * {@link CompositeNode} representing data root. The semantics of the filter\r
- * tree is the same as filter semantics defined in the NETCONF protocol for\r
- * rpc operations <code>get</code> and <code>get-config</code> in Section 6\r
- * of RFC6241.\r
- * \r
- * \r
- * @see http://tools.ietf.org/html/rfc6241#section-6\r
- * @param <T>\r
- * Interface generated from YANG module representing root of data\r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @param filter\r
- * A filter data root\r
- * @return\r
- */\r
- <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter);\r
-\r
- /**\r
- * \r
- * @param <T>\r
- * Interface generated from YANG module representing root of data\r
- * @param store\r
- * Identifier of the store, in which will be the candidate data\r
- * modified\r
- * @param changeSet\r
- * Modification of data tree.\r
- * @return Result object containing the modified data tree if the operation\r
- * was successful, otherwise list of the encountered errors.\r
- */\r
- RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store,\r
- DataRoot changeSet);\r
-\r
- /**\r
- * Initiates a two-phase commit of candidate data.\r
- * \r
- * <p>\r
- * The {@link Consumer} could initiate a commit of candidate data\r
- * \r
- * <p>\r
- * The successful commit changes the state of the system and may affect\r
- * several components.\r
- * \r
- * <p>\r
- * The effects of successful commit of data are described in the\r
- * specifications and YANG models describing the {@link Provider} components\r
- * of controller. It is assumed that {@link Consumer} has an understanding\r
- * of this changes.\r
- * \r
- * \r
- * @see DataCommitHandler for further information how two-phase commit is\r
- * processed.\r
- * @param store\r
- * Identifier of the store, where commit should occur.\r
- * @return Result of the commit, containing success information or list of\r
- * encountered errors, if commit was not successful.\r
- */\r
- Future<RpcResult<Void>> commit(DataStoreIdentifier store);\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.yangtools.yang.binding.DataRoot;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * DataBrokerService provides unified access to the data stores available in the
+ * system.
+ *
+ *
+ * @see DataProviderService
+ */
+public interface DataBrokerService extends BindingAwareService {
+
+ /**
+ * Returns a data from specified Data Store.
+ *
+ * Returns all the data visible to the consumer from specified Data Store.
+ *
+ * @param <T>
+ * Interface generated from YANG module representing root of data
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @return data visible to the consumer
+ */
+ <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType);
+
+ /**
+ * Returns a filtered subset of data from specified Data Store.
+ *
+ * <p>
+ * The filter is modeled as an hierarchy of Java TOs starting with
+ * implementation of {@link DataRoot} representing data root. The semantics
+ * of the filter tree is the same as filter semantics defined in the NETCONF
+ * protocol for rpc operations <code>get</code> and <code>get-config</code>
+ * in Section 6 of RFC6241.
+ *
+ *
+ * @see http://tools.ietf.org/html/rfc6241#section-6
+ * @param <T>
+ * Interface generated from YANG module representing root of data
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @param filter
+ * Data tree filter similar to the NETCONF filter
+ * @return
+ */
+ <T extends DataRoot> T getData(DataStoreIdentifier store, T filter);
+
+ /**
+ * Returns a candidate data which are not yet commited.
+ *
+ *
+ * @param <T>
+ * Interface generated from YANG module representing root of data
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @return
+ */
+ <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, Class<T> rootType);
+
+ /**
+ * Returns a filtered subset of candidate data from specified Data Store.
+ *
+ * <p>
+ * The filter is modeled as an hierarchy of {@link Node} starting with
+ * {@link CompositeNode} representing data root. The semantics of the filter
+ * tree is the same as filter semantics defined in the NETCONF protocol for
+ * rpc operations <code>get</code> and <code>get-config</code> in Section 6
+ * of RFC6241.
+ *
+ *
+ * @see http://tools.ietf.org/html/rfc6241#section-6
+ * @param <T>
+ * Interface generated from YANG module representing root of data
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @param filter
+ * A filter data root
+ * @return
+ */
+ <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter);
+
+ /**
+ *
+ * @param <T>
+ * Interface generated from YANG module representing root of data
+ * @param store
+ * Identifier of the store, in which will be the candidate data
+ * modified
+ * @param changeSet
+ * Modification of data tree.
+ * @return Result object containing the modified data tree if the operation
+ * was successful, otherwise list of the encountered errors.
+ */
+ RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store, DataRoot changeSet);
+
+ /**
+ * Initiates a two-phase commit of candidate data.
+ *
+ * <p>
+ * The {@link Consumer} could initiate a commit of candidate data
+ *
+ * <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 {@link Provider} components
+ * of controller. It is assumed that {@link 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.
+ */
+ Future<RpcResult<Void>> commit(DataStoreIdentifier store);
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-\r
-\r
-/**\r
- * Two phase commit handler (cohort) of the two-phase commit protocol of data.\r
- * \r
- * <p>\r
- * The provider should expose the implementation of DataCommitHandler if it's\r
- * functionality depends on any subset of data stored in data repositories, in\r
- * order to participate in {@link DataBrokerService#commit(DataStoreIdentifier)\r
- * operation.\r
- * \r
- * <p>\r
- * Operations of two-phase commit handlers should not change data in data store,\r
- * this is responsibility of the coordinator (broker or some component of the\r
- * broker).\r
- * \r
- * The commit handlers are responsible for changing the internal state of the\r
- * provider to reflect the commited changes in data.\r
- * \r
- * <h3>Two-phase commit</h3>\r
- * \r
- * <h4>Commit Request Phase</h4>\r
- * \r
- * <ol>\r
- * <li> <code>Consumer</code> edits data by invocation of\r
- * <code>DataBrokerService.editCandidateData(DataStoreIdentifier, DataRoot)</code>\r
- * <li> <code>Consumer</code> starts a commit by invoking\r
- * <code>DataBrokerService.commit(DataStoreIdentifier)</code>\r
- * <li> <code>Broker</code> retrieves a list of all registered\r
- * <code>DataCommitHandlers</code>\r
- * <li>For each <code>DataCommitHandler</code>\r
- * <ol>\r
- * <li><code>Broker</code> invokes a\r
- * <code>DataCommitHandler.requestCommit(DataStoreIdentifier)</code> operation.\r
- * <li><code>DataCommitHandler</code> returns a <code>RpcResult</code> with\r
- * <code>CommitTransaction</code>\r
- * <li>If the result was successful, broker adds <code>CommitTransaction</code>\r
- * to the list of opened transactions. If not, brokers stops a commit request\r
- * phase and starts a rollback phase.\r
- * </ol>\r
- * <li><code>Broker</code> starts a commit finish phase\r
- * </ol>\r
- * \r
- * <h4>Commit Finish Phase</h4>\r
- * \r
- * <ol>\r
- * <li>For each <code>CommitTransaction</code> from Commit Request phase\r
- * <ol>\r
- * <li><code>Broker</code> broker invokes a\r
- * <code>CommitTransaction.finish()</code>\r
- * <li>The provider finishes a commit (applies the change) and returns an\r
- * <code>RpcResult</code>.\r
- * </ol>\r
- * <li>\r
- * <ul>\r
- * <li>If all returned results means successful, the brokers end two-phase\r
- * commit by returning a success commit result to the Consumer.\r
- * <li>If error occured, the broker starts a commit rollback phase.\r
- * </ul>\r
- * </ol>\r
- * \r
- * <h4>Commit Rollback Phase</h4>\r
- * <li>For each <code>CommitTransaction</code> from Commit Request phase\r
- * <ol>\r
- * <li><code>Broker</code>\r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * \r
- * broker invokes a {@link CommitTransaction#finish()}\r
- * <li>The provider rollbacks a commit and returns an {@link RpcResult} of\r
- * rollback. </ol>\r
- * <li>Broker returns a error result to the consumer.\r
- * \r
- * \r
- * <h3>Registration of functionality</h3>\r
- * The registration could be done by :\r
- * <ul>\r
- * <li>returning an instance of implementation in the return value of\r
- * {@link Provider#getProviderFunctionality()}\r
- * <li>passing an instance of implementation and {@link DataStoreIdentifier} of\r
- * rpc as arguments to the\r
- * {@link DataProviderService#addCommitHandler(DataStoreIdentifier, DataCommitHandler)}\r
- * </ul>\r
- * \r
-\r
- * \r
- */\r
-public interface DataCommitHandler extends ProviderFunctionality {\r
- /**\r
- * A set of Data Stores supported by implementation.\r
- * \r
- * The set of {@link DataStoreIdentifier}s which identifies target data\r
- * stores which are supported by this commit handler. This set is used, when\r
- * {@link Provider} is registered to the SAL, to register and expose the\r
- * commit handler functionality to affected data stores.\r
- * \r
- * @return Set of Data Store identifiers\r
- */\r
- Set<DataStoreIdentifier> getSupportedDataStores();\r
-\r
- /**\r
- * The provider (commit handler) starts a commit transaction.\r
- * \r
- * <p>\r
- * The commit handler (provider) prepares an commit scenario, rollback\r
- * scenario and validates data.\r
- * \r
- * <p>\r
- * If the provider is aware that at this point the commit would not be\r
- * successful, the transaction is not created, but list of errors which\r
- * prevented the start of transaction are returned.\r
- * \r
- * @param store\r
- * @return Transaction object representing this commit, errors otherwise.\r
- */\r
- RpcResult<CommitTransaction> requestCommit(DataStoreIdentifier store);\r
-\r
- public interface CommitTransaction {\r
- /**\r
- * \r
- * @return Data store affected by the transaction\r
- */\r
- DataStoreIdentifier getDataStore();\r
-\r
- /**\r
- * Returns the handler associated with this transaction.\r
- * \r
- * @return Handler\r
- */\r
- DataCommitHandler getHandler();\r
-\r
- /**\r
- * \r
- * Finishes a commit.\r
- * \r
- * The provider (commit handler) should apply all changes to its state\r
- * which are a result of data change-\r
- * \r
- * @return\r
- */\r
- RpcResult<Void> finish() throws IllegalStateException;\r
-\r
- /**\r
- * Rollbacks a commit.\r
- * \r
- * @return\r
- * @throws IllegalStateException\r
- * If the method is invoked after {@link #finish()}\r
- */\r
- RpcResult<Void> rollback() throws IllegalStateException;\r
- }\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Two phase commit handler (cohort) of the two-phase commit protocol of data.
+ *
+ * <p>
+ * The provider should expose the implementation of DataCommitHandler if it's
+ * functionality depends on any subset of data stored in data repositories, in
+ * order to participate in {@link DataBrokerService#commit(DataStoreIdentifier)
+ * operation.
+ *
+ * <p>
+ * Operations of two-phase commit handlers should not change data in data store,
+ * this is responsibility of the coordinator (broker or some component of the
+ * broker).
+ *
+ * The commit handlers are responsible for changing the internal state of the
+ * provider to reflect the commited changes in data.
+ *
+ * <h3>Two-phase commit</h3>
+ *
+ * <h4>Commit Request Phase</h4>
+ *
+ * <ol>
+ * <li> <code>Consumer</code> edits data by invocation of
+ * <code>DataBrokerService.editCandidateData(DataStoreIdentifier, DataRoot)</code>
+ * <li> <code>Consumer</code> starts a commit by invoking
+ * <code>DataBrokerService.commit(DataStoreIdentifier)</code>
+ * <li> <code>Broker</code> retrieves a list of all registered
+ * <code>DataCommitHandlers</code>
+ * <li>For each <code>DataCommitHandler</code>
+ * <ol>
+ * <li><code>Broker</code> invokes a
+ * <code>DataCommitHandler.requestCommit(DataStoreIdentifier)</code> operation.
+ * <li><code>DataCommitHandler</code> returns a <code>RpcResult</code> with
+ * <code>CommitTransaction</code>
+ * <li>If the result was successful, broker adds <code>CommitTransaction</code>
+ * to the list of opened transactions. If not, brokers stops a commit request
+ * phase and starts a rollback phase.
+ * </ol>
+ * <li><code>Broker</code> starts a commit finish phase
+ * </ol>
+ *
+ * <h4>Commit Finish Phase</h4>
+ *
+ * <ol>
+ * <li>For each <code>CommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code> broker invokes a
+ * <code>CommitTransaction.finish()</code>
+ * <li>The provider finishes a commit (applies the change) and returns an
+ * <code>RpcResult</code>.
+ * </ol>
+ * <li>
+ * <ul>
+ * <li>If all returned results means successful, the brokers end two-phase
+ * commit by returning a success commit result to the Consumer.
+ * <li>If error occured, the broker starts a commit rollback phase.
+ * </ul>
+ * </ol>
+ *
+ * <h4>Commit Rollback Phase</h4>
+ * <li>For each <code>CommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code>
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * broker invokes a {@link CommitTransaction#finish()}
+ * <li>The provider rollbacks a commit and returns an {@link RpcResult} of
+ * rollback. </ol>
+ * <li>Broker returns a error result to the consumer.
+ *
+ *
+ * <h3>Registration of functionality</h3>
+ * The registration could be done by :
+ * <ul>
+ * <li>returning an instance of implementation in the return value of
+ * {@link Provider#getProviderFunctionality()}
+ * <li>passing an instance of implementation and {@link DataStoreIdentifier} of
+ * rpc as arguments to the
+ * {@link DataProviderService#addCommitHandler(DataStoreIdentifier, DataCommitHandler)}
+ * </ul>
+ *
+ *
+ *
+ */
+public interface DataCommitHandler extends ProviderFunctionality {
+ /**
+ * A set of Data Stores supported by implementation.
+ *
+ * The set of {@link DataStoreIdentifier}s which identifies target data
+ * stores which are supported by this commit handler. This set is used, when
+ * {@link Provider} is registered to the SAL, to register and expose the
+ * commit handler functionality to affected data stores.
+ *
+ * @return Set of Data Store identifiers
+ */
+ Set<DataStoreIdentifier> getSupportedDataStores();
+
+ /**
+ * The provider (commit handler) starts a commit transaction.
+ *
+ * <p>
+ * The commit handler (provider) prepares an commit scenario, rollback
+ * scenario and validates data.
+ *
+ * <p>
+ * If the provider is aware that at this point the commit would not be
+ * successful, the transaction is not created, but list of errors which
+ * prevented the start of transaction are returned.
+ *
+ * @param store
+ * @return Transaction object representing this commit, errors otherwise.
+ */
+ RpcResult<CommitTransaction> requestCommit(DataStoreIdentifier store);
+
+ public interface CommitTransaction {
+ /**
+ *
+ * @return Data store affected by the transaction
+ */
+ DataStoreIdentifier getDataStore();
+
+ /**
+ * Returns the handler associated with this transaction.
+ *
+ * @return Handler
+ */
+ DataCommitHandler getHandler();
+
+ /**
+ *
+ * Finishes a commit.
+ *
+ * The provider (commit handler) should apply all changes to its state
+ * which are a result of data change-
+ *
+ * @return
+ */
+ RpcResult<Void> finish() throws IllegalStateException;
+
+ /**
+ * Rollbacks a commit.
+ *
+ * @return
+ * @throws IllegalStateException
+ * If the method is invoked after {@link #finish()}
+ */
+ RpcResult<Void> rollback() throws IllegalStateException;
+ }
+
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-\r
-public interface DataProviderService extends DataBrokerService {\r
-\r
- /**\r
- * Adds {@link DataValidator} for specified Data Store\r
- * \r
- * @param store\r
- * Data Store\r
- * @param validator\r
- * Validator\r
- */\r
- public void addValidator(DataStoreIdentifier store, DataValidator validator);\r
-\r
- /**\r
- * Removes {@link DataValidator} from specified Data Store\r
- * \r
- * @param store\r
- * @param validator\r
- * Validator\r
- */\r
- public void removeValidator(DataStoreIdentifier store,\r
- DataValidator validator);\r
-\r
- /**\r
- * Adds {@link DataCommitHandler} for specified data store\r
- * \r
- * @param store\r
- * @param provider\r
- */\r
- void addCommitHandler(DataStoreIdentifier store, DataCommitHandler provider);\r
-\r
- /**\r
- * Removes {@link DataCommitHandler} from specified data store\r
- * \r
- * @param store\r
- * @param provider\r
- */\r
- void removeCommitHandler(DataStoreIdentifier store,\r
- DataCommitHandler provider);\r
-\r
- /**\r
- * Adds {@link DataRefresher} for specified data store\r
- * \r
- * @param store\r
- * @param refresher\r
- */\r
- void addRefresher(DataStoreIdentifier store, DataRefresher refresher);\r
-\r
- /**\r
- * Removes {@link DataRefresher} from specified data store\r
- * \r
- * @param store\r
- * @param refresher\r
- */\r
- void removeRefresher(DataStoreIdentifier store, DataRefresher refresher);\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+
+public interface DataProviderService extends DataBrokerService {
+
+ /**
+ * Adds {@link DataValidator} for specified Data Store
+ *
+ * @param store
+ * Data Store
+ * @param validator
+ * Validator
+ */
+ public void addValidator(DataStoreIdentifier store, DataValidator validator);
+
+ /**
+ * Removes {@link DataValidator} from specified Data Store
+ *
+ * @param store
+ * @param validator
+ * Validator
+ */
+ public void removeValidator(DataStoreIdentifier store, DataValidator validator);
+
+ /**
+ * Adds {@link DataCommitHandler} for specified data store
+ *
+ * @param store
+ * @param provider
+ */
+ void addCommitHandler(DataStoreIdentifier store, DataCommitHandler provider);
+
+ /**
+ * Removes {@link DataCommitHandler} from specified data store
+ *
+ * @param store
+ * @param provider
+ */
+ void removeCommitHandler(DataStoreIdentifier store, DataCommitHandler provider);
+
+ /**
+ * Adds {@link DataRefresher} for specified data store
+ *
+ * @param store
+ * @param refresher
+ */
+ void addRefresher(DataStoreIdentifier store, DataRefresher refresher);
+
+ /**
+ * Removes {@link DataRefresher} from specified data store
+ *
+ * @param store
+ * @param refresher
+ */
+ void removeRefresher(DataStoreIdentifier store, DataRefresher refresher);
+
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-/**\r
- * Trigger for refreshing of the data exposed by the {@link Provider}\r
- * \r
- * \r
- * \r
- */\r
-public interface DataRefresher extends\r
- BindingAwareProvider.ProviderFunctionality {\r
-\r
- /**\r
- * Fired when some component explicitly requested the data refresh.\r
- * \r
- * The provider which exposed the {@link DataRefresher} should republish its\r
- * provided data by editing the data in all affected data stores.\r
- */\r
- void refreshData();\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
+
+/**
+ * Trigger for refreshing of the data exposed by the {@link Provider}
+ *
+ *
+ *
+ */
+public interface DataRefresher extends BindingAwareProvider.ProviderFunctionality {
+
+ /**
+ * Fired when some component explicitly requested the data refresh.
+ *
+ * The provider which exposed the {@link DataRefresher} should republish its
+ * provided data by editing the data in all affected data stores.
+ */
+ void refreshData();
}
\ No newline at end of file
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;\r
-import org.opendaylight.controller.yang.binding.DataRoot;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-\r
-\r
-public interface DataValidator extends ProviderFunctionality {\r
-\r
- RpcResult<Void> validate(DataRoot data);\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
+import org.opendaylight.yangtools.yang.binding.DataRoot;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public interface DataValidator extends ProviderFunctionality {
+
+ RpcResult<Void> validate(DataRoot data);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.api.data;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataRoot;
+
+public interface RuntimeDataProvider {
+
+ Set<DataStoreIdentifier> getSupportedStores();
+
+
+ Set<Class<? extends DataRoot>> getProvidedDataRoots();
+
+
+ /**
+ * Returns a data from specified Data Store.
+ *
+ * Returns all the data visible to the consumer from specified Data Store.
+ *
+ * @param <T>
+ * Interface generated from YANG module representing root of data
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @return data visible to the consumer
+ */
+ <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType);
+
+ /**
+ * Returns a filtered subset of data from specified Data Store.
+ *
+ * <p>
+ * The filter is modeled as an hierarchy of Java TOs starting with
+ * implementation of {@link DataRoot} representing data root. The semantics
+ * of the filter tree is the same as filter semantics defined in the NETCONF
+ * protocol for rpc operations <code>get</code> and <code>get-config</code>
+ * in Section 6 of RFC6241.
+ *
+ *
+ * @see http://tools.ietf.org/html/rfc6241#section-6
+ * @param <T>
+ * Interface generated from YANG module representing root of data
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @param filter
+ * Data tree filter similar to the NETCONF filter
+ * @return
+ */
+ <T extends DataRoot> T getData(DataStoreIdentifier store, T filter);
+}
-/*\r
- * Copyright (c) 2013 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.binding.api;\r
-\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
- <modelVersion>4.0.0</modelVersion>\r
- <parent>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </parent>\r
- <artifactId>sal-binding-broker-impl</artifactId>\r
-\r
-\r
-\r
- <dependencies>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-common-util</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-binding-api</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-binding-spi</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-core-api</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
-\r
- <dependency>\r
- <groupId>org.slf4j</groupId>\r
- <artifactId>slf4j-api</artifactId>\r
- </dependency>\r
-\r
- <dependency>\r
- <groupId>com.google.guava</groupId>\r
- <artifactId>guava</artifactId>\r
- <type>jar</type>\r
- </dependency>\r
- </dependencies>\r
+<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.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${maven.bundle.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Bundle-Activator>org.opendaylight.controller.sal.binding.impl.BrokerActivator</Bundle-Activator>
+ <Private-Package>
+ org.opendaylight.controller.sal.binding.impl,
+ org.opendaylight.controller.sal.binding.impl.utils,
+ org.eclipse.xtend2.lib,
+ org.eclipse.xtext.xbase.*
+ </Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.xtend</groupId>
+ <artifactId>xtend-maven-plugin</artifactId>
+ <version>2.4.2</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/src/main/xtend-gen</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ <version>2.4.1</version>
+ <configuration>
+ <filesets>
+ <fileset>
+ <directory>${basedir}/src/main/xtend-gen</directory>
+ <includes>
+ <include>**</include>
+ </includes>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <!-- >dependency> <groupId>org.opendaylight.controller</groupId> <artifactId>sal-core-api</artifactId>
+ <version>1.0-SNAPSHOT</version> </dependency -->
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>5.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.reflections</groupId>
+ <artifactId>reflections</artifactId>
+ <version>0.9.9-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ <version>3.17.1-GA</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.xtend</groupId>
+ <artifactId>org.eclipse.xtend.lib</artifactId>
+ <version>2.4.2</version>
+ </dependency>
+ </dependencies>
</project>
\ No newline at end of file
--- /dev/null
+/xtend-gen
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider
+import org.opendaylight.yangtools.yang.binding.RpcService
+import javassist.ClassPool
+import javassist.CtMethod
+import javassist.CtField
+import org.osgi.framework.BundleContext
+import java.util.Map
+import java.util.HashMap
+import javassist.LoaderClassPath
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker
+import java.util.Hashtable
+
+import static extension org.opendaylight.controller.sal.binding.impl.utils.PropertiesUtils.*
+import static extension org.opendaylight.controller.sal.binding.impl.utils.GeneratorUtils.*
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService
+import org.osgi.framework.ServiceRegistration
+import org.opendaylight.controller.sal.binding.impl.utils.PropertiesUtils
+import org.opendaylight.controller.sal.binding.api.NotificationService
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext
+import javassist.Modifier
+import org.slf4j.LoggerFactory
+
+class BindingAwareBrokerImpl implements BindingAwareBroker {
+ private static val DELEGATE_FIELD = "_delegate"
+ private static val log = LoggerFactory.getLogger(BindingAwareBrokerImpl)
+
+ private val clsPool = ClassPool.getDefault()
+ private Map<Class<? extends RpcService>, RpcProxyContext> managedProxies = new HashMap();
+ private var NotificationBrokerImpl notifyBroker
+ private var ServiceRegistration<NotificationProviderService> notifyBrokerRegistration
+
+ @Property
+ var BundleContext brokerBundleContext
+
+ def start() {
+ initGenerator();
+
+ // Initialization of notificationBroker
+ notifyBroker = new NotificationBrokerImpl(null);
+ val brokerProperties = PropertiesUtils.newProperties();
+ notifyBrokerRegistration = brokerBundleContext.registerService(NotificationProviderService, notifyBroker,
+ brokerProperties)
+ brokerBundleContext.registerService(NotificationService, notifyBroker, brokerProperties)
+ }
+
+ def initGenerator() {
+
+ // YANG Binding Class Loader
+ clsPool.appendClassPath(new LoaderClassPath(RpcService.classLoader))
+ }
+
+ override registerConsumer(BindingAwareConsumer consumer, BundleContext bundleCtx) {
+ val ctx = consumer.createContext(bundleCtx)
+ consumer.onSessionInitialized(ctx)
+ return ctx
+ }
+
+ override registerProvider(BindingAwareProvider provider, BundleContext bundleCtx) {
+ val ctx = provider.createContext(bundleCtx)
+ provider.onSessionInitialized(ctx)
+ provider.onSessionInitiated(ctx as ProviderContext)
+ return ctx
+ }
+
+ private def createContext(BindingAwareConsumer consumer, BundleContext consumerCtx) {
+ new OsgiConsumerContext(consumerCtx, this)
+ }
+
+ private def createContext(BindingAwareProvider provider, BundleContext providerCtx) {
+ new OsgiProviderContext(providerCtx, this)
+ }
+
+ /**
+ * Returns a Managed Direct Proxy for supplied class
+ *
+ * Managed direct proxy is a generated proxy class conforming to the supplied interface
+ * which delegates all calls to the backing delegate.
+ *
+ * Proxy does not do any validation, null pointer checks or modifies data in any way, it
+ * is only use to avoid exposing direct references to backing implementation of service.
+ *
+ * If proxy class does not exist for supplied service class it will be generated automatically.
+ */
+ def <T extends RpcService> getManagedDirectProxy(Class<T> service) {
+
+ var RpcProxyContext existing = null
+ if ((existing = managedProxies.get(service)) != null) {
+ return existing.proxy
+ }
+ val proxyClass = service.generateDirectProxy()
+ val rpcProxyCtx = new RpcProxyContext(proxyClass)
+ val properties = new Hashtable<String, String>()
+ rpcProxyCtx.proxy = proxyClass.newInstance as RpcService
+
+ properties.salServiceType = Constants.SAL_SERVICE_TYPE_CONSUMER_PROXY
+ rpcProxyCtx.registration = brokerBundleContext.registerService(service, rpcProxyCtx.proxy as T, properties)
+ managedProxies.put(service, rpcProxyCtx)
+ return rpcProxyCtx.proxy
+ }
+
+ protected def generateDirectProxy(Class<? extends RpcService> delegate) {
+ val targetFqn = delegate.generatedName(Constants.PROXY_DIRECT_SUFFIX)
+ log.debug("Generating DirectProxy for {} Proxy name: {}",delegate,targetFqn);
+ val objCls = clsPool.get(Object)
+ val delegateCls = clsPool.get(delegate)
+ val proxyCls = clsPool.makeClass(targetFqn)
+ proxyCls.addInterface(delegateCls)
+ val delField = new CtField(delegateCls, DELEGATE_FIELD, proxyCls);
+ delField.modifiers = Modifier.PUBLIC
+ proxyCls.addField(delField)
+ delegateCls.methods.filter[it.declaringClass != objCls].forEach [
+ val proxyMethod = new CtMethod(it, proxyCls, null);
+ proxyMethod.body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
+ proxyCls.addMethod(proxyMethod)
+ ]
+ return proxyCls.toClass(delegate.classLoader)
+ }
+
+ /**
+ * Registers RPC Implementation
+ *
+ */
+ def <T extends RpcService> registerRpcImplementation(Class<T> type, T service, OsgiProviderContext context,
+ Hashtable<String, String> properties) {
+ val proxy = getManagedDirectProxy(type)
+ if(proxy.delegate != null) {
+ throw new IllegalStateException("Service " + type + "is already registered");
+ }
+ val osgiReg = context.bundleContext.registerService(type, service, properties);
+ proxy.delegate = service;
+ return new RpcServiceRegistrationImpl<T>(type, service, osgiReg);
+ }
+
+ /**
+ * Helper method to return delegate from ManagedDirectedProxy with use of reflection.
+ *
+ * Note: This method uses reflection, but access to delegate field should be
+ * avoided and called only if neccessary.
+ *
+ */
+ def <T extends RpcService> getDelegate(RpcService proxy) {
+ val field = proxy.class.getField(DELEGATE_FIELD)
+ if(field == null) throw new UnsupportedOperationException("Unable to get delegate from proxy");
+ return field.get(proxy) as T
+ }
+
+ /**
+ * Helper method to set delegate to ManagedDirectedProxy with use of reflection.
+ *
+ * Note: This method uses reflection, but setting delegate field should not occur too much
+ * to introduce any significant performance hits.
+ *
+ */
+ def void setDelegate(RpcService proxy, RpcService delegate) {
+ val field = proxy.class.getField(DELEGATE_FIELD)
+ if(field == null) throw new UnsupportedOperationException("Unable to set delegate to proxy");
+ if (field.type.isAssignableFrom(delegate.class)) {
+ field.set(proxy,delegate)
+ } else throw new IllegalArgumentException("delegate class is not assignable to proxy");
+ }
+
+
+}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.binding.impl;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
-import org.opendaylight.controller.sal.binding.api.BindingAwareService;
-import org.opendaylight.controller.sal.binding.spi.Mapper;
-import org.opendaylight.controller.sal.binding.spi.MappingProvider;
-import org.opendaylight.controller.sal.binding.spi.RpcMapper;
-import org.opendaylight.controller.sal.binding.spi.RpcMapper.RpcProxyInvocationHandler;
-import org.opendaylight.controller.sal.binding.spi.SALBindingModule;
-import org.opendaylight.controller.sal.common.util.Rpcs;
-import org.opendaylight.controller.sal.core.api.Provider;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.controller.yang.binding.DataObject;
-import org.opendaylight.controller.yang.binding.RpcService;
-import org.opendaylight.controller.yang.common.QName;
-import org.opendaylight.controller.yang.common.RpcResult;
-import org.opendaylight.controller.yang.data.api.CompositeNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BindingBrokerImpl implements BindingAwareBroker {
-
- private static Logger log = LoggerFactory
- .getLogger(BindingBrokerImpl.class);
-
- private Set<ConsumerSessionImpl> sessions = new HashSet<ConsumerSessionImpl>();
- private Set<ProviderSessionImpl> providerSessions = new HashSet<ProviderSessionImpl>();
-
- private Set<SALBindingModule> modules = new HashSet<SALBindingModule>();
- private Map<Class<? extends BindingAwareService>, SALBindingModule> salServiceProviders = new HashMap<Class<? extends BindingAwareService>, SALBindingModule>();
- private MappingProvider mapping;
- private BIFacade biFacade = new BIFacade();
- private org.opendaylight.controller.sal.core.api.Broker.ProviderSession biSession;
- private ExecutorService executor;
-
- Map<Class<? extends RpcService>, RpcService> rpcImpls = Collections
- .synchronizedMap(new HashMap<Class<? extends RpcService>, RpcService>());
-
- private RpcProxyInvocationHandler rpcProxyHandler = new RpcProxyInvocationHandler() {
-
- @Override
- public Future<RpcResult<? extends DataObject>> invokeRpc(
- RpcService proxy, QName rpc, DataObject input) {
- return rpcProxyInvoked(proxy, rpc, input);
- }
- };
-
- @Override
- public ConsumerSession registerConsumer(BindingAwareConsumer consumer) {
- checkPredicates(consumer);
- log.info("Registering consumer " + consumer);
-
- ConsumerSessionImpl session = newSessionFor(consumer);
- consumer.onSessionInitialized(session);
-
- sessions.add(session);
- return session;
- }
-
- @Override
- public ProviderSession registerProvider(BindingAwareProvider provider) {
- checkPredicates(provider);
-
- ProviderSessionImpl session = newSessionFor(provider);
- provider.onSessionInitiated(session);
-
- providerSessions.add(session);
- return session;
- }
-
- public void addModule(SALBindingModule module) {
- log.info("Registering broker module " + module);
- if (modules.contains(module)) {
- log.error("Module already registered");
- throw new IllegalArgumentException("Module already exists.");
- }
-
- Set<Class<? extends BindingAwareService>> provServices = module
- .getProvidedServices();
- for (Class<? extends BindingAwareService> serviceType : provServices) {
- log.info(" Registering session service implementation: "
- + serviceType.getCanonicalName());
- salServiceProviders.put(serviceType, module);
- }
- }
-
- public void consumerSessionClosed(ConsumerSessionImpl consumerSessionImpl) {
- sessions.remove(consumerSessionImpl);
- providerSessions.remove(consumerSessionImpl);
- }
-
- private void checkPredicates(BindingAwareProvider prov) {
- if (prov == null)
- throw new IllegalArgumentException("Provider should not be null.");
- for (ProviderSessionImpl session : providerSessions) {
- if (prov.equals(session.getProvider()))
- throw new IllegalStateException("Provider already registered");
- }
-
- }
-
- private void checkPredicates(BindingAwareConsumer cons) {
- if (cons == null)
- throw new IllegalArgumentException("Consumer should not be null.");
- for (ConsumerSessionImpl session : sessions) {
- if (cons.equals(session.getConsumer()))
- throw new IllegalStateException("Consumer already registered");
- }
- }
-
- private ConsumerSessionImpl newSessionFor(BindingAwareConsumer cons) {
- return new ConsumerSessionImpl(cons);
- }
-
- private ProviderSessionImpl newSessionFor(BindingAwareProvider provider) {
- return new ProviderSessionImpl(provider);
- }
-
- private <T extends BindingAwareService> T newSALServiceForSession(
- Class<T> service, ConsumerSession session) {
-
- SALBindingModule serviceProvider = salServiceProviders.get(service);
- if (serviceProvider == null) {
- return null;
- }
- return serviceProvider.getServiceForSession(service, session);
-
- }
-
- private <T extends RpcService> T newRpcProxyForSession(Class<T> service) {
-
- RpcMapper<T> mapper = mapping.rpcMapperForClass(service);
- if (mapper == null) {
- log.error("Mapper for " + service + "is unavailable.");
- return null;
- }
- T proxy = mapper.getConsumerProxy(rpcProxyHandler);
-
- return proxy;
- }
-
- private Future<RpcResult<? extends DataObject>> rpcProxyInvoked(
- RpcService rpcProxy, QName rpcType, DataObject inputData) {
- if (rpcProxy == null) {
- throw new IllegalArgumentException("Proxy must not be null");
- }
- if (rpcType == null) {
- throw new IllegalArgumentException(
- "rpcType (QName) should not be null");
- }
- Future<RpcResult<? extends DataObject>> ret = null;
-
- // Real invocation starts here
- RpcMapper<? extends RpcService> mapper = mapping
- .rpcMapperForProxy(rpcProxy);
- RpcService impl = rpcImpls.get(mapper.getServiceClass());
-
- if (impl == null) {
- // RPC is probably remote
- CompositeNode inputNode = null;
- Mapper<? extends DataObject> inputMapper = mapper.getInputMapper();
- if (inputMapper != null) {
- inputNode = inputMapper.domFromObject(inputData);
- }
- Future<RpcResult<CompositeNode>> biResult = biSession.rpc(rpcType,
- inputNode);
- ret = new TranslatedFuture(biResult, mapper);
-
- } else {
- // RPC is local
- Callable<RpcResult<? extends DataObject>> invocation = localRpcCallableFor(
- impl, mapper, rpcType, inputData);
- ret = executor.submit(invocation);
- }
- return ret;
- }
-
- private Callable<RpcResult<? extends DataObject>> localRpcCallableFor(
- final RpcService impl,
- final RpcMapper<? extends RpcService> mapper, final QName rpcType,
- final DataObject inputData) {
-
- return new Callable<RpcResult<? extends DataObject>>() {
-
- @Override
- public RpcResult<? extends DataObject> call() throws Exception {
- return mapper.invokeRpcImplementation(rpcType, impl, inputData);
- }
- };
- }
-
- // Binding Independent invocation of Binding Aware RPC
- private RpcResult<CompositeNode> invokeLocalRpc(QName rpc,
- CompositeNode inputNode) {
- RpcMapper<? extends RpcService> mapper = mapping.rpcMapperForData(rpc,
- inputNode);
-
- DataObject inputTO = mapper.getInputMapper().objectFromDom(inputNode);
-
- RpcService impl = rpcImpls.get(mapper.getServiceClass());
- if (impl == null) {
- log.warn("Implementation for rpc: " + rpc + "not available.");
- }
- RpcResult<? extends DataObject> result = mapper
- .invokeRpcImplementation(rpc, impl, inputTO);
- DataObject outputTO = result.getResult();
-
- CompositeNode outputNode = null;
- if (outputTO != null) {
- outputNode = mapper.getOutputMapper().domFromObject(outputTO);
- }
- return Rpcs.getRpcResult(result.isSuccessful(), outputNode,
- result.getErrors());
- }
-
- private class ConsumerSessionImpl implements
- BindingAwareBroker.ConsumerSession {
-
- private final BindingAwareConsumer consumer;
- private Map<Class<? extends BindingAwareService>, BindingAwareService> sessionSalServices = Collections
- .synchronizedMap(new HashMap<Class<? extends BindingAwareService>, BindingAwareService>());
-
- private Map<Class<? extends RpcService>, RpcService> sessionRpcProxies = Collections
- .synchronizedMap(new HashMap<Class<? extends RpcService>, RpcService>());
-
- public ConsumerSessionImpl(BindingAwareConsumer cons) {
- this.consumer = cons;
- }
-
- @Override
- public <T extends BindingAwareService> T getSALService(Class<T> service) {
-
- BindingAwareService serv = sessionSalServices.get(service);
- if (serv != null) {
- if (service.isInstance(serv)) {
- @SuppressWarnings("unchecked")
- T ret = (T) serv;
- return ret;
- } else {
- log.error("Implementation for service " + service.getName()
- + " does not implement the service interface");
- throw new IllegalStateException("Service implementation "
- + serv.getClass().getName() + "does not implement "
- + service.getName());
- }
- } else {
- T ret = BindingBrokerImpl.this.newSALServiceForSession(service,
- this);
- if (ret != null) {
- sessionSalServices.put(service, ret);
- }
- return ret;
- }
- }
-
- @Override
- public <T extends RpcService> T getRpcService(Class<T> service) {
- RpcService current = sessionRpcProxies.get(service);
- if (current != null) {
- if (service.isInstance(current)) {
- @SuppressWarnings("unchecked")
- T ret = (T) current;
- return ret;
- } else {
- log.error("Proxy for rpc service " + service.getName()
- + " does not implement the service interface");
- throw new IllegalStateException("Service implementation "
- + current.getClass().getName()
- + "does not implement " + service.getName());
- }
- } else {
- T ret = BindingBrokerImpl.this.newRpcProxyForSession(service);
- if (ret != null) {
- sessionRpcProxies.put(service, ret);
- }
- return ret;
- }
- }
-
- public BindingAwareConsumer getConsumer() {
- return this.consumer;
- }
-
- }
-
- private class ProviderSessionImpl extends ConsumerSessionImpl implements
- BindingAwareBroker.ProviderSession {
-
- private final BindingAwareProvider provider;
-
- public ProviderSessionImpl(BindingAwareProvider provider2) {
- super(null);
- this.provider = provider2;
- }
-
- @Override
- public void addRpcImplementation(RpcService implementation) {
- if (implementation == null) {
- throw new IllegalArgumentException(
- "Implementation should not be null");
- }
- // TODO Implement this method
- throw new UnsupportedOperationException("Not implemented");
- }
-
- @Override
- public void removeRpcImplementation(RpcService implementation) {
- if (implementation == null) {
- throw new IllegalArgumentException(
- "Implementation should not be null");
- }
- // TODO Implement this method
- throw new UnsupportedOperationException("Not implemented");
- }
-
- public BindingAwareProvider getProvider() {
- return this.provider;
- }
-
- }
-
- private class BIFacade implements Provider,RpcImplementation {
-
- @Override
- public Set<QName> getSupportedRpcs() {
- return Collections.emptySet();
- }
-
- @Override
- public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
- if (rpc == null) {
- throw new IllegalArgumentException(
- "Rpc type should not be null");
- }
-
- return BindingBrokerImpl.this.invokeLocalRpc(rpc, input);
- }
-
- @Override
- public void onSessionInitiated(
- org.opendaylight.controller.sal.core.api.Broker.ProviderSession session) {
-
- BindingBrokerImpl.this.biSession = session;
- for (SALBindingModule module : modules) {
- try {
- module.onBISessionAvailable(biSession);
- } catch(Exception e) {
- log.error("Module " +module +" throwed unexpected exception",e);
- }
- }
- }
-
- @Override
- public Collection<ProviderFunctionality> getProviderFunctionality() {
- return Collections.emptySet();
- }
-
- }
-
- private static class TranslatedFuture implements
- Future<RpcResult<? extends DataObject>> {
- private final Future<RpcResult<CompositeNode>> realFuture;
- private final RpcMapper<?> mapper;
-
- public TranslatedFuture(Future<RpcResult<CompositeNode>> future,
- RpcMapper<?> mapper) {
- realFuture = future;
- this.mapper = mapper;
- }
-
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return realFuture.cancel(mayInterruptIfRunning);
- }
-
- @Override
- public boolean isCancelled() {
- return realFuture.isCancelled();
- }
-
- @Override
- public boolean isDone() {
- return realFuture.isDone();
- }
-
- @Override
- public RpcResult<? extends DataObject> get()
- throws InterruptedException, ExecutionException {
- RpcResult<CompositeNode> val = realFuture.get();
- return tranlate(val);
- }
-
- @Override
- public RpcResult<? extends DataObject> get(long timeout, TimeUnit unit)
- throws InterruptedException, ExecutionException,
- TimeoutException {
- RpcResult<CompositeNode> val = realFuture.get(timeout, unit);
- return tranlate(val);
- }
-
- private RpcResult<? extends DataObject> tranlate(
- RpcResult<CompositeNode> result) {
- CompositeNode outputNode = result.getResult();
- DataObject outputTO = null;
- if (outputNode != null) {
- Mapper<?> outputMapper = mapper.getOutputMapper();
- outputTO = outputMapper.objectFromDom(outputNode);
- }
- return Rpcs.getRpcResult(result.isSuccessful(), outputTO,
- result.getErrors());
- }
-
- }
-}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import java.util.Hashtable;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BrokerActivator implements BundleActivator {
+
+ private static final Logger log = LoggerFactory.getLogger(BrokerActivator.class);
+ private BindingAwareBrokerImpl baSal;
+ private ServiceRegistration<BindingAwareBroker> baSalRegistration;
+
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ log.info("Binding Aware Broker initialized");
+ baSal = new BindingAwareBrokerImpl();
+ baSal.setBrokerBundleContext(context);
+ baSal.start();
+
+ BindingAwareBroker baSalService = baSal;
+ Hashtable<String, String> properties = new Hashtable<>();
+ this.baSalRegistration = context.registerService(BindingAwareBroker.class,baSalService, properties);
+
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ log.info("Binding Aware Broker stopped");
+ baSalRegistration.unregister();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+class Constants {
+
+ private new() {
+ }
+
+ public static val SAL_SERVICE_TYPE = "salServiceType"
+ public static val SAL_SERVICE_TYPE_CONSUMER_PROXY = "consumerProxy"
+ public static val SAL_SERVICE_TYPE_PROVIDER = "provider"
+ public static val SAL_SERVICE_TYPE_CONNECTOR = "connector"
+
+ public static val PROXY_DIRECT_SUFFIX = "DirectProxy";
+}
+++ /dev/null
-package org.opendaylight.controller.sal.binding.impl;
-
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.Future;
-
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
-import org.opendaylight.controller.sal.binding.api.BindingAwareService;
-import org.opendaylight.controller.sal.binding.api.DataBrokerService;
-import org.opendaylight.controller.sal.binding.api.DataCommitHandler;
-import org.opendaylight.controller.sal.binding.api.DataProviderService;
-import org.opendaylight.controller.sal.binding.api.DataValidator;
-import org.opendaylight.controller.sal.binding.spi.MappingProvider;
-import org.opendaylight.controller.sal.binding.spi.SALBindingModule;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
-import org.opendaylight.controller.sal.binding.api.DataRefresher;
-import org.opendaylight.controller.yang.binding.DataRoot;
-import org.opendaylight.controller.yang.common.RpcResult;
-import org.opendaylight.controller.yang.data.api.CompositeNode;
-
-public class DataModule implements SALBindingModule {
-
- private BindingAwareBroker broker;
- private org.opendaylight.controller.sal.core.api.Broker.ProviderSession biSession;
- private MappingProvider mappingProvider;
- private final BIFacade biFacade = new BIFacade();
- private org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService;
-
- @Override
- public void setBroker(BindingAwareBroker broker) {
- this.broker = broker;
- }
-
- @Override
- public void onBISessionAvailable(
- org.opendaylight.controller.sal.core.api.Broker.ProviderSession session) {
- this.biSession = session;
- this.biDataService = session
- .getService(org.opendaylight.controller.sal.core.api.data.DataProviderService.class);
- // biDataService.addRefresher(store, refresher)
-
- }
-
- @Override
- public void setMappingProvider(MappingProvider provider) {
- this.mappingProvider = provider;
-
- }
-
- @Override
- public Set<Class<? extends BindingAwareService>> getProvidedServices() {
- Set<Class<? extends BindingAwareService>> ret = new HashSet<Class<? extends BindingAwareService>>();
- ret.add(DataBrokerService.class);
- ret.add(DataProviderService.class);
- return ret;
- }
-
- @Override
- public <T extends BindingAwareService> T getServiceForSession(
- Class<T> service, ConsumerSession session) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {
- // TODO Auto-generated method stub
- return null;
- }
-
- private class DataBrokerSession implements DataBrokerService {
-
- @Override
- public <T extends DataRoot> T getData(DataStoreIdentifier store,
- Class<T> rootType) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public <T extends DataRoot> T getData(DataStoreIdentifier store,
- T filter) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public <T extends DataRoot> T getCandidateData(
- DataStoreIdentifier store, Class<T> rootType) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public <T extends DataRoot> T getCandidateData(
- DataStoreIdentifier store, T filter) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store,
- DataRoot changeSet) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {
- // TODO Auto-generated method stub
- return null;
- }
-
- }
-
- private class DataProviderSession extends DataBrokerSession implements
- DataProviderService {
-
- @Override
- public void addValidator(DataStoreIdentifier store,
- DataValidator validator) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void removeValidator(DataStoreIdentifier store,
- DataValidator validator) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void addCommitHandler(DataStoreIdentifier store,
- DataCommitHandler provider) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void removeCommitHandler(DataStoreIdentifier store,
- DataCommitHandler provider) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void addRefresher(DataStoreIdentifier store,
- DataRefresher refresher) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void removeRefresher(DataStoreIdentifier store,
- DataRefresher refresher) {
- // TODO Auto-generated method stub
-
- }
-
- }
-
- private class BIFacade
- implements
- org.opendaylight.controller.sal.core.api.data.DataCommitHandler,
- org.opendaylight.controller.sal.core.api.data.DataValidator,
- org.opendaylight.controller.sal.core.api.data.DataProviderService.DataRefresher {
-
- @Override
- public RpcResult<Void> validate(CompositeNode toValidate) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Set<DataStoreIdentifier> getSupportedDataStores() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public RpcResult<CommitTransaction> requestCommit(
- DataStoreIdentifier store) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public void refreshData() {
- // TODO Auto-generated method stub
-
- }
-
- }
-
-}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService
+import org.opendaylight.yangtools.yang.binding.Notification
+import com.google.common.collect.Multimap
+import org.opendaylight.controller.sal.binding.api.NotificationListener
+import com.google.common.collect.HashMultimap
+import java.util.concurrent.ExecutorService
+import java.util.Collection
+
+class NotificationBrokerImpl implements NotificationProviderService {
+
+ val Multimap<Class<? extends Notification>, NotificationListener<?>> listeners;
+ val ExecutorService executor;
+
+ new(ExecutorService executor) {
+ listeners = HashMultimap.create()
+ this.executor = executor;
+ }
+
+ override <T extends Notification> addNotificationListener(Class<T> notificationType,
+ NotificationListener<T> listener) {
+ listeners.put(notificationType, listener)
+ }
+
+ override <T extends Notification> removeNotificationListener(Class<T> notificationType,
+ NotificationListener<T> listener) {
+ listeners.remove(notificationType, listener)
+ }
+
+ override notify(Notification notification) {
+ notification.notificationTypes.forEach [
+ listeners.get(it as Class<? extends Notification>)?.notifyAll(notification)
+ ]
+ }
+
+ def getNotificationTypes(Notification notification) {
+ notification.class.interfaces.filter[it != Notification && Notification.isAssignableFrom(it)]
+ }
+
+ def notifyAll(Collection<NotificationListener<?>> listeners, Notification notification) {
+ listeners.forEach[(it as NotificationListener).onNotification(notification)]
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.binding.impl;
-
-import org.opendaylight.controller.sal.binding.spi.MappingProvider.MappingExtension;
-import org.opendaylight.controller.yang.binding.Notification;
-import org.opendaylight.controller.yang.binding.NotificationListener;
-
-public interface NotificationInvoker extends MappingExtension {
- void notify(Notification notification, NotificationListener listener);
-}
\ No newline at end of file
+++ /dev/null
-/*\r
- * Copyright (c) 2013 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.binding.impl;\r
-\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashSet;\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareService;\r
-import org.opendaylight.controller.sal.binding.api.NotificationProviderService;\r
-import org.opendaylight.controller.sal.binding.api.NotificationService;\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;\r
-import org.opendaylight.controller.sal.binding.spi.SALBindingModule;\r
-import org.opendaylight.controller.sal.binding.spi.Mapper;\r
-import org.opendaylight.controller.sal.binding.spi.MappingProvider;\r
-import org.opendaylight.controller.sal.binding.spi.MappingProvider.MappingExtensionFactory;\r
-\r
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
-import org.opendaylight.controller.yang.binding.DataObject;\r
-import org.opendaylight.controller.yang.binding.Notification;\r
-import org.opendaylight.controller.yang.binding.NotificationListener;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.google.common.collect.HashMultimap;\r
-import com.google.common.collect.Multimap;\r
-\r
-public class NotificationModule implements SALBindingModule {\r
-\r
- private ProviderSession biSession;\r
- private org.opendaylight.controller.sal.core.api.notify.NotificationProviderService biNotifyService;\r
- private MappingProvider mappingProvider;\r
-\r
- private Multimap<Class<? extends Notification>, NotificationListener> listeners = HashMultimap\r
- .create();\r
- private Set<QName> biNotifications = new HashSet<QName>();\r
- private static final Logger log = LoggerFactory\r
- .getLogger(NotificationModule.class);\r
- private final BindingIndependentListener biListener = new BindingIndependentListener();\r
- private BindingAwareBroker broker;\r
-\r
- @Override\r
- public Set<Class<? extends BindingAwareService>> getProvidedServices() {\r
-\r
- Set<Class<? extends BindingAwareService>> ret = new HashSet<Class<? extends BindingAwareService>>();\r
- ret.add(NotificationService.class);\r
- ret.add(NotificationProviderService.class);\r
- return ret;\r
- }\r
-\r
- @Override\r
- public <T extends BindingAwareService> T getServiceForSession(\r
- Class<T> service, ConsumerSession session) {\r
- if (service == null)\r
- throw new IllegalArgumentException("Service should not be null");\r
- if (session == null)\r
- throw new IllegalArgumentException("Session should not be null");\r
-\r
- if (NotificationProviderSession.class.equals(service)) {\r
- if (session instanceof org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderSession) {\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) new NotificationProviderSession(session);\r
- return ret;\r
- } else {\r
- throw new IllegalArgumentException(\r
- "NotificationProviderService is available only to ProviderSession");\r
- }\r
- }\r
-\r
- if (NotificationService.class.equals(service)) {\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) new NotificationSession(session);\r
- return ret;\r
- }\r
- return null;\r
- }\r
-\r
- @Override\r
- public Set<Class<? extends org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality>> getSupportedProviderFunctionality() {\r
- return Collections.emptySet();\r
- }\r
-\r
- @Override\r
- public void setBroker(BindingAwareBroker broker) {\r
- this.broker = broker;\r
- }\r
-\r
- @Override\r
- public void setMappingProvider(MappingProvider provider) {\r
- this.mappingProvider = provider;\r
- }\r
-\r
- @Override\r
- public void onBISessionAvailable(ProviderSession session) {\r
- biSession = session;\r
- if (biSession != null) {\r
- biNotifyService = session\r
- .getService(org.opendaylight.controller.sal.core.api.notify.NotificationProviderService.class);\r
- }\r
- }\r
-\r
- private void notify(Notification notification) {\r
- notifyBindingIndependent(notification);\r
- notifyBindingAware(notification);\r
- }\r
-\r
- private void notifyBindingAware(Notification notification) {\r
- Class<? extends Notification> type = notification.getClass();\r
- Collection<NotificationListener> toNotify = listeners.get(type);\r
-\r
- // Invocation of notification on registered listeners\r
- if (toNotify != null) {\r
-\r
- // We get factory for Notification Invoker\r
- MappingExtensionFactory<NotificationInvoker> invokerFactory = mappingProvider\r
- .getExtensionFactory(NotificationInvoker.class);\r
-\r
- // We get generated invoker for NoficiationListener interface\r
- // associated to Notification Type\r
- NotificationInvoker invoker = invokerFactory.forClass(type);\r
- for (NotificationListener listener : toNotify) {\r
- try {\r
- // Invoker invokes the right method on subtype of\r
- // NotificationListener\r
- // associated to the type of notification\r
- invoker.notify(notification, listener);\r
- } catch (Exception e) {\r
-\r
- }\r
- }\r
- }\r
- }\r
-\r
- private void notifyBindingIndependent(Notification notification) {\r
- Class<? extends Notification> type = notification.getClass();\r
-\r
- if (biSession == null) {\r
- return;\r
- }\r
- if (biSession.isClosed()) {\r
- return;\r
- }\r
- if (biNotifyService == null) {\r
- return;\r
- }\r
-\r
- // FIXME: Somehow we need to resolve this for class hierarchy.\r
- // probably use type.getInterfaces()\r
- Mapper<? extends Notification> mapper = mappingProvider\r
- .mapperForClass(type);\r
- CompositeNode domNotification = mapper.domFromObject(notification);\r
-\r
- biNotifyService.sendNotification(domNotification);\r
- }\r
-\r
- private void addBAListener(Class<? extends Notification> notificationType,\r
- NotificationListener listener) {\r
-\r
- listeners.put(notificationType, listener);\r
- Mapper<? extends Notification> mapper = mappingProvider\r
- .mapperForClass(notificationType);\r
- QName biType = mapper.getQName();\r
- if (false == biNotifications.contains(biType)) {\r
- // The listener is not registered for binding independent\r
- // notification\r
- biNotifications.add(biType);\r
-\r
- if (biNotifyService != null) {\r
- biNotifyService.addNotificationListener(biType, biListener);\r
- }\r
- }\r
-\r
- }\r
-\r
- private void removeBAListener(\r
- Class<? extends Notification> notificationType,\r
- NotificationListener listener) {\r
- listeners.remove(notificationType, listener);\r
- }\r
-\r
- private class NotificationSession implements NotificationService {\r
- private final ConsumerSession session;\r
- private Multimap<Class<? extends Notification>, NotificationListener> sessionListeners = HashMultimap\r
- .create();\r
-\r
- public NotificationSession(ConsumerSession session) {\r
- this.session = session;\r
- }\r
-\r
- @Override\r
- public void addNotificationListener(\r
- Class<? extends Notification> notificationType,\r
- NotificationListener listener) {\r
-\r
- NotificationModule.this.addBAListener(notificationType, listener);\r
- sessionListeners.put(notificationType, listener);\r
-\r
- }\r
-\r
- @Override\r
- public void removeNotificationListener(\r
- Class<? extends Notification> notificationType,\r
- NotificationListener listener) {\r
- sessionListeners.remove(notificationType, listener);\r
- NotificationModule.this\r
- .removeBAListener(notificationType, listener);\r
- }\r
-\r
- }\r
-\r
- private class NotificationProviderSession extends NotificationSession\r
- implements NotificationProviderService {\r
-\r
- public NotificationProviderSession(ConsumerSession session) {\r
- super(session);\r
- }\r
-\r
- @Override\r
- public void notify(Notification notification) {\r
- NotificationModule.this.notify(notification);\r
- }\r
-\r
- }\r
-\r
- private class BindingIndependentListener\r
- implements\r
- org.opendaylight.controller.sal.core.api.notify.NotificationListener {\r
-\r
- @Override\r
- public Set<QName> getSupportedNotifications() {\r
- return biNotifications;\r
- }\r
-\r
- @Override\r
- public void onNotification(CompositeNode notification) {\r
- NotificationModule.this\r
- .onBindingIndependentNotification(notification);\r
- }\r
-\r
- }\r
-\r
- private void onBindingIndependentNotification(CompositeNode biNotification) {\r
- QName biType = biNotification.getNodeType();\r
-\r
- Mapper<DataObject> mapper = mappingProvider.mapperForQName(biType);\r
- if (mapper == null) {\r
- log.info("Received notification does not have a binding defined.");\r
- return;\r
- }\r
- Class<DataObject> type = mapper.getDataObjectClass();\r
-\r
- // We check if the received QName / type is really Notification\r
- if (Notification.class.isAssignableFrom(type)) {\r
- Notification notification = (Notification) mapper\r
- .objectFromDom(biNotification);\r
- notifyBindingAware(notification);\r
- } else {\r
- // The generated type for this QName does not inherits from\r
- // notification something went wrong - generated APIs and/or\r
- // provider sending notification\r
- // which was incorectly described in the YANG schema.\r
- log.error("Received notification " + biType\r
- + " is not binded as notification");\r
- }\r
-\r
- }\r
-}\r
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+import org.opendaylight.controller.sal.binding.api.NotificationService
+import org.opendaylight.controller.sal.binding.api.NotificationListener
+import org.opendaylight.yangtools.yang.binding.Notification
+import com.google.common.collect.Multimap
+import com.google.common.collect.HashMultimap
+
+class NotificationServiceImpl implements NotificationService {
+ val Multimap<Class<? extends Notification>, NotificationListener<?>> listeners;
+
+ new() {
+ listeners = HashMultimap.create()
+ }
+
+ override <T extends Notification> addNotificationListener(Class<T> notificationType,
+ NotificationListener<T> listener) {
+ listeners.put(notificationType, listener)
+ }
+
+ override <T extends Notification> removeNotificationListener(Class<T> notificationType,
+ NotificationListener<T> listener) {
+ listeners.remove(notificationType, listener)
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.LoggerFactory
+
+class OsgiConsumerContext implements ConsumerContext {
+
+ static val log = LoggerFactory.getLogger(OsgiConsumerContext)
+ protected val BundleContext bundleContext;
+ protected val BindingAwareBrokerImpl broker;
+
+ new(BundleContext ctx,BindingAwareBrokerImpl broker) {
+ this.bundleContext = ctx;
+ this.broker = broker;
+ }
+
+
+ override def <T extends BindingAwareService> getSALService(Class<T> service) {
+ // SAL Services are global
+ var ref = bundleContext.getServiceReference(service);
+ return bundleContext.getService(ref) as T;
+ }
+
+
+
+ override def <T extends RpcService> T getRpcService(Class<T> module) {
+ try {
+
+ val services = bundleContext.getServiceReferences(module, getProxyFilter());
+
+ // Proxy service found / using first implementation
+ // FIXME: Add advanced logic to retrieve service with right set of models
+ if(false == services.empty) {
+ val ref = services.iterator().next() as ServiceReference<T>;
+ return bundleContext.getService(ref) as T;
+ }
+ } catch (InvalidSyntaxException e) {
+ log.error("Created filter was invalid:", e.message,e)
+ }
+ return null;
+
+
+ }
+
+ private def getProxyFilter() {
+ return '''(«Constants.SAL_SERVICE_TYPE»=«Constants.SAL_SERVICE_TYPE_CONSUMER_PROXY»)'''
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcServiceRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+
+import static extension org.opendaylight.controller.sal.binding.impl.utils.PropertiesUtils.*;
+
+class OsgiProviderContext extends OsgiConsumerContext implements ProviderContext {
+
+ @Property
+ val Map<Class<? extends RpcService>, RpcServiceRegistrationImpl<? extends RpcService>> registeredServices
+
+ new(BundleContext ctx, BindingAwareBrokerImpl broker) {
+ super(ctx, broker);
+ _registeredServices = new HashMap();
+ }
+
+ override def <T extends RpcService> RpcServiceRegistration<T> addRpcImplementation(Class<T> type, T implementation) {
+
+ // TODO Auto-generated method stub
+ val properties = new Hashtable<String, String>();
+ properties.salServiceType = Constants.SAL_SERVICE_TYPE_PROVIDER
+
+ // Fill requirements
+ val salReg = broker.registerRpcImplementation(type, implementation, this, properties)
+ registeredServices.put(type, salReg)
+ return salReg;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+import org.opendaylight.yangtools.yang.binding.RpcService
+import org.osgi.framework.ServiceRegistration
+
+class RpcProxyContext {
+
+ new(Class<? extends RpcService> proxyClass) {
+ this.proxyClass = proxyClass
+ }
+
+ protected val Class<? extends RpcService> proxyClass;
+
+ @Property
+ protected var RpcService proxy;
+
+ @Property
+ protected var ServiceRegistration<? extends RpcService> registration;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcServiceRegistration
+import org.osgi.framework.ServiceRegistration
+import org.opendaylight.yangtools.yang.binding.RpcService
+
+class RpcServiceRegistrationImpl<T extends RpcService> implements RpcServiceRegistration<T> {
+
+ val ServiceRegistration<T> osgiRegistration;
+ private val T service;
+ val Class<T> cls;
+
+ public new(Class<T> type, T service, ServiceRegistration<T> osgiReg) {
+ this.cls = type;
+ this.osgiRegistration = osgiReg;
+ this.service = service;
+ }
+
+ override getService() {
+ this.service
+ }
+
+ override unregister() {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl.utils
+
+import javassist.ClassPool
+import javassist.NotFoundException
+import javassist.LoaderClassPath
+
+class GeneratorUtils {
+
+ static val PREFIX = "_gen.";
+
+ public static def generatedName(Class<?> cls, String suffix) {
+ '''«PREFIX»«cls.package.name».«cls.simpleName»$«suffix»'''.toString()
+ }
+
+ public static def get(ClassPool pool, Class<?> cls) {
+ try {
+ return pool.get(cls.name)
+ } catch (NotFoundException e) {
+ pool.appendClassPath(new LoaderClassPath(cls.classLoader));
+ return pool.get(cls.name)
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl.utils
+
+import java.util.Hashtable
+import org.opendaylight.controller.sal.binding.impl.Constants
+
+class PropertiesUtils {
+
+ private new() {}
+
+ static def setSalServiceType(Hashtable<String,String> properties, String value) {
+ properties.put(Constants.SAL_SERVICE_TYPE,value)
+ return properties
+ }
+
+ static def getSalServiceType(Hashtable<String,String> properties) {
+ return properties.get(Constants.SAL_SERVICE_TYPE)
+ }
+
+ static def newProperties() {
+ new Hashtable<String,String>()
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl.utils;
\ No newline at end of file
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
+ <artifactId>sal-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>sal-binding-spi</artifactId>
package org.opendaylight.controller.sal.binding.spi;
import org.opendaylight.controller.concepts.lang.Transformer;
-import org.opendaylight.controller.yang.binding.DataObject;
-import org.opendaylight.controller.yang.common.QName;
-import org.opendaylight.controller.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
public interface DataDomToJavaTransformer<P extends DataObject> extends Transformer<CompositeNode, P> {
package org.opendaylight.controller.sal.binding.spi;
import org.opendaylight.controller.concepts.lang.InputClassBasedTransformer;
-import org.opendaylight.controller.yang.binding.DataObject;
-import org.opendaylight.controller.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
public interface JavaToDataDomTransformer<I extends DataObject> extends
InputClassBasedTransformer<DataObject, I, CompositeNode> {
*/
package org.opendaylight.controller.sal.binding.spi;
-import org.opendaylight.controller.yang.binding.DataObject;
-import org.opendaylight.controller.yang.common.QName;
-import org.opendaylight.controller.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
/**
* Translator between Binding-Independent format and generated Binding Data Objects
*
*/
package org.opendaylight.controller.sal.binding.spi;
-import org.opendaylight.controller.yang.binding.DataObject;
-import org.opendaylight.controller.yang.binding.RpcService;
-import org.opendaylight.controller.yang.common.QName;
-import org.opendaylight.controller.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
public interface MappingProvider {
import java.util.concurrent.Future;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.yang.binding.DataObject;
-import org.opendaylight.controller.yang.binding.RpcService;
-import org.opendaylight.controller.yang.common.QName;
-import org.opendaylight.controller.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
public interface RpcMapper<T extends RpcService> {
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.binding.spi;
-
-import java.util.Set;
-
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.BindingAwareService;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-
-public interface SALBindingModule {
-
- void setBroker(BindingAwareBroker broker);
- void onBISessionAvailable(ProviderSession session);
-
- void setMappingProvider(MappingProvider provider);
-
- Set<Class<? extends BindingAwareService>> getProvidedServices();
-
- <T extends BindingAwareService> T getServiceForSession(Class<T> service,
- ConsumerSession session);
-
- Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality();
-}
<modelVersion>4.0.0</modelVersion>\r
<parent>\r
<groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
+ <artifactId>sal-parent</artifactId>\r
<version>1.0-SNAPSHOT</version>\r
</parent>\r
<artifactId>sal-broker-impl</artifactId>\r
<artifactId>guava</artifactId>\r
<type>jar</type>\r
</dependency>\r
- </dependencies>
+ </dependencies>\r
</project>
\ No newline at end of file
-/*\r
- * Copyright (c) 2013 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.impl;\r
-\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.concurrent.Callable;\r
-import java.util.concurrent.ExecutorService;\r
-import java.util.concurrent.Future;\r
-import org.opendaylight.controller.sal.core.api.Broker;\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-import org.opendaylight.controller.sal.core.api.Consumer;\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-import org.opendaylight.controller.sal.core.api.RpcImplementation;\r
-import org.opendaylight.controller.sal.core.spi.BrokerModule;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-public class BrokerImpl implements Broker {\r
- private static Logger log = LoggerFactory.getLogger(BrokerImpl.class);\r
-\r
- // Broker Generic Context\r
- private Set<ConsumerSessionImpl> sessions = Collections\r
- .synchronizedSet(new HashSet<ConsumerSessionImpl>());\r
- private Set<ProviderSessionImpl> providerSessions = Collections\r
- .synchronizedSet(new HashSet<ProviderSessionImpl>());\r
- private Set<BrokerModule> modules = Collections\r
- .synchronizedSet(new HashSet<BrokerModule>());\r
- private Map<Class<? extends BrokerService>, BrokerModule> serviceProviders = Collections\r
- .synchronizedMap(new HashMap<Class<? extends BrokerService>, BrokerModule>());\r
-\r
- // RPC Context\r
- private Map<QName, RpcImplementation> rpcImpls = Collections\r
- .synchronizedMap(new HashMap<QName, RpcImplementation>());\r
-\r
- // Implementation specific\r
- private ExecutorService executor;\r
-\r
- @Override\r
- public ConsumerSession registerConsumer(Consumer consumer) {\r
- checkPredicates(consumer);\r
- log.info("Registering consumer " + consumer);\r
- ConsumerSessionImpl session = newSessionFor(consumer);\r
- consumer.onSessionInitiated(session);\r
- sessions.add(session);\r
- return session;\r
- }\r
-\r
- @Override\r
- public ProviderSession registerProvider(Provider provider) {\r
- checkPredicates(provider);\r
-\r
- ProviderSessionImpl session = newSessionFor(provider);\r
- provider.onSessionInitiated(session);\r
- providerSessions.add(session);\r
- return session;\r
- }\r
-\r
- public void addModule(BrokerModule module) {\r
- log.info("Registering broker module " + module);\r
- if (modules.contains(module)) {\r
- log.error("Module already registered");\r
- throw new IllegalArgumentException("Module already exists.");\r
- }\r
- \r
- Set<Class<? extends BrokerService>> provServices = module\r
- .getProvidedServices();\r
- for (Class<? extends BrokerService> serviceType : provServices) {\r
- log.info(" Registering session service implementation: "\r
- + serviceType.getCanonicalName());\r
- serviceProviders.put(serviceType, module);\r
- }\r
- }\r
-\r
- public <T extends BrokerService> T serviceFor(Class<T> service,\r
- ConsumerSessionImpl session) {\r
- BrokerModule prov = serviceProviders.get(service);\r
- if (prov == null) {\r
- log.warn("Service " + service.toString() + " is not supported");\r
- return null;\r
- }\r
- return prov.getServiceForSession(service, session);\r
- }\r
-\r
- // RPC Functionality\r
- \r
- private void addRpcImplementation(QName rpcType,\r
- RpcImplementation implementation) {\r
- synchronized (rpcImpls) {\r
- if (rpcImpls.get(rpcType) != null) {\r
- throw new IllegalStateException("Implementation for rpc "\r
- + rpcType + " is already registered.");\r
- }\r
- rpcImpls.put(rpcType, implementation);\r
- }\r
- // TODO Add notification for availability of Rpc Implementation\r
- }\r
-\r
- private void removeRpcImplementation(QName rpcType,\r
- RpcImplementation implToRemove) {\r
- synchronized (rpcImpls) {\r
- if (implToRemove == rpcImpls.get(rpcType)) {\r
- rpcImpls.remove(rpcType);\r
- }\r
- }\r
- // TODO Add notification for removal of Rpc Implementation\r
- }\r
-\r
- private Future<RpcResult<CompositeNode>> invokeRpc(QName rpc,\r
- CompositeNode input) {\r
- RpcImplementation impl = rpcImpls.get(rpc);\r
- // if()\r
-\r
- Callable<RpcResult<CompositeNode>> call = callableFor(impl,\r
- rpc, input);\r
- Future<RpcResult<CompositeNode>> result = executor.submit(call);\r
-\r
- return result;\r
- }\r
- \r
- // Validation\r
-\r
- private void checkPredicates(Provider prov) {\r
- if (prov == null)\r
- throw new IllegalArgumentException("Provider should not be null.");\r
- for (ProviderSessionImpl session : providerSessions) {\r
- if (prov.equals(session.getProvider()))\r
- throw new IllegalStateException("Provider already registered");\r
- }\r
-\r
- }\r
-\r
- private void checkPredicates(Consumer cons) {\r
- if (cons == null)\r
- throw new IllegalArgumentException("Consumer should not be null.");\r
- for (ConsumerSessionImpl session : sessions) {\r
- if (cons.equals(session.getConsumer()))\r
- throw new IllegalStateException("Consumer already registered");\r
- }\r
- }\r
-\r
- // Private Factory methods\r
- \r
- private ConsumerSessionImpl newSessionFor(Consumer cons) {\r
- return new ConsumerSessionImpl(cons);\r
- }\r
-\r
- private ProviderSessionImpl newSessionFor(Provider provider) {\r
- return new ProviderSessionImpl(provider);\r
- }\r
-\r
- private void consumerSessionClosed(ConsumerSessionImpl consumerSessionImpl) {\r
- sessions.remove(consumerSessionImpl);\r
- providerSessions.remove(consumerSessionImpl);\r
- }\r
-\r
- private static Callable<RpcResult<CompositeNode>> callableFor(\r
- final RpcImplementation implemenation, final QName rpc,\r
- final CompositeNode input) {\r
-\r
- return new Callable<RpcResult<CompositeNode>>() {\r
-\r
- @Override\r
- public RpcResult<CompositeNode> call() throws Exception {\r
- return implemenation.invokeRpc(rpc, input);\r
- }\r
- };\r
- }\r
- \r
- private class ConsumerSessionImpl implements ConsumerSession {\r
-\r
- private final Consumer consumer;\r
-\r
- private Map<Class<? extends BrokerService>, BrokerService> instantiatedServices = Collections\r
- .synchronizedMap(new HashMap<Class<? extends BrokerService>, BrokerService>());\r
- private boolean closed = false;\r
-\r
- public Consumer getConsumer() {\r
- return consumer;\r
- }\r
-\r
- public ConsumerSessionImpl(Consumer consumer) {\r
- this.consumer = consumer;\r
- }\r
-\r
- @Override\r
- public Future<RpcResult<CompositeNode>> rpc(QName rpc,\r
- CompositeNode input) {\r
- return BrokerImpl.this.invokeRpc(rpc, input);\r
- }\r
-\r
- @Override\r
- public <T extends BrokerService> T getService(Class<T> service) {\r
- BrokerService potential = instantiatedServices.get(service);\r
- if (potential != null) {\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) potential;\r
- return ret;\r
- }\r
- T ret = BrokerImpl.this.serviceFor(service, this);\r
- if (ret != null) {\r
- instantiatedServices.put(service, ret);\r
- }\r
- return ret;\r
- }\r
-\r
- @Override\r
- public void close() {\r
- Collection<BrokerService> toStop = instantiatedServices.values();\r
- this.closed = true;\r
- for (BrokerService brokerService : toStop) {\r
- brokerService.closeSession();\r
- }\r
- BrokerImpl.this.consumerSessionClosed(this);\r
- }\r
-\r
- @Override\r
- public boolean isClosed() {\r
- return closed;\r
- }\r
-\r
- }\r
-\r
- private class ProviderSessionImpl extends ConsumerSessionImpl implements\r
- ProviderSession {\r
-\r
- private Provider provider;\r
- private Map<QName, RpcImplementation> sessionRpcImpls = Collections.synchronizedMap(new HashMap<QName, RpcImplementation>());\r
-\r
- public ProviderSessionImpl(Provider provider) {\r
- super(null);\r
- this.provider = provider;\r
- }\r
-\r
- @Override\r
- public void addRpcImplementation(QName rpcType,\r
- RpcImplementation implementation)\r
- throws IllegalArgumentException {\r
- if (rpcType == null) {\r
- throw new IllegalArgumentException("rpcType must not be null");\r
- }\r
- if (implementation == null) {\r
- throw new IllegalArgumentException(\r
- "Implementation must not be null");\r
- }\r
- BrokerImpl.this.addRpcImplementation(rpcType, implementation);\r
- sessionRpcImpls.put(rpcType, implementation);\r
- }\r
-\r
- @Override\r
- public void removeRpcImplementation(QName rpcType,\r
- RpcImplementation implToRemove) throws IllegalArgumentException {\r
- RpcImplementation localImpl = rpcImpls.get(rpcType);\r
- if (localImpl != implToRemove) {\r
- throw new IllegalStateException(\r
- "Implementation was not registered in this session");\r
- }\r
-\r
- BrokerImpl.this.removeRpcImplementation(rpcType, implToRemove);\r
- sessionRpcImpls.remove(rpcType);\r
- }\r
-\r
- public Provider getProvider() {\r
- return this.provider;\r
- }\r
-\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.spi.BrokerModule;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BrokerImpl implements Broker {
+ private static Logger log = LoggerFactory.getLogger(BrokerImpl.class);
+
+ // Broker Generic Context
+ private Set<ConsumerSessionImpl> sessions = Collections
+ .synchronizedSet(new HashSet<ConsumerSessionImpl>());
+ private Set<ProviderSessionImpl> providerSessions = Collections
+ .synchronizedSet(new HashSet<ProviderSessionImpl>());
+ private Set<BrokerModule> modules = Collections
+ .synchronizedSet(new HashSet<BrokerModule>());
+ private Map<Class<? extends BrokerService>, BrokerModule> serviceProviders = Collections
+ .synchronizedMap(new HashMap<Class<? extends BrokerService>, BrokerModule>());
+
+ // RPC Context
+ private Map<QName, RpcImplementation> rpcImpls = Collections
+ .synchronizedMap(new HashMap<QName, RpcImplementation>());
+
+ // Implementation specific
+ private ExecutorService executor;
+
+ @Override
+ public ConsumerSession registerConsumer(Consumer consumer) {
+ checkPredicates(consumer);
+ log.info("Registering consumer " + consumer);
+ ConsumerSessionImpl session = newSessionFor(consumer);
+ consumer.onSessionInitiated(session);
+ sessions.add(session);
+ return session;
+ }
+
+ @Override
+ public ProviderSession registerProvider(Provider provider) {
+ checkPredicates(provider);
+
+ ProviderSessionImpl session = newSessionFor(provider);
+ provider.onSessionInitiated(session);
+ providerSessions.add(session);
+ return session;
+ }
+
+ public void addModule(BrokerModule module) {
+ log.info("Registering broker module " + module);
+ if (modules.contains(module)) {
+ log.error("Module already registered");
+ throw new IllegalArgumentException("Module already exists.");
+ }
+
+ Set<Class<? extends BrokerService>> provServices = module
+ .getProvidedServices();
+ for (Class<? extends BrokerService> serviceType : provServices) {
+ log.info(" Registering session service implementation: "
+ + serviceType.getCanonicalName());
+ serviceProviders.put(serviceType, module);
+ }
+ }
+
+ public <T extends BrokerService> T serviceFor(Class<T> service,
+ ConsumerSessionImpl session) {
+ BrokerModule prov = serviceProviders.get(service);
+ if (prov == null) {
+ log.warn("Service " + service.toString() + " is not supported");
+ return null;
+ }
+ return prov.getServiceForSession(service, session);
+ }
+
+ // RPC Functionality
+
+ private void addRpcImplementation(QName rpcType,
+ RpcImplementation implementation) {
+ synchronized (rpcImpls) {
+ if (rpcImpls.get(rpcType) != null) {
+ throw new IllegalStateException("Implementation for rpc "
+ + rpcType + " is already registered.");
+ }
+ rpcImpls.put(rpcType, implementation);
+ }
+ // TODO Add notification for availability of Rpc Implementation
+ }
+
+ private void removeRpcImplementation(QName rpcType,
+ RpcImplementation implToRemove) {
+ synchronized (rpcImpls) {
+ if (implToRemove == rpcImpls.get(rpcType)) {
+ rpcImpls.remove(rpcType);
+ }
+ }
+ // TODO Add notification for removal of Rpc Implementation
+ }
+
+ private Future<RpcResult<CompositeNode>> invokeRpc(QName rpc,
+ CompositeNode input) {
+ RpcImplementation impl = rpcImpls.get(rpc);
+ // if()
+
+ Callable<RpcResult<CompositeNode>> call = callableFor(impl,
+ rpc, input);
+ Future<RpcResult<CompositeNode>> result = executor.submit(call);
+
+ return result;
+ }
+
+ // Validation
+
+ private void checkPredicates(Provider prov) {
+ if (prov == null)
+ throw new IllegalArgumentException("Provider should not be null.");
+ for (ProviderSessionImpl session : providerSessions) {
+ if (prov.equals(session.getProvider()))
+ throw new IllegalStateException("Provider already registered");
+ }
+
+ }
+
+ private void checkPredicates(Consumer cons) {
+ if (cons == null)
+ throw new IllegalArgumentException("Consumer should not be null.");
+ for (ConsumerSessionImpl session : sessions) {
+ if (cons.equals(session.getConsumer()))
+ throw new IllegalStateException("Consumer already registered");
+ }
+ }
+
+ // Private Factory methods
+
+ private ConsumerSessionImpl newSessionFor(Consumer cons) {
+ return new ConsumerSessionImpl(cons);
+ }
+
+ private ProviderSessionImpl newSessionFor(Provider provider) {
+ return new ProviderSessionImpl(provider);
+ }
+
+ private void consumerSessionClosed(ConsumerSessionImpl consumerSessionImpl) {
+ sessions.remove(consumerSessionImpl);
+ providerSessions.remove(consumerSessionImpl);
+ }
+
+ private static Callable<RpcResult<CompositeNode>> callableFor(
+ final RpcImplementation implemenation, final QName rpc,
+ final CompositeNode input) {
+
+ return new Callable<RpcResult<CompositeNode>>() {
+
+ @Override
+ public RpcResult<CompositeNode> call() throws Exception {
+ return implemenation.invokeRpc(rpc, input);
+ }
+ };
+ }
+
+ private class ConsumerSessionImpl implements ConsumerSession {
+
+ private final Consumer consumer;
+
+ private Map<Class<? extends BrokerService>, BrokerService> instantiatedServices = Collections
+ .synchronizedMap(new HashMap<Class<? extends BrokerService>, BrokerService>());
+ private boolean closed = false;
+
+ public Consumer getConsumer() {
+ return consumer;
+ }
+
+ public ConsumerSessionImpl(Consumer consumer) {
+ this.consumer = consumer;
+ }
+
+ @Override
+ public Future<RpcResult<CompositeNode>> rpc(QName rpc,
+ CompositeNode input) {
+ return BrokerImpl.this.invokeRpc(rpc, input);
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(Class<T> service) {
+ BrokerService potential = instantiatedServices.get(service);
+ if (potential != null) {
+ @SuppressWarnings("unchecked")
+ T ret = (T) potential;
+ return ret;
+ }
+ T ret = BrokerImpl.this.serviceFor(service, this);
+ if (ret != null) {
+ instantiatedServices.put(service, ret);
+ }
+ return ret;
+ }
+
+ @Override
+ public void close() {
+ Collection<BrokerService> toStop = instantiatedServices.values();
+ this.closed = true;
+ for (BrokerService brokerService : toStop) {
+ brokerService.closeSession();
+ }
+ BrokerImpl.this.consumerSessionClosed(this);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ }
+
+ private class ProviderSessionImpl extends ConsumerSessionImpl implements
+ ProviderSession {
+
+ private Provider provider;
+ private Map<QName, RpcImplementation> sessionRpcImpls = Collections.synchronizedMap(new HashMap<QName, RpcImplementation>());
+
+ public ProviderSessionImpl(Provider provider) {
+ super(null);
+ this.provider = provider;
+ }
+
+ @Override
+ public void addRpcImplementation(QName rpcType,
+ RpcImplementation implementation)
+ throws IllegalArgumentException {
+ if (rpcType == null) {
+ throw new IllegalArgumentException("rpcType must not be null");
+ }
+ if (implementation == null) {
+ throw new IllegalArgumentException(
+ "Implementation must not be null");
+ }
+ BrokerImpl.this.addRpcImplementation(rpcType, implementation);
+ sessionRpcImpls.put(rpcType, implementation);
+ }
+
+ @Override
+ public void removeRpcImplementation(QName rpcType,
+ RpcImplementation implToRemove) throws IllegalArgumentException {
+ RpcImplementation localImpl = rpcImpls.get(rpcType);
+ if (localImpl != implToRemove) {
+ throw new IllegalStateException(
+ "Implementation was not registered in this session");
+ }
+
+ BrokerImpl.this.removeRpcImplementation(rpcType, implToRemove);
+ sessionRpcImpls.remove(rpcType);
+ }
+
+ public Provider getProvider() {
+ return this.provider;
+ }
+
+ }
+}
-/*\r
- * Copyright (c) 2013 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.impl;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.concurrent.ExecutorService;\r
-import java.util.concurrent.Future;\r
-\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-import org.opendaylight.controller.sal.common.util.Rpcs;\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
-import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;\r
-import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;\r
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService;\r
-import org.opendaylight.controller.sal.core.api.data.DataCommitHandler;\r
-import org.opendaylight.controller.sal.core.api.data.DataProviderService;\r
-import org.opendaylight.controller.sal.core.api.data.DataValidator;\r
-import org.opendaylight.controller.sal.core.api.data.DataCommitHandler.CommitTransaction;\r
-import org.opendaylight.controller.sal.core.api.data.DataProviderService.DataRefresher;\r
-import org.opendaylight.controller.sal.core.spi.BrokerModule;\r
-import org.opendaylight.controller.yang.common.RpcError;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-import org.opendaylight.controller.yang.data.api.CompositeNodeModification;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.google.common.collect.ImmutableSet;\r
-\r
-public class DataBrokerModule implements BrokerModule {\r
-\r
- private static final Logger log = LoggerFactory\r
- .getLogger(DataBrokerModule.class);\r
-\r
- private static final Set<Class<? extends ProviderFunctionality>> SUPPORTED_PROVIDER_FUNCTIONALITY = ImmutableSet\r
- .of((Class<? extends ProviderFunctionality>) DataValidator.class,\r
- DataRefresher.class, DataCommitHandler.class);\r
-\r
- private static final Set<Class<? extends BrokerService>> PROVIDED_SESSION_SERVICES = ImmutableSet\r
- .of((Class<? extends BrokerService>) DataBrokerService.class,\r
- DataProviderService.class);\r
-\r
- private Map<DataStoreIdentifier, StoreContext> storeContext;\r
-\r
- private ExecutorService executor;\r
- \r
- private SequentialCommitHandlerCoordinator coordinator = new SequentialCommitHandlerCoordinator();\r
-\r
- @Override\r
- public Set<Class<? extends BrokerService>> getProvidedServices() {\r
- return PROVIDED_SESSION_SERVICES;\r
- }\r
-\r
- @Override\r
- public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {\r
- return SUPPORTED_PROVIDER_FUNCTIONALITY;\r
- }\r
-\r
- @Override\r
- public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {\r
- return Collections.emptySet();\r
- }\r
-\r
- @Override\r
- public <T extends BrokerService> T getServiceForSession(Class<T> service,\r
- ConsumerSession session) {\r
- if (DataProviderService.class.equals(service)\r
- && session instanceof ProviderSession) {\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) newDataProviderService(session);\r
- return ret;\r
- } else if (DataBrokerService.class.equals(service)) {\r
-\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) newDataConsumerService(session);\r
- return ret;\r
- }\r
-\r
- throw new IllegalArgumentException(\r
- "The requested session-specific service is not provided by this module.");\r
- }\r
-\r
- private DataProviderService newDataProviderService(ConsumerSession session) {\r
- return new DataProviderSession();\r
- }\r
-\r
- private DataBrokerService newDataConsumerService(ConsumerSession session) {\r
- return new DataConsumerSession();\r
- }\r
-\r
- private StoreContext context(DataStoreIdentifier store) {\r
- return storeContext.get(store);\r
- }\r
-\r
- private static class StoreContext {\r
- private Set<DataCommitHandler> commitHandlers = Collections\r
- .synchronizedSet(new HashSet<DataCommitHandler>());\r
- private Set<DataValidator> validators = Collections\r
- .synchronizedSet(new HashSet<DataValidator>());\r
- private Set<DataRefresher> refreshers = Collections\r
- .synchronizedSet(new HashSet<DataRefresher>());\r
- }\r
-\r
- private class DataConsumerSession implements DataBrokerService {\r
-\r
- @Override\r
- public CompositeNode getData(DataStoreIdentifier store) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public CompositeNode getData(DataStoreIdentifier store,\r
- CompositeNode filter) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public CompositeNode getCandidateData(DataStoreIdentifier store) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public CompositeNode getCandidateData(DataStoreIdentifier store,\r
- CompositeNode filter) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public RpcResult<CompositeNode> editCandidateData(\r
- DataStoreIdentifier store, CompositeNodeModification changeSet) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public void closeSession() {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public Set<DataStoreIdentifier> getDataStores() {\r
- // TODO Auto-generated method stub\r
- return null;\r
- }\r
-\r
- }\r
-\r
- private class DataProviderSession extends DataConsumerSession implements\r
- DataProviderService {\r
-\r
- private Set<DataCommitHandler> providerCommitHandlers = new HashSet<DataCommitHandler>();\r
- private Set<DataValidator> providerValidators = new HashSet<DataValidator>();\r
- private Set<DataRefresher> providerRefreshers = new HashSet<DataRefresher>();\r
-\r
- @Override\r
- public void addValidator(DataStoreIdentifier store,\r
- DataValidator validator) {\r
- if (validator == null)\r
- throw new IllegalArgumentException(\r
- "Validator should not be null");\r
-\r
- providerValidators.add(validator);\r
- context(store).validators.add(validator);\r
- }\r
-\r
- @Override\r
- public void removeValidator(DataStoreIdentifier store,\r
- DataValidator validator) {\r
- if (validator == null)\r
- throw new IllegalArgumentException(\r
- "Validator should not be null");\r
-\r
- providerValidators.remove(validator);\r
- context(store).validators.remove(validator);\r
- }\r
-\r
- @Override\r
- public void addCommitHandler(DataStoreIdentifier store,\r
- DataCommitHandler provider) {\r
- if (provider == null)\r
- throw new IllegalArgumentException(\r
- "CommitHandler should not be null");\r
-\r
- providerCommitHandlers.add(provider);\r
- context(store).commitHandlers.add(provider);\r
- }\r
-\r
- @Override\r
- public void removeCommitHandler(DataStoreIdentifier store,\r
- DataCommitHandler provider) {\r
- if (provider == null)\r
- throw new IllegalArgumentException(\r
- "CommitHandler should not be null");\r
-\r
- providerCommitHandlers.remove(provider);\r
- context(store).commitHandlers.remove(provider);\r
- }\r
-\r
- @Override\r
- public void addRefresher(DataStoreIdentifier store,\r
- DataRefresher provider) {\r
- if (provider == null)\r
- throw new IllegalArgumentException(\r
- "Refresher should not be null");\r
-\r
- providerRefreshers.add(provider);\r
- context(store).refreshers.add(provider);\r
- }\r
-\r
- @Override\r
- public void removeRefresher(DataStoreIdentifier store,\r
- DataRefresher provider) {\r
- if (provider == null)\r
- throw new IllegalArgumentException(\r
- "Refresher should not be null");\r
-\r
- providerRefreshers.remove(provider);\r
- context(store).refreshers.remove(provider);\r
- }\r
-\r
- }\r
-\r
- private class SequentialCommitHandlerCoordinator implements\r
- DataCommitHandler {\r
-\r
- @Override\r
- public RpcResult<CommitTransaction> requestCommit(\r
- DataStoreIdentifier store) {\r
- List<RpcError> errors = new ArrayList<RpcError>();\r
- Set<CommitTransaction> transactions = new HashSet<DataCommitHandler.CommitTransaction>();\r
- boolean successful = true;\r
-\r
- for (DataCommitHandler commitHandler : context(store).commitHandlers) {\r
- try {\r
- RpcResult<CommitTransaction> partialResult = commitHandler\r
- .requestCommit(store);\r
- successful = partialResult.isSuccessful() & successful;\r
- if (partialResult.isSuccessful()) {\r
- transactions.add(partialResult.getResult());\r
- }\r
-\r
- errors.addAll(partialResult.getErrors());\r
- } catch (Exception e) {\r
- log.error("Uncaught exception prevented commit request."\r
- + e.getMessage(), e);\r
- successful = false;\r
- // FIXME: Add RPC Error with exception.\r
- }\r
- if (successful == false)\r
- break;\r
- }\r
- CommitTransaction transaction = new SequentialCommitTransaction(\r
- store, transactions);\r
- return Rpcs.getRpcResult(successful, transaction, errors);\r
- }\r
-\r
- @Override\r
- public Set<DataStoreIdentifier> getSupportedDataStores() {\r
- return Collections.emptySet();\r
- }\r
- }\r
-\r
- private class SequentialCommitTransaction implements CommitTransaction {\r
-\r
- final Set<CommitTransaction> transactions;\r
- final DataStoreIdentifier store;\r
-\r
- public SequentialCommitTransaction(DataStoreIdentifier s,\r
- Set<CommitTransaction> t) {\r
- transactions = t;\r
- store = s;\r
- }\r
-\r
- @Override\r
- public RpcResult<Void> finish() {\r
- List<RpcError> errors = new ArrayList<RpcError>();\r
- boolean successful = true;\r
-\r
- for (CommitTransaction commitHandler : transactions) {\r
- try {\r
- RpcResult<Void> partialResult = commitHandler.finish();\r
- successful = partialResult.isSuccessful() & successful;\r
- errors.addAll(partialResult.getErrors());\r
- } catch (Exception e) {\r
- log.error(\r
- "Uncaught exception prevented finishing of commit."\r
- + e.getMessage(), e);\r
- successful = false;\r
- // FIXME: Add RPC Error with exception.\r
- }\r
- if (successful == false)\r
- break;\r
- }\r
-\r
- return Rpcs.getRpcResult(successful, null, errors);\r
- }\r
-\r
- @Override\r
- public RpcResult<Void> rollback() {\r
- List<RpcError> errors = new ArrayList<RpcError>();\r
- boolean successful = true;\r
-\r
- for (CommitTransaction commitHandler : transactions) {\r
- try {\r
- RpcResult<Void> partialResult = commitHandler.rollback();\r
- successful = partialResult.isSuccessful() & successful;\r
- errors.addAll(partialResult.getErrors());\r
- } catch (Exception e) {\r
- log.error(\r
- "Uncaught exception prevented rollback of commit."\r
- + e.getMessage(), e);\r
- successful = false;\r
- // FIXME: Add RPC Error with exception.\r
- }\r
- if (successful == false)\r
- break;\r
- }\r
-\r
- return Rpcs.getRpcResult(successful, null, errors);\r
- }\r
-\r
- @Override\r
- public DataStoreIdentifier getDataStore() {\r
- return this.store;\r
- }\r
-\r
- @Override\r
- public DataCommitHandler getHandler() {\r
- return coordinator;\r
- }\r
- }\r
-\r
- private class ValidationCoordinator implements DataValidator {\r
-\r
- private final DataStoreIdentifier store;\r
-\r
- ValidationCoordinator(DataStoreIdentifier store) {\r
- this.store = store;\r
- }\r
-\r
- @Override\r
- public RpcResult<Void> validate(CompositeNode toValidate) {\r
- List<RpcError> errors = new ArrayList<RpcError>();\r
- boolean successful = true;\r
-\r
- for (DataValidator validator : context(store).validators) {\r
- try {\r
- RpcResult<Void> partialResult = validator\r
- .validate(toValidate);\r
- successful = partialResult.isSuccessful() & successful;\r
- errors.addAll(partialResult.getErrors());\r
- } catch (Exception e) {\r
- log.error(\r
- "Uncaught exception prevented validation."\r
- + e.getMessage(), e);\r
- successful = false;\r
- // FIXME: Add RPC Error with exception.\r
- }\r
- if (successful == false)\r
- break;\r
- }\r
-\r
- return Rpcs.getRpcResult(successful, null, errors);\r
- }\r
-\r
- @Override\r
- public Set<DataStoreIdentifier> getSupportedDataStores() {\r
- return Collections.emptySet();\r
- }\r
-\r
- }\r
-\r
- private class DataRefreshCoordinator implements DataRefresher {\r
-\r
- private final DataStoreIdentifier store;\r
-\r
- DataRefreshCoordinator(DataStoreIdentifier store) {\r
- this.store = store;\r
- }\r
-\r
- @Override\r
- public void refreshData() {\r
-\r
- for (DataRefresher refresher : context(store).refreshers) {\r
- try {\r
- refresher.refreshData();\r
- } catch (Exception e) {\r
- log.error(\r
- "Uncaught exception during refresh of data: "\r
- + e.getMessage(), e);\r
- }\r
-\r
- }\r
- }\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;
+import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;
+import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.core.api.data.DataCommitHandler;
+import org.opendaylight.controller.sal.core.api.data.DataProviderService;
+import org.opendaylight.controller.sal.core.api.data.DataValidator;
+import org.opendaylight.controller.sal.core.api.data.DataCommitHandler.CommitTransaction;
+import org.opendaylight.controller.sal.core.api.data.DataProviderService.DataRefresher;
+import org.opendaylight.controller.sal.core.spi.BrokerModule;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
+
+public class DataBrokerModule implements BrokerModule {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(DataBrokerModule.class);
+
+ private static final Set<Class<? extends ProviderFunctionality>> SUPPORTED_PROVIDER_FUNCTIONALITY = ImmutableSet
+ .of((Class<? extends ProviderFunctionality>) DataValidator.class,
+ DataRefresher.class, DataCommitHandler.class);
+
+ private static final Set<Class<? extends BrokerService>> PROVIDED_SESSION_SERVICES = ImmutableSet
+ .of((Class<? extends BrokerService>) DataBrokerService.class,
+ DataProviderService.class);
+
+ private Map<DataStoreIdentifier, StoreContext> storeContext;
+
+ private ExecutorService executor;
+
+ private SequentialCommitHandlerCoordinator coordinator = new SequentialCommitHandlerCoordinator();
+
+ @Override
+ public Set<Class<? extends BrokerService>> getProvidedServices() {
+ return PROVIDED_SESSION_SERVICES;
+ }
+
+ @Override
+ public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {
+ return SUPPORTED_PROVIDER_FUNCTIONALITY;
+ }
+
+ @Override
+ public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public <T extends BrokerService> T getServiceForSession(Class<T> service,
+ ConsumerSession session) {
+ if (DataProviderService.class.equals(service)
+ && session instanceof ProviderSession) {
+ @SuppressWarnings("unchecked")
+ T ret = (T) newDataProviderService(session);
+ return ret;
+ } else if (DataBrokerService.class.equals(service)) {
+
+ @SuppressWarnings("unchecked")
+ T ret = (T) newDataConsumerService(session);
+ return ret;
+ }
+
+ throw new IllegalArgumentException(
+ "The requested session-specific service is not provided by this module.");
+ }
+
+ private DataProviderService newDataProviderService(ConsumerSession session) {
+ return new DataProviderSession();
+ }
+
+ private DataBrokerService newDataConsumerService(ConsumerSession session) {
+ return new DataConsumerSession();
+ }
+
+ private StoreContext context(DataStoreIdentifier store) {
+ return storeContext.get(store);
+ }
+
+ private static class StoreContext {
+ private Set<DataCommitHandler> commitHandlers = Collections
+ .synchronizedSet(new HashSet<DataCommitHandler>());
+ private Set<DataValidator> validators = Collections
+ .synchronizedSet(new HashSet<DataValidator>());
+ private Set<DataRefresher> refreshers = Collections
+ .synchronizedSet(new HashSet<DataRefresher>());
+ }
+
+ private class DataConsumerSession implements DataBrokerService {
+
+ @Override
+ public CompositeNode getData(DataStoreIdentifier store) {
+ // TODO Implement this method
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public CompositeNode getData(DataStoreIdentifier store,
+ CompositeNode filter) {
+ // TODO Implement this method
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public CompositeNode getCandidateData(DataStoreIdentifier store) {
+ // TODO Implement this method
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public CompositeNode getCandidateData(DataStoreIdentifier store,
+ CompositeNode filter) {
+ // TODO Implement this method
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public RpcResult<CompositeNode> editCandidateData(
+ DataStoreIdentifier store, MutableCompositeNode changeSet) {
+ // TODO Implement this method
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {
+ // TODO Implement this method
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public void closeSession() {
+ // TODO Implement this method
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Set<DataStoreIdentifier> getDataStores() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+
+ private class DataProviderSession extends DataConsumerSession implements
+ DataProviderService {
+
+ private Set<DataCommitHandler> providerCommitHandlers = new HashSet<DataCommitHandler>();
+ private Set<DataValidator> providerValidators = new HashSet<DataValidator>();
+ private Set<DataRefresher> providerRefreshers = new HashSet<DataRefresher>();
+
+ @Override
+ public void addValidator(DataStoreIdentifier store,
+ DataValidator validator) {
+ if (validator == null)
+ throw new IllegalArgumentException(
+ "Validator should not be null");
+
+ providerValidators.add(validator);
+ context(store).validators.add(validator);
+ }
+
+ @Override
+ public void removeValidator(DataStoreIdentifier store,
+ DataValidator validator) {
+ if (validator == null)
+ throw new IllegalArgumentException(
+ "Validator should not be null");
+
+ providerValidators.remove(validator);
+ context(store).validators.remove(validator);
+ }
+
+ @Override
+ public void addCommitHandler(DataStoreIdentifier store,
+ DataCommitHandler provider) {
+ if (provider == null)
+ throw new IllegalArgumentException(
+ "CommitHandler should not be null");
+
+ providerCommitHandlers.add(provider);
+ context(store).commitHandlers.add(provider);
+ }
+
+ @Override
+ public void removeCommitHandler(DataStoreIdentifier store,
+ DataCommitHandler provider) {
+ if (provider == null)
+ throw new IllegalArgumentException(
+ "CommitHandler should not be null");
+
+ providerCommitHandlers.remove(provider);
+ context(store).commitHandlers.remove(provider);
+ }
+
+ @Override
+ public void addRefresher(DataStoreIdentifier store,
+ DataRefresher provider) {
+ if (provider == null)
+ throw new IllegalArgumentException(
+ "Refresher should not be null");
+
+ providerRefreshers.add(provider);
+ context(store).refreshers.add(provider);
+ }
+
+ @Override
+ public void removeRefresher(DataStoreIdentifier store,
+ DataRefresher provider) {
+ if (provider == null)
+ throw new IllegalArgumentException(
+ "Refresher should not be null");
+
+ providerRefreshers.remove(provider);
+ context(store).refreshers.remove(provider);
+ }
+
+ }
+
+ private class SequentialCommitHandlerCoordinator implements
+ DataCommitHandler {
+
+ @Override
+ public RpcResult<CommitTransaction> requestCommit(
+ DataStoreIdentifier store) {
+ List<RpcError> errors = new ArrayList<RpcError>();
+ Set<CommitTransaction> transactions = new HashSet<DataCommitHandler.CommitTransaction>();
+ boolean successful = true;
+
+ for (DataCommitHandler commitHandler : context(store).commitHandlers) {
+ try {
+ RpcResult<CommitTransaction> partialResult = commitHandler
+ .requestCommit(store);
+ successful = partialResult.isSuccessful() & successful;
+ if (partialResult.isSuccessful()) {
+ transactions.add(partialResult.getResult());
+ }
+
+ errors.addAll(partialResult.getErrors());
+ } catch (Exception e) {
+ log.error("Uncaught exception prevented commit request."
+ + e.getMessage(), e);
+ successful = false;
+ // FIXME: Add RPC Error with exception.
+ }
+ if (successful == false)
+ break;
+ }
+ CommitTransaction transaction = new SequentialCommitTransaction(
+ store, transactions);
+ return Rpcs.getRpcResult(successful, transaction, errors);
+ }
+
+ @Override
+ public Set<DataStoreIdentifier> getSupportedDataStores() {
+ return Collections.emptySet();
+ }
+ }
+
+ private class SequentialCommitTransaction implements CommitTransaction {
+
+ final Set<CommitTransaction> transactions;
+ final DataStoreIdentifier store;
+
+ public SequentialCommitTransaction(DataStoreIdentifier s,
+ Set<CommitTransaction> t) {
+ transactions = t;
+ store = s;
+ }
+
+ @Override
+ public RpcResult<Void> finish() {
+ List<RpcError> errors = new ArrayList<RpcError>();
+ boolean successful = true;
+
+ for (CommitTransaction commitHandler : transactions) {
+ try {
+ RpcResult<Void> partialResult = commitHandler.finish();
+ successful = partialResult.isSuccessful() & successful;
+ errors.addAll(partialResult.getErrors());
+ } catch (Exception e) {
+ log.error(
+ "Uncaught exception prevented finishing of commit."
+ + e.getMessage(), e);
+ successful = false;
+ // FIXME: Add RPC Error with exception.
+ }
+ if (successful == false)
+ break;
+ }
+
+ return Rpcs.getRpcResult(successful, null, errors);
+ }
+
+ @Override
+ public RpcResult<Void> rollback() {
+ List<RpcError> errors = new ArrayList<RpcError>();
+ boolean successful = true;
+
+ for (CommitTransaction commitHandler : transactions) {
+ try {
+ RpcResult<Void> partialResult = commitHandler.rollback();
+ successful = partialResult.isSuccessful() & successful;
+ errors.addAll(partialResult.getErrors());
+ } catch (Exception e) {
+ log.error(
+ "Uncaught exception prevented rollback of commit."
+ + e.getMessage(), e);
+ successful = false;
+ // FIXME: Add RPC Error with exception.
+ }
+ if (successful == false)
+ break;
+ }
+
+ return Rpcs.getRpcResult(successful, null, errors);
+ }
+
+ @Override
+ public DataStoreIdentifier getDataStore() {
+ return this.store;
+ }
+
+ @Override
+ public DataCommitHandler getHandler() {
+ return coordinator;
+ }
+ }
+
+ private class ValidationCoordinator implements DataValidator {
+
+ private final DataStoreIdentifier store;
+
+ ValidationCoordinator(DataStoreIdentifier store) {
+ this.store = store;
+ }
+
+ @Override
+ public RpcResult<Void> validate(CompositeNode toValidate) {
+ List<RpcError> errors = new ArrayList<RpcError>();
+ boolean successful = true;
+
+ for (DataValidator validator : context(store).validators) {
+ try {
+ RpcResult<Void> partialResult = validator
+ .validate(toValidate);
+ successful = partialResult.isSuccessful() & successful;
+ errors.addAll(partialResult.getErrors());
+ } catch (Exception e) {
+ log.error(
+ "Uncaught exception prevented validation."
+ + e.getMessage(), e);
+ successful = false;
+ // FIXME: Add RPC Error with exception.
+ }
+ if (successful == false)
+ break;
+ }
+
+ return Rpcs.getRpcResult(successful, null, errors);
+ }
+
+ @Override
+ public Set<DataStoreIdentifier> getSupportedDataStores() {
+ return Collections.emptySet();
+ }
+
+ }
+
+ private class DataRefreshCoordinator implements DataRefresher {
+
+ private final DataStoreIdentifier store;
+
+ DataRefreshCoordinator(DataStoreIdentifier store) {
+ this.store = store;
+ }
+
+ @Override
+ public void refreshData() {
+
+ for (DataRefresher refresher : context(store).refreshers) {
+ try {
+ refresher.refreshData();
+ } catch (Exception e) {
+ log.error(
+ "Uncaught exception during refresh of data: "
+ + e.getMessage(), e);
+ }
+
+ }
+ }
+ }
+}
-/*\r
- * Copyright (c) 2013 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.impl;\r
-\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashSet;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
-import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;\r
-import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;\r
-import org.opendaylight.controller.sal.core.api.notify.NotificationListener;\r
-import org.opendaylight.controller.sal.core.api.notify.NotificationProviderService;\r
-import org.opendaylight.controller.sal.core.api.notify.NotificationService;\r
-import org.opendaylight.controller.sal.core.spi.BrokerModule;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.google.common.collect.HashMultimap;\r
-import com.google.common.collect.ImmutableSet;\r
-import com.google.common.collect.Multimap;\r
-\r
-public class NotificationModule implements BrokerModule {\r
- private static Logger log = LoggerFactory\r
- .getLogger(NotificationModule.class);\r
-\r
- private Multimap<QName, NotificationListener> listeners = HashMultimap\r
- .create();\r
-\r
- private static final Set<Class<? extends BrokerService>> PROVIDED_SERVICE_TYPE = ImmutableSet\r
- .of((Class<? extends BrokerService>) NotificationService.class,\r
- NotificationProviderService.class);\r
-\r
- private static final Set<Class<? extends ConsumerFunctionality>> SUPPORTED_CONSUMER_FUNCTIONALITY = ImmutableSet\r
- .of((Class<? extends ConsumerFunctionality>) NotificationListener.class,\r
- NotificationListener.class); // Workaround: if we use the\r
- // version of method with only\r
- // one argument, the generics\r
- // inference will not work\r
-\r
- @Override\r
- public Set<Class<? extends BrokerService>> getProvidedServices() {\r
- return PROVIDED_SERVICE_TYPE;\r
- }\r
-\r
- @Override\r
- public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {\r
- return SUPPORTED_CONSUMER_FUNCTIONALITY;\r
- }\r
-\r
- @Override\r
- public <T extends BrokerService> T getServiceForSession(Class<T> service,\r
- ConsumerSession session) {\r
- if (NotificationProviderService.class.equals(service)\r
- && session instanceof ProviderSession) {\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) newNotificationProviderService(session);\r
- return ret;\r
- } else if (NotificationService.class.equals(service)) {\r
-\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) newNotificationConsumerService(session);\r
- return ret;\r
- }\r
-\r
- throw new IllegalArgumentException(\r
- "The requested session-specific service is not provided by this module.");\r
- }\r
-\r
- private void sendNotification(CompositeNode notification) {\r
- QName type = notification.getNodeType();\r
- Collection<NotificationListener> toNotify = listeners.get(type);\r
- log.info("Publishing notification " + type);\r
-\r
- if (toNotify == null) {\r
- // No listeners were registered - returns.\r
- return;\r
- }\r
-\r
- for (NotificationListener listener : toNotify) {\r
- try {\r
- // FIXME: ensure that notification is immutable\r
- listener.onNotification(notification);\r
- } catch (Exception e) {\r
- log.error("Uncaught exception in NotificationListener", e);\r
- }\r
- }\r
-\r
- }\r
-\r
- private NotificationService newNotificationConsumerService(\r
- ConsumerSession session) {\r
- return new NotificationConsumerSessionImpl();\r
- }\r
-\r
- private NotificationProviderService newNotificationProviderService(\r
- ConsumerSession session) {\r
- return new NotificationProviderSessionImpl();\r
- }\r
-\r
- private class NotificationConsumerSessionImpl implements\r
- NotificationService {\r
-\r
- private Multimap<QName, NotificationListener> consumerListeners = HashMultimap\r
- .create();\r
- private boolean closed = false;\r
-\r
- @Override\r
- public void addNotificationListener(QName notification,\r
- NotificationListener listener) {\r
- checkSessionState();\r
- if (notification == null) {\r
- throw new IllegalArgumentException(\r
- "Notification type must not be null.");\r
- }\r
- if (listener == null) {\r
- throw new IllegalArgumentException("Listener must not be null.");\r
- }\r
-\r
- consumerListeners.put(notification, listener);\r
- listeners.put(notification, listener);\r
- log.info("Registered listener for notification: " + notification);\r
- }\r
-\r
- @Override\r
- public void removeNotificationListener(QName notification,\r
- NotificationListener listener) {\r
- checkSessionState();\r
- if (notification == null) {\r
- throw new IllegalArgumentException(\r
- "Notification type must not be null.");\r
- }\r
- if (listener == null) {\r
- throw new IllegalArgumentException("Listener must not be null.");\r
- }\r
- consumerListeners.remove(notification, listener);\r
- listeners.remove(notification, listener);\r
- }\r
-\r
- @Override\r
- public void closeSession() {\r
- closed = true;\r
- Map<QName, Collection<NotificationListener>> toRemove = consumerListeners\r
- .asMap();\r
- for (Entry<QName, Collection<NotificationListener>> entry : toRemove\r
- .entrySet()) {\r
- listeners.remove(entry.getKey(), entry.getValue());\r
- }\r
- }\r
-\r
- protected void checkSessionState() {\r
- if (closed)\r
- throw new IllegalStateException("Session is closed");\r
- }\r
- }\r
-\r
- private class NotificationProviderSessionImpl extends\r
- NotificationConsumerSessionImpl implements\r
- NotificationProviderService {\r
-\r
- @Override\r
- public void sendNotification(CompositeNode notification) {\r
- checkSessionState();\r
- if (notification == null)\r
- throw new IllegalArgumentException(\r
- "Notification must not be null.");\r
- NotificationModule.this.sendNotification(notification);\r
- }\r
- }\r
-\r
- @Override\r
- public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {\r
- return Collections.emptySet();\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;
+import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;
+import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
+import org.opendaylight.controller.sal.core.api.notify.NotificationProviderService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationService;
+import org.opendaylight.controller.sal.core.spi.BrokerModule;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+
+public class NotificationModule implements BrokerModule {
+ private static Logger log = LoggerFactory
+ .getLogger(NotificationModule.class);
+
+ private Multimap<QName, NotificationListener> listeners = HashMultimap
+ .create();
+
+ private static final Set<Class<? extends BrokerService>> PROVIDED_SERVICE_TYPE = ImmutableSet
+ .of((Class<? extends BrokerService>) NotificationService.class,
+ NotificationProviderService.class);
+
+ private static final Set<Class<? extends ConsumerFunctionality>> SUPPORTED_CONSUMER_FUNCTIONALITY = ImmutableSet
+ .of((Class<? extends ConsumerFunctionality>) NotificationListener.class,
+ NotificationListener.class); // Workaround: if we use the
+ // version of method with only
+ // one argument, the generics
+ // inference will not work
+
+ @Override
+ public Set<Class<? extends BrokerService>> getProvidedServices() {
+ return PROVIDED_SERVICE_TYPE;
+ }
+
+ @Override
+ public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {
+ return SUPPORTED_CONSUMER_FUNCTIONALITY;
+ }
+
+ @Override
+ public <T extends BrokerService> T getServiceForSession(Class<T> service,
+ ConsumerSession session) {
+ if (NotificationProviderService.class.equals(service)
+ && session instanceof ProviderSession) {
+ @SuppressWarnings("unchecked")
+ T ret = (T) newNotificationProviderService(session);
+ return ret;
+ } else if (NotificationService.class.equals(service)) {
+
+ @SuppressWarnings("unchecked")
+ T ret = (T) newNotificationConsumerService(session);
+ return ret;
+ }
+
+ throw new IllegalArgumentException(
+ "The requested session-specific service is not provided by this module.");
+ }
+
+ private void sendNotification(CompositeNode notification) {
+ QName type = notification.getNodeType();
+ Collection<NotificationListener> toNotify = listeners.get(type);
+ log.info("Publishing notification " + type);
+
+ if (toNotify == null) {
+ // No listeners were registered - returns.
+ return;
+ }
+
+ for (NotificationListener listener : toNotify) {
+ try {
+ // FIXME: ensure that notification is immutable
+ listener.onNotification(notification);
+ } catch (Exception e) {
+ log.error("Uncaught exception in NotificationListener", e);
+ }
+ }
+
+ }
+
+ private NotificationService newNotificationConsumerService(
+ ConsumerSession session) {
+ return new NotificationConsumerSessionImpl();
+ }
+
+ private NotificationProviderService newNotificationProviderService(
+ ConsumerSession session) {
+ return new NotificationProviderSessionImpl();
+ }
+
+ private class NotificationConsumerSessionImpl implements
+ NotificationService {
+
+ private Multimap<QName, NotificationListener> consumerListeners = HashMultimap
+ .create();
+ private boolean closed = false;
+
+ @Override
+ public void addNotificationListener(QName notification,
+ NotificationListener listener) {
+ checkSessionState();
+ if (notification == null) {
+ throw new IllegalArgumentException(
+ "Notification type must not be null.");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null.");
+ }
+
+ consumerListeners.put(notification, listener);
+ listeners.put(notification, listener);
+ log.info("Registered listener for notification: " + notification);
+ }
+
+ @Override
+ public void removeNotificationListener(QName notification,
+ NotificationListener listener) {
+ checkSessionState();
+ if (notification == null) {
+ throw new IllegalArgumentException(
+ "Notification type must not be null.");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null.");
+ }
+ consumerListeners.remove(notification, listener);
+ listeners.remove(notification, listener);
+ }
+
+ @Override
+ public void closeSession() {
+ closed = true;
+ Map<QName, Collection<NotificationListener>> toRemove = consumerListeners
+ .asMap();
+ for (Entry<QName, Collection<NotificationListener>> entry : toRemove
+ .entrySet()) {
+ listeners.remove(entry.getKey(), entry.getValue());
+ }
+ }
+
+ protected void checkSessionState() {
+ if (closed)
+ throw new IllegalStateException("Session is closed");
+ }
+ }
+
+ private class NotificationProviderSessionImpl extends
+ NotificationConsumerSessionImpl implements
+ NotificationProviderService {
+
+ @Override
+ public void sendNotification(CompositeNode notification) {
+ checkSessionState();
+ if (notification == null)
+ throw new IllegalArgumentException(
+ "Notification must not be null.");
+ NotificationModule.this.sendNotification(notification);
+ }
+ }
+
+ @Override
+ public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {
+ return Collections.emptySet();
+ }
+}
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
- <modelVersion>4.0.0</modelVersion>\r
- <parent>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </parent>\r
- <artifactId>sal-common-util</artifactId>\r
-\r
- <dependencies>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>yang-common</artifactId>\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-common</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
+<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.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-common-util</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
</dependencies>
+ <packaging>bundle</packaging>
</project>
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.common.util;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class Futures {
+
+ private Futures(){}
+
+ public static <T> Future<T> immediateFuture(T result) {
+ return new ImmediateFuture<T>(result);
+ }
+
+ private static class ImmediateFuture<T> implements Future<T> {
+
+ private final T result;
+
+ public ImmediateFuture(T result) {
+ this.result = result;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ return result;
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
+ return result;
+ }
+
+ }
+}
-/*\r
- * Copyright (c) 2013 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.common.util;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import org.opendaylight.controller.yang.common.RpcError;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-\r
-public class Rpcs {\r
- public static <T> RpcResult<T> getRpcResult(boolean successful, T result,\r
- Collection<RpcError> errors) {\r
- RpcResult<T> ret = new RpcResultTO<T>(successful, result, errors);\r
- return ret;\r
- }\r
-\r
- private static class RpcResultTO<T> implements RpcResult<T> {\r
-\r
- private final Collection<RpcError> errors;\r
- private final T result;\r
- private final boolean successful;\r
-\r
- public RpcResultTO(boolean successful, T result,\r
- Collection<RpcError> errors) {\r
- this.successful = successful;\r
- this.result = result;\r
- this.errors = Collections.unmodifiableList(new ArrayList<RpcError>(\r
- errors));\r
- }\r
-\r
- @Override\r
- public boolean isSuccessful() {\r
- return successful;\r
- }\r
-\r
- @Override\r
- public T getResult() {\r
- return result;\r
- }\r
-\r
- @Override\r
- public Collection<RpcError> getErrors() {\r
- return errors;\r
- }\r
-\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public class Rpcs {
+ public static <T> RpcResult<T> getRpcResult(boolean successful, T result,
+ Collection<RpcError> errors) {
+ RpcResult<T> ret = new RpcResultTO<T>(successful, result, errors);
+ return ret;
+ }
+
+ private static class RpcResultTO<T> implements RpcResult<T> {
+
+ private final Collection<RpcError> errors;
+ private final T result;
+ private final boolean successful;
+
+ public RpcResultTO(boolean successful, T result,
+ Collection<RpcError> errors) {
+ this.successful = successful;
+ this.result = result;
+ this.errors = Collections.unmodifiableList(new ArrayList<RpcError>(
+ errors));
+ }
+
+ @Override
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ @Override
+ public T getResult() {
+ return result;
+ }
+
+ @Override
+ public Collection<RpcError> getErrors() {
+ return errors;
+ }
+
+ }
+}
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
- <modelVersion>4.0.0</modelVersion>\r
- <parent>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </parent>\r
- <artifactId>sal-common</artifactId>\r
-\r
- <dependencies>\r
+<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.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-common</artifactId>
+
+ <dependencies>
</dependencies>
+ <packaging>bundle</packaging>
</project>
\ No newline at end of file
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
- <modelVersion>4.0.0</modelVersion>\r
- <parent>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </parent>\r
- <artifactId>sal-core-api</artifactId>\r
-\r
- <dependencies>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-common</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
-\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>yang-data-api</artifactId>\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>yang-model-api</artifactId>\r
- </dependency>\r
+<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.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-core-api</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
-/*\r
- * Copyright (c) 2013 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.api;\r
-\r
-import java.util.concurrent.Future;\r
-\r
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService;\r
-import org.opendaylight.controller.sal.core.api.data.DataProviderService;\r
-import org.opendaylight.controller.sal.core.api.notify.NotificationProviderService;\r
-import org.opendaylight.controller.sal.core.api.notify.NotificationService;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-\r
-\r
-/**\r
- * Core component of the SAL layer responsible for wiring the SAL consumers.\r
- * \r
- * The responsibility of the broker is to maintain registration of SAL\r
- * functionality {@link Consumer}s and {@link Provider}s, store provider and\r
- * consumer specific context and functionality registration via\r
- * {@link ConsumerSession} and provide access to infrastructure services, which\r
- * removes direct dependencies between providers and consumers.\r
- * \r
- * \r
- * <h3>Infrastructure services</h3> Some examples of infrastructure services:\r
- * \r
- * <ul>\r
- * <li>RPC Invocation - see {@link ConsumerSession#rpc(QName, CompositeNode)},\r
- * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)} and\r
- * {@link RpcImplementation}\r
- * <li>Notification Service - see {@link NotificationService} and\r
- * {@link NotificationProviderService}\r
- * <li>Functionality and Data model\r
- * <li>Data Store access and modification - see {@link DataBrokerService} and\r
- * {@link DataProviderService}\r
- * </ul>\r
- * \r
- * The services are exposed via session.\r
- * \r
- * <h3>Session-based access</h3>\r
- * \r
- * The providers and consumers needs to register in order to use the\r
- * binding-independent SAL layer and to expose functionality via SAL layer.\r
- * \r
- * For more information about session-based access see {@link ConsumerSession}\r
- * and {@link ProviderSession}\r
- * \r
- * \r
- * \r
- */\r
-public interface Broker {\r
-\r
- /**\r
- * Registers the {@link Consumer}, which will use the SAL layer.\r
- * \r
- * <p>\r
- * During the registration, the broker obtains the initial functionality\r
- * from consumer, using the {@link Consumer#getConsumerFunctionality()}, and\r
- * register that functionality into system and concrete infrastructure\r
- * services.\r
- * \r
- * <p>\r
- * Note that consumer could register additional functionality at later point\r
- * by using service and functionality specific APIs.\r
- * \r
- * <p>\r
- * The consumer is required to use returned session for all communication\r
- * with broker or one of the broker services. The session is announced to\r
- * the consumer by invoking\r
- * {@link Consumer#onSessionInitiated(ConsumerSession)}.\r
- * \r
- * @param cons\r
- * Consumer to be registered.\r
- * @return a session specific to consumer registration\r
- * @throws IllegalArgumentException\r
- * If the consumer is <code>null</code>.\r
- * @throws IllegalStateException\r
- * If the consumer is already registered.\r
- */\r
- ConsumerSession registerConsumer(Consumer cons);\r
-\r
- /**\r
- * Registers the {@link Provider}, which will use the SAL layer.\r
- * \r
- * <p>\r
- * During the registration, the broker obtains the initial functionality\r
- * from consumer, using the {@link Provider#getProviderFunctionality()}, and\r
- * register that functionality into system and concrete infrastructure\r
- * services.\r
- * \r
- * <p>\r
- * Note that consumer could register additional functionality at later point\r
- * by using service and functionality specific APIs (e.g.\r
- * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}\r
- * \r
- * <p>\r
- * The consumer is <b>required to use</b> returned session for all\r
- * communication with broker or one of the broker services. The session is\r
- * announced to the consumer by invoking\r
- * {@link Provider#onSessionInitiated(ProviderSession)}.\r
- * \r
- * \r
- * @param prov\r
- * Provider to be registered.\r
- * @return a session unique to the provider registration.\r
- * @throws IllegalArgumentException\r
- * If the provider is <code>null</code>.\r
- * @throws IllegalStateException\r
- * If the consumer is already registered.\r
- */\r
- ProviderSession registerProvider(Provider prov);\r
-\r
- /**\r
- * {@link Consumer} specific access to the SAL functionality.\r
- * \r
- * <p>\r
- * ConsumerSession is {@link Consumer}-specific access to the SAL\r
- * functionality and infrastructure services.\r
- * \r
- * <p>\r
- * The session serves to store SAL context (e.g. registration of\r
- * functionality) for the consumer and provides access to the SAL\r
- * infrastructure services and other functionality provided by\r
- * {@link Provider}s.\r
- * \r
-\r
- * \r
- */\r
- public interface ConsumerSession {\r
-\r
- /**\r
- * Sends an RPC to other components registered to the broker.\r
- * \r
- * @see RpcImplementation\r
- * @param rpc\r
- * Name of RPC\r
- * @param input\r
- * Input data to the RPC\r
- * @return Result of the RPC call\r
- */\r
- Future<RpcResult<CompositeNode>> rpc(QName rpc, CompositeNode input);\r
-\r
- boolean isClosed();\r
-\r
- /**\r
- * Returns a session specific instance (implementation) of requested\r
- * service\r
- * \r
- * @param service\r
- * Broker service\r
- * @return Session specific implementation of service\r
- */\r
- <T extends BrokerService> T getService(Class<T> service);\r
-\r
- /**\r
- * Closes a session between consumer and broker.\r
- * \r
- * <p>\r
- * The close operation unregisters a consumer and remove all registered\r
- * functionality of the consumer from the system.\r
- * \r
- */\r
- void close();\r
- }\r
-\r
- /**\r
- * {@link Provider} specific access to the SAL functionality.\r
- * \r
- * <p>\r
- * ProviderSession is {@link Provider}-specific access to the SAL\r
- * functionality and infrastructure services, which also allows for exposing\r
- * the provider's functionality to the other {@link Consumer}s.\r
- * \r
- * <p>\r
- * The session serves to store SAL context (e.g. registration of\r
- * functionality) for the providers and exposes access to the SAL\r
- * infrastructure services, dynamic functionality registration and any other\r
- * functionality provided by other {@link Provider}s.\r
- * \r
- */\r
- public interface ProviderSession extends ConsumerSession {\r
- /**\r
- * Registers an implementation of the rpc.\r
- * \r
- * <p>\r
- * The registered rpc functionality will be available to all other\r
- * consumers and providers registered to the broker, which are aware of\r
- * the {@link QName} assigned to the rpc.\r
- * \r
- * <p>\r
- * There is no assumption that rpc type is in the set returned by\r
- * invoking {@link RpcImplementation#getSupportedRpcs()}. This allows\r
- * for dynamic rpc implementations.\r
- * \r
- * @param rpcType\r
- * Name of Rpc\r
- * @param implementation\r
- * Provider's Implementation of the RPC functionality\r
- * @throws IllegalArgumentException\r
- * If the name of RPC is invalid\r
- */\r
- void addRpcImplementation(QName rpcType,\r
- RpcImplementation implementation)\r
- throws IllegalArgumentException;\r
-\r
- /**\r
- * Unregisters an Rpc implementation\r
- * \r
- * @param rpcType\r
- * Name of Rpc\r
- * @param implementation\r
- * Registered Implementation of the Rpc functionality\r
- * @throws IllegalArgumentException\r
- */\r
- void removeRpcImplementation(QName rpcType,\r
- RpcImplementation implementation)\r
- throws IllegalArgumentException;\r
-\r
- /**\r
- * Closes a session between provider and SAL.\r
- * \r
- * <p>\r
- * The close operation unregisters a provider and remove all registered\r
- * functionality of the provider from the system.\r
- */\r
- @Override\r
- public void close();\r
-\r
- @Override\r
- boolean isClosed();\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.core.api.data.DataProviderService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationProviderService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+
+/**
+ * Core component 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 ConsumerSession} and provide access to infrastructure services, which
+ * removes direct dependencies between providers and consumers.
+ *
+ *
+ * <h3>Infrastructure services</h3> Some examples of infrastructure services:
+ *
+ * <ul>
+ * <li>RPC Invocation - see {@link ConsumerSession#rpc(QName, CompositeNode)},
+ * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)} and
+ * {@link RpcImplementation}
+ * <li>Notification Service - see {@link NotificationService} and
+ * {@link NotificationProviderService}
+ * <li>Functionality and Data model
+ * <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 ConsumerSession}
+ * and {@link ProviderSession}
+ *
+ *
+ *
+ */
+public interface Broker {
+
+ /**
+ * Registers the {@link Consumer}, which will use the SAL layer.
+ *
+ * <p>
+ * During the registration, the broker obtains the initial functionality
+ * from consumer, using the {@link Consumer#getConsumerFunctionality()}, and
+ * register that functionality into system and concrete infrastructure
+ * services.
+ *
+ * <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(ConsumerSession)}.
+ *
+ * @param cons
+ * Consumer to be registered.
+ * @return a session specific to consumer registration
+ * @throws IllegalArgumentException
+ * If the consumer is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ConsumerSession registerConsumer(Consumer cons);
+
+ /**
+ * Registers the {@link Provider}, which will use the SAL layer.
+ *
+ * <p>
+ * During the registration, the broker obtains the initial functionality
+ * from consumer, using the {@link Provider#getProviderFunctionality()}, and
+ * register that functionality into system and concrete infrastructure
+ * services.
+ *
+ * <p>
+ * Note that consumer could register additional functionality at later point
+ * by using service and functionality specific APIs (e.g.
+ * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}
+ *
+ * <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 Provider#onSessionInitiated(ProviderSession)}.
+ *
+ *
+ * @param prov
+ * Provider to be registered.
+ * @return a session unique to the provider registration.
+ * @throws IllegalArgumentException
+ * If the provider is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ProviderSession registerProvider(Provider prov);
+
+ /**
+ * {@link Consumer} specific access to the SAL functionality.
+ *
+ * <p>
+ * ConsumerSession is {@link Consumer}-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 ConsumerSession {
+
+ /**
+ * Sends an RPC to other components registered to the broker.
+ *
+ * @see RpcImplementation
+ * @param rpc
+ * Name of RPC
+ * @param input
+ * Input data to the RPC
+ * @return Result of the RPC call
+ */
+ Future<RpcResult<CompositeNode>> rpc(QName rpc, CompositeNode input);
+
+ boolean isClosed();
+
+ /**
+ * Returns a session specific instance (implementation) of requested
+ * service
+ *
+ * @param service
+ * Broker service
+ * @return Session specific implementation of service
+ */
+ <T extends BrokerService> T getService(Class<T> service);
+
+ /**
+ * Closes a session between consumer and broker.
+ *
+ * <p>
+ * The close operation unregisters a consumer and remove all registered
+ * functionality of the consumer from the system.
+ *
+ */
+ void close();
+ }
+
+ /**
+ * {@link Provider} specific access to the SAL functionality.
+ *
+ * <p>
+ * ProviderSession is {@link Provider}-specific access to the SAL
+ * functionality and infrastructure services, which also allows for exposing
+ * the provider's functionality to the other {@link Consumer}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 Provider}s.
+ *
+ */
+ public interface ProviderSession extends ConsumerSession {
+ /**
+ * Registers an implementation of the rpc.
+ *
+ * <p>
+ * The registered rpc functionality will be available to all other
+ * consumers and providers registered to the broker, which are aware of
+ * the {@link QName} assigned to the rpc.
+ *
+ * <p>
+ * There is no assumption that rpc type is in the set returned by
+ * invoking {@link RpcImplementation#getSupportedRpcs()}. This allows
+ * for dynamic rpc implementations.
+ *
+ * @param rpcType
+ * Name of Rpc
+ * @param implementation
+ * Provider's Implementation of the RPC functionality
+ * @throws IllegalArgumentException
+ * If the name of RPC is invalid
+ */
+ void addRpcImplementation(QName rpcType,
+ RpcImplementation implementation)
+ throws IllegalArgumentException;
+
+ /**
+ * Unregisters an Rpc implementation
+ *
+ * @param rpcType
+ * Name of Rpc
+ * @param implementation
+ * Registered Implementation of the Rpc functionality
+ * @throws IllegalArgumentException
+ */
+ void removeRpcImplementation(QName rpcType,
+ RpcImplementation implementation)
+ throws IllegalArgumentException;
+
+ /**
+ * Closes a session between provider and SAL.
+ *
+ * <p>
+ * The close operation unregisters a provider and remove all registered
+ * functionality of the provider from the system.
+ */
+ @Override
+ public void close();
+
+ @Override
+ boolean isClosed();
+ }
+}
-/*\r
- * Copyright (c) 2013 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.api;\r
-\r
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
-\r
-/**\r
- * \r
- * Session-specific instance of the broker functionality.\r
- * \r
- * <p>\r
- * BrokerService is marker interface for infrastructure services provided by the\r
- * SAL. These services are session-specific, each {@link Provider} and\r
- * {@link Consumer} usually has own instance of the service with it's own\r
- * context.\r
- * \r
- * <p>\r
- * The consumer's (or provider's) instance of specific service could be obtained\r
- * by invoking {@link ConsumerSession#getService(Class)} method on session\r
- * assigned to the consumer.\r
- * \r
- * <p>\r
- * {@link BrokerService} and {@link Provider} may seem similar, but provider\r
- * provides YANG model-based functionality and {@link BrokerService} exposes the\r
- * necessary supporting functionality to implement specific functionality of\r
- * YANG and to reuse it in the development of {@link Consumer}s and\r
- * {@link Provider}s.\r
- * \r
- * \r
- */\r
-public interface BrokerService {\r
-\r
- void closeSession();\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+
+/**
+ *
+ * Session-specific instance of the broker functionality.
+ *
+ * <p>
+ * BrokerService is marker interface for infrastructure services provided by the
+ * SAL. These services are session-specific, each {@link Provider} and
+ * {@link Consumer} usually has own instance of the service with it's own
+ * context.
+ *
+ * <p>
+ * The consumer's (or provider's) instance of specific service could be obtained
+ * by invoking {@link ConsumerSession#getService(Class)} method on session
+ * assigned to the consumer.
+ *
+ * <p>
+ * {@link BrokerService} and {@link Provider} may seem similar, but provider
+ * provides YANG model-based functionality and {@link BrokerService} exposes the
+ * necessary supporting functionality to implement specific functionality of
+ * YANG and to reuse it in the development of {@link Consumer}s and
+ * {@link Provider}s.
+ *
+ *
+ */
+public interface BrokerService {
+
+ void closeSession();
+}
-/*\r
- * Copyright (c) 2013 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.api;\r
-\r
-import java.util.Collection;\r
-\r
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
-\r
-/**\r
- * \r
- * Defines the component of controller and supplies additional metadata. A\r
- * component of the controller or application supplies a concrete implementation\r
- * of this interface.\r
- * \r
- * A user-implemented component (application) which faciliates the SAL and SAL\r
- * services to access infrastructure services or providers' functionality.\r
- * \r
- * \r
- */\r
-public interface Consumer {\r
-\r
- /**\r
- * Callback signaling initialization of the consumer session to the SAL.\r
- * \r
- * The consumer MUST use the session for all communication with SAL or\r
- * retrieving SAL infrastructure services.\r
- * \r
- * This method is invoked by {@link Broker#registerConsumer(Consumer)}\r
- * \r
- * @param session\r
- * Unique session between consumer and SAL.\r
- */\r
- public void onSessionInitiated(ConsumerSession session);\r
-\r
- /**\r
- * Get a set of implementations of consumer functionality to be registered\r
- * into system during the consumer registration to the SAL.\r
- * \r
- * This method is invoked by {@link Broker#registerConsumer(Consumer)}.\r
- * \r
- * @return Set of consumer functionality.\r
- */\r
- public Collection<ConsumerFunctionality> getConsumerFunctionality();\r
-\r
- /**\r
- * The marker interface for the interfaces describing the consumer\r
- * functionality contracts.\r
- * \r
- * \r
- */\r
- public interface ConsumerFunctionality {\r
-\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * A user-implemented component (application) which faciliates the SAL and SAL
+ * services to access infrastructure services or providers' functionality.
+ *
+ *
+ */
+public interface Consumer {
+
+ /**
+ * Callback signaling initialization of the consumer session to the SAL.
+ *
+ * The consumer MUST use the session for all communication with SAL or
+ * retrieving SAL infrastructure services.
+ *
+ * This method is invoked by {@link Broker#registerConsumer(Consumer)}
+ *
+ * @param session
+ * Unique session between consumer and SAL.
+ */
+ public void onSessionInitiated(ConsumerSession session);
+
+ /**
+ * Get a set of implementations of consumer functionality to be registered
+ * into system during the consumer registration to the SAL.
+ *
+ * This method is invoked by {@link Broker#registerConsumer(Consumer)}.
+ *
+ * @return Set of consumer functionality.
+ */
+ public Collection<ConsumerFunctionality> getConsumerFunctionality();
+
+ /**
+ * The marker interface for the interfaces describing the consumer
+ * functionality contracts.
+ *
+ *
+ */
+ public interface ConsumerFunctionality {
+
+ }
+}
-/*\r
- * Copyright (c) 2013 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.api;\r
-\r
-import java.util.Collection;\r
-\r
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
-\r
-/**\r
- * \r
- * Defines the component of controller and supplies additional metadata. A\r
- * component of the controller or application supplies a concrete implementation\r
- * of this interface.\r
- * \r
- * <p>\r
- * A user-implemented component (application) which faciliates the SAL and SAL\r
- * services to access infrastructure services and to provide functionality to\r
- * {@link Consumer}s and other providers.\r
- * \r
- * \r
- */\r
-public interface Provider {\r
-\r
- /**\r
- * Callback signaling initialization of the provider session to the SAL.\r
- * \r
- * <p>\r
- * The provider <b>MUST use the session</b> for all communication with SAL\r
- * or retrieving SAL infrastructure services.\r
- * \r
- * <p>\r
- * This method is invoked by {@link Broker#registerConsumer(Consumer)}\r
- * \r
- * @param session\r
- * Unique session between provider and SAL.\r
- */\r
- public void onSessionInitiated(ProviderSession session);\r
-\r
- /**\r
- * Gets a set of implementations of provider functionality to be registered\r
- * into system during the provider registration to the SAL.\r
- * \r
- * <p>\r
- * This method is invoked by {@link Broker#registerProvider(Provider)} to\r
- * learn the initial provided functionality\r
- * \r
- * @return Set of provider's functionality.\r
- */\r
- public Collection<ProviderFunctionality> getProviderFunctionality();\r
-\r
- /**\r
- * Functionality provided by the {@link Provider}\r
- * \r
- * <p>\r
- * Marker interface used to mark the interfaces describing specific\r
- * functionality which could be exposed by providers to other components.\r
- * \r
-\r
- * \r
- */\r
- public interface ProviderFunctionality {\r
-\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * <p>
+ * A user-implemented component (application) which faciliates the SAL and SAL
+ * services to access infrastructure services and to provide functionality to
+ * {@link Consumer}s and other providers.
+ *
+ *
+ */
+public interface Provider {
+
+ /**
+ * Callback signaling initialization of the provider session to the SAL.
+ *
+ * <p>
+ * The provider <b>MUST use the session</b> for all communication with SAL
+ * or retrieving SAL infrastructure services.
+ *
+ * <p>
+ * This method is invoked by {@link Broker#registerConsumer(Consumer)}
+ *
+ * @param session
+ * Unique session between provider and SAL.
+ */
+ public void onSessionInitiated(ProviderSession session);
+
+ /**
+ * Gets a set of implementations of provider functionality to be registered
+ * into system during the provider registration to the SAL.
+ *
+ * <p>
+ * This method is invoked by {@link Broker#registerProvider(Provider)} to
+ * learn the initial provided functionality
+ *
+ * @return Set of provider's functionality.
+ */
+ public Collection<ProviderFunctionality> getProviderFunctionality();
+
+ /**
+ * Functionality provided by the {@link Provider}
+ *
+ * <p>
+ * Marker interface used to mark the interfaces describing specific
+ * functionality which could be exposed by providers to other components.
+ *
+
+ *
+ */
+ public interface ProviderFunctionality {
+
+ }
+}
-/*\r
- * Copyright (c) 2013 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.api;\r
-\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-\r
-/**\r
- * {@link Provider}'s implementation of rpc.\r
- * \r
- * In order to expose the rpc to other components, the provider MUST register\r
- * concrete implementation of this interface\r
- * \r
- * The registration could be done by :\r
- * <ul>\r
- * <li>returning an instance of implementation in the return value of\r
- * {@link Provider#getProviderFunctionality()}\r
- * <li>passing an instance of implementation and {@link QName} of rpc as\r
- * arguments to the\r
- * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}\r
- * </ul>\r
- * \r
- * The simplified process of the invocation of rpc is following:\r
- * \r
- * <ol>\r
- * <li> {@link Consumer} invokes\r
- * {@link ConsumerSession#rpc(QName, CompositeNode)}\r
- * <li> {@link Broker} finds registered {@link RpcImplementation}s\r
- * <li> {@link Broker} invokes\r
- * {@link RpcImplementation#invokeRpc(QName, CompositeNode)}\r
- * <li> {@link RpcImplementation} processes the data and returns a\r
- * {@link RpcResult}\r
- * <li> {@link Broker} returns the {@link RpcResult} to {@link Consumer}\r
- * </ol>\r
- * \r
- * \r
- */\r
-public interface RpcImplementation extends Provider.ProviderFunctionality {\r
-\r
- /**\r
- * A set of rpc types supported by implementation.\r
- * \r
- * The set of rpc {@link QName}s which are supported by this implementation.\r
- * This set is used, when {@link Provider} is registered to the SAL, to\r
- * register and expose the implementation of the returned rpcs.\r
- * \r
- * @return Set of QNames identifying supported RPCs\r
- */\r
- Set<QName> getSupportedRpcs();\r
-\r
- /**\r
- * Invokes a implementation of specified rpc.\r
- * \r
- * \r
- * @param rpc\r
- * Rpc to be invoked\r
- * @param input\r
- * Input data for rpc.\r
- * \r
- * @throws IllegalArgumentException\r
- * <ul>\r
- * <li>If rpc is null.\r
- * <li>If input is not <code>null</code> and\r
- * <code>false == rpc.equals(input.getNodeType)</code>\r
- * </ul>\r
- * @return RpcResult containing the output of rpc if was executed\r
- * successfully, the list of errors otherwise.\r
- */\r
- RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input);\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+/**
+ * {@link Provider}'s implementation of rpc.
+ *
+ * In order to expose the rpc to other components, the provider MUST register
+ * concrete implementation of this interface
+ *
+ * The registration could be done by :
+ * <ul>
+ * <li>returning an instance of implementation in the return value of
+ * {@link Provider#getProviderFunctionality()}
+ * <li>passing an instance of implementation and {@link QName} of rpc as
+ * arguments to the
+ * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}
+ * </ul>
+ *
+ * The simplified process of the invocation of rpc is following:
+ *
+ * <ol>
+ * <li> {@link Consumer} invokes
+ * {@link ConsumerSession#rpc(QName, CompositeNode)}
+ * <li> {@link Broker} finds registered {@link RpcImplementation}s
+ * <li> {@link Broker} invokes
+ * {@link RpcImplementation#invokeRpc(QName, CompositeNode)}
+ * <li> {@link RpcImplementation} processes the data and returns a
+ * {@link RpcResult}
+ * <li> {@link Broker} returns the {@link RpcResult} to {@link Consumer}
+ * </ol>
+ *
+ *
+ */
+public interface RpcImplementation extends Provider.ProviderFunctionality {
+
+ /**
+ * A set of rpc types supported by implementation.
+ *
+ * The set of rpc {@link QName}s which are supported by this implementation.
+ * This set is used, when {@link Provider} is registered to the SAL, to
+ * register and expose the implementation of the returned rpcs.
+ *
+ * @return Set of QNames identifying supported RPCs
+ */
+ Set<QName> getSupportedRpcs();
+
+ /**
+ * Invokes a implementation of specified rpc.
+ *
+ *
+ * @param rpc
+ * Rpc to be invoked
+ * @param input
+ * Input data for rpc.
+ *
+ * @throws IllegalArgumentException
+ * <ul>
+ * <li>If rpc is null.
+ * <li>If input is not <code>null</code> and
+ * <code>false == rpc.equals(input.getNodeType)</code>
+ * </ul>
+ * @return RpcResult containing the output of rpc if was executed
+ * successfully, the list of errors otherwise.
+ */
+ RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input);
+
+}
-/*\r
- * Copyright (c) 2013 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.api.data;\r
-\r
-import java.util.Set;\r
-import java.util.concurrent.Future;\r
-\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-import org.opendaylight.controller.sal.core.api.Consumer;\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-import org.opendaylight.controller.yang.data.api.CompositeNodeModification;\r
-import org.opendaylight.controller.yang.data.api.Node;\r
-\r
-\r
-/**\r
- * DataBrokerService provides unified access to the data stores available in the\r
- * system.\r
- * \r
- * \r
- * @see DataProviderService\r
- * \r
- */\r
-public interface DataBrokerService extends BrokerService {\r
-\r
- \r
- Set<DataStoreIdentifier> getDataStores();\r
- \r
- /**\r
- * Returns a data from specified Data Store.\r
- * \r
- * Returns all the data visible to the consumer from specified Data Store.\r
- * \r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @return data visible to the consumer\r
- */\r
- CompositeNode getData(DataStoreIdentifier store);\r
-\r
- /**\r
- * Returns a filtered subset of data from specified Data Store.\r
- * \r
- * <p>\r
- * The filter is modeled as an hierarchy of {@link Node} starting with\r
- * {@link CompositeNode} representing data root. The semantics of the filter\r
- * tree is the same as filter semantics defined in the NETCONF protocol for\r
- * rpc operations <code>get</code> and <code>get-config</code> in Section 6\r
- * of RFC6241.\r
- * \r
- * \r
- * @see http://tools.ietf.org/html/rfc6241#section-6\r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @param filter\r
- * Data tree filter similar to the NETCONF filter\r
- * @return\r
- */\r
- CompositeNode getData(DataStoreIdentifier store, CompositeNode filter);\r
-\r
- /**\r
- * Returns a candidate data which are not yet commited.\r
- * \r
- * \r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @return\r
- */\r
- CompositeNode getCandidateData(DataStoreIdentifier store);\r
-\r
- /**\r
- * Returns a filtered subset of candidate data from specified Data Store.\r
- * \r
- * <p>\r
- * The filter is modeled as an hierarchy of {@link Node} starting with\r
- * {@link CompositeNode} representing data root. The semantics of the filter\r
- * tree is the same as filter semantics defined in the NETCONF protocol for\r
- * rpc operations <code>get</code> and <code>get-config</code> in Section 6\r
- * of RFC6241.\r
- * \r
- * \r
- * @see http://tools.ietf.org/html/rfc6241#section-6\r
- * @param store\r
- * Identifier of the store, from which will be data retrieved\r
- * @param filter\r
- * A CompositeNode filter\r
- * @return\r
- */\r
- CompositeNode getCandidateData(DataStoreIdentifier store,\r
- CompositeNode filter);\r
-\r
- /**\r
- * \r
- * @param store\r
- * Identifier of the store, in which will be the candidate data\r
- * modified\r
- * @param changeSet\r
- * Modification of data tree.\r
- * @return Result object containing the modified data tree if the operation\r
- * was successful, otherwise list of the encountered errors.\r
- */\r
- RpcResult<CompositeNode> editCandidateData(DataStoreIdentifier store,\r
- CompositeNodeModification changeSet);\r
-\r
- /**\r
- * Initiates a two-phase commit of candidate data.\r
- * \r
- * <p>\r
- * The {@link Consumer} could initiate a commit of candidate data\r
- * \r
- * <p>\r
- * The successful commit changes the state of the system and may affect\r
- * several components.\r
- * \r
- * <p>\r
- * The effects of successful commit of data are described in the\r
- * specifications and YANG models describing the {@link Provider} components\r
- * of controller. It is assumed that {@link Consumer} has an understanding\r
- * of this changes.\r
- * \r
- * \r
- * @see DataCommitHandler for further information how two-phase commit is\r
- * processed.\r
- * @param store\r
- * Identifier of the store, where commit should occur.\r
- * @return Result of the commit, containing success information or list of\r
- * encountered errors, if commit was not successful.\r
- */\r
- Future<RpcResult<Void>> commit(DataStoreIdentifier store);\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.data;
+
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+
+/**
+ * DataBrokerService provides unified access to the data stores available in the
+ * system.
+ *
+ *
+ * @see DataProviderService
+ *
+ */
+public interface DataBrokerService extends BrokerService {
+
+
+ Set<DataStoreIdentifier> getDataStores();
+
+ /**
+ * Returns a data from specified Data Store.
+ *
+ * Returns all the data visible to the consumer from specified Data Store.
+ *
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @return data visible to the consumer
+ */
+ CompositeNode getData(DataStoreIdentifier store);
+
+ /**
+ * Returns a filtered subset of data from specified Data Store.
+ *
+ * <p>
+ * The filter is modeled as an hierarchy of {@link Node} starting with
+ * {@link CompositeNode} representing data root. The semantics of the filter
+ * tree is the same as filter semantics defined in the NETCONF protocol for
+ * rpc operations <code>get</code> and <code>get-config</code> in Section 6
+ * of RFC6241.
+ *
+ *
+ * @see http://tools.ietf.org/html/rfc6241#section-6
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @param filter
+ * Data tree filter similar to the NETCONF filter
+ * @return
+ */
+ CompositeNode getData(DataStoreIdentifier store, CompositeNode filter);
+
+ /**
+ * Returns a candidate data which are not yet commited.
+ *
+ *
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @return
+ */
+ CompositeNode getCandidateData(DataStoreIdentifier store);
+
+ /**
+ * Returns a filtered subset of candidate data from specified Data Store.
+ *
+ * <p>
+ * The filter is modeled as an hierarchy of {@link Node} starting with
+ * {@link CompositeNode} representing data root. The semantics of the filter
+ * tree is the same as filter semantics defined in the NETCONF protocol for
+ * rpc operations <code>get</code> and <code>get-config</code> in Section 6
+ * of RFC6241.
+ *
+ *
+ * @see http://tools.ietf.org/html/rfc6241#section-6
+ * @param store
+ * Identifier of the store, from which will be data retrieved
+ * @param filter
+ * A CompositeNode filter
+ * @return
+ */
+ CompositeNode getCandidateData(DataStoreIdentifier store,
+ CompositeNode filter);
+
+ /**
+ *
+ * @param store
+ * Identifier of the store, in which will be the candidate data
+ * modified
+ * @param changeSet
+ * Modification of data tree.
+ * @return Result object containing the modified data tree if the operation
+ * was successful, otherwise list of the encountered errors.
+ */
+ RpcResult<CompositeNode> editCandidateData(DataStoreIdentifier store,
+ MutableCompositeNode changeSet);
+
+ /**
+ * Initiates a two-phase commit of candidate data.
+ *
+ * <p>
+ * The {@link Consumer} could initiate a commit of candidate data
+ *
+ * <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 {@link Provider} components
+ * of controller. It is assumed that {@link 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.
+ */
+ Future<RpcResult<Void>> commit(DataStoreIdentifier store);
+}
-/*\r
- * Copyright (c) 2013 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.api.data;\r
-\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-\r
-\r
-/**\r
- * Two phase commit handler (cohort) of the two-phase commit protocol of data.\r
- * \r
- * <p>\r
- * The provider should expose the implementation of DataCommitHandler if it's\r
- * functionality depends on any subset of data stored in data repositories, in\r
- * order to participate in {@link DataBrokerService#commit(DataStoreIdentifier)\r
- * operation.\r
- * \r
- * <p>\r
- * Operations of two-phase commit handlers should not change data in data store,\r
- * this is responsibility of the coordinator (broker or some component of the\r
- * broker).\r
- * \r
- * The commit handlers are responsible for changing the internal state of the\r
- * provider to reflect the commited changes in data.\r
- * \r
- * <h3>Two-phase commit</h3>\r
- * \r
- * <h4>Commit Request Phase</h4>\r
- * \r
- * <ol>\r
- * <li> <code>Consumer</code> edits data by invocation of\r
- * <code>DataBrokerService.editCandidateData(DataStoreIdentifier, CompositeNodeModification)</code>\r
- * <li> <code>Consumer</code> starts a commit by invoking\r
- * <code>DataBrokerService.commit(DataStoreIdentifier)</code>\r
- * <li> <code>Broker</code> retrieves a list of all registered\r
- * <code>DataCommitHandlers</code>\r
- * <li>For each <code>DataCommitHandler</code>\r
- * <ol>\r
- * <li><code>Broker</code> invokes a\r
- * <code>DataCommitHandler.requestCommit(DataStoreIdentifier)</code> operation.\r
- * <li><code>DataCommitHandler</code> returns a <code>RpcResult</code> with\r
- * <code>CommitTransaction</code>\r
- * <li>If the result was successful, broker adds <code>CommitTransaction</code>\r
- * to the list of opened transactions. If not, brokers stops a commit request\r
- * phase and starts a rollback phase.\r
- * </ol>\r
- * <li><code>Broker</code> starts a commit finish phase\r
- * </ol>\r
- * \r
- * <h4>Commit Finish Phase</h4>\r
- * \r
- * <ol>\r
- * <li>For each <code>CommitTransaction</code> from Commit Request phase\r
- * <ol>\r
- * <li><code>Broker</code> broker invokes a\r
- * <code>CommitTransaction.finish()</code>\r
- * <li>The provider finishes a commit (applies the change) and returns an\r
- * <code>RpcResult</code>.\r
- * </ol>\r
- * <li>\r
- * <ul>\r
- * <li>If all returned results means successful, the brokers end two-phase\r
- * commit by returning a success commit result to the Consumer.\r
- * <li>If error occured, the broker starts a commit rollback phase.\r
- * </ul>\r
- * </ol>\r
- * \r
- * <h4>Commit Rollback Phase</h4>\r
- * <li>For each <code>CommitTransaction</code> from Commit Request phase\r
- * <ol>\r
- * <li><code>Broker</code>\r
- * \r
- * broker invokes a {@link CommitTransaction#finish()}\r
- * <li>The provider rollbacks a commit and returns an {@link RpcResult} of\r
- * rollback. </ol>\r
- * <li>Broker returns a error result to the consumer.\r
- * \r
- * \r
- * <h3>Registration of functionality</h3>\r
- * The registration could be done by :\r
- * <ul>\r
- * <li>returning an instance of implementation in the return value of\r
- * {@link Provider#getProviderFunctionality()}\r
- * <li>passing an instance of implementation and {@link DataStoreIdentifier} of\r
- * rpc as arguments to the\r
- * {@link DataProviderService#addCommitHandler(DataStoreIdentifier, DataCommitHandler)}\r
- * </ul>\r
- * \r
- * \r
- */\r
-public interface DataCommitHandler extends Provider.ProviderFunctionality {\r
-\r
- /**\r
- * A set of Data Stores supported by implementation.\r
- * \r
- * The set of {@link DataStoreIdentifier}s which identifies target data\r
- * stores which are supported by this commit handler. This set is used, when\r
- * {@link Provider} is registered to the SAL, to register and expose the\r
- * commit handler functionality to affected data stores.\r
- * \r
- * @return Set of Data Store identifiers\r
- */\r
- Set<DataStoreIdentifier> getSupportedDataStores();\r
-\r
- /**\r
- * The provider (commit handler) starts a commit transaction.\r
- * \r
- * <p>\r
- * The commit handler (provider) prepares an commit scenario, rollback\r
- * scenario and validates data.\r
- * \r
- * <p>\r
- * If the provider is aware that at this point the commit would not be\r
- * successful, the transaction is not created, but list of errors which\r
- * prevented the start of transaction are returned.\r
- * \r
- * @param store\r
- * @return Transaction object representing this commit, errors otherwise.\r
- */\r
- RpcResult<CommitTransaction> requestCommit(DataStoreIdentifier store);\r
-\r
- public interface CommitTransaction {\r
- /**\r
- * \r
- * @return Data store affected by the transaction\r
- */\r
- DataStoreIdentifier getDataStore();\r
-\r
- /**\r
- * Returns the handler associated with this transaction.\r
- * \r
- * @return Handler\r
- */\r
- DataCommitHandler getHandler();\r
-\r
- /**\r
- * \r
- * Finishes a commit.\r
- * \r
- * The provider (commit handler) should apply all changes to its state\r
- * which are a result of data change-\r
- * \r
- * @return\r
- */\r
- RpcResult<Void> finish() throws IllegalStateException;\r
-\r
- /**\r
- * Rollbacks a commit.\r
- * \r
- * @return\r
- * @throws IllegalStateException\r
- * If the method is invoked after {@link #finish()}\r
- */\r
- RpcResult<Void> rollback() throws IllegalStateException;\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.data;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+
+/**
+ * Two phase commit handler (cohort) of the two-phase commit protocol of data.
+ *
+ * <p>
+ * The provider should expose the implementation of DataCommitHandler if it's
+ * functionality depends on any subset of data stored in data repositories, in
+ * order to participate in {@link DataBrokerService#commit(DataStoreIdentifier)
+ * operation.
+ *
+ * <p>
+ * Operations of two-phase commit handlers should not change data in data store,
+ * this is responsibility of the coordinator (broker or some component of the
+ * broker).
+ *
+ * The commit handlers are responsible for changing the internal state of the
+ * provider to reflect the commited changes in data.
+ *
+ * <h3>Two-phase commit</h3>
+ *
+ * <h4>Commit Request Phase</h4>
+ *
+ * <ol>
+ * <li> <code>Consumer</code> edits data by invocation of
+ * <code>DataBrokerService.editCandidateData(DataStoreIdentifier, CompositeNodeModification)</code>
+ * <li> <code>Consumer</code> starts a commit by invoking
+ * <code>DataBrokerService.commit(DataStoreIdentifier)</code>
+ * <li> <code>Broker</code> retrieves a list of all registered
+ * <code>DataCommitHandlers</code>
+ * <li>For each <code>DataCommitHandler</code>
+ * <ol>
+ * <li><code>Broker</code> invokes a
+ * <code>DataCommitHandler.requestCommit(DataStoreIdentifier)</code> operation.
+ * <li><code>DataCommitHandler</code> returns a <code>RpcResult</code> with
+ * <code>CommitTransaction</code>
+ * <li>If the result was successful, broker adds <code>CommitTransaction</code>
+ * to the list of opened transactions. If not, brokers stops a commit request
+ * phase and starts a rollback phase.
+ * </ol>
+ * <li><code>Broker</code> starts a commit finish phase
+ * </ol>
+ *
+ * <h4>Commit Finish Phase</h4>
+ *
+ * <ol>
+ * <li>For each <code>CommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code> broker invokes a
+ * <code>CommitTransaction.finish()</code>
+ * <li>The provider finishes a commit (applies the change) and returns an
+ * <code>RpcResult</code>.
+ * </ol>
+ * <li>
+ * <ul>
+ * <li>If all returned results means successful, the brokers end two-phase
+ * commit by returning a success commit result to the Consumer.
+ * <li>If error occured, the broker starts a commit rollback phase.
+ * </ul>
+ * </ol>
+ *
+ * <h4>Commit Rollback Phase</h4>
+ * <li>For each <code>CommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code>
+ *
+ * broker invokes a {@link CommitTransaction#finish()}
+ * <li>The provider rollbacks a commit and returns an {@link RpcResult} of
+ * rollback. </ol>
+ * <li>Broker returns a error result to the consumer.
+ *
+ *
+ * <h3>Registration of functionality</h3>
+ * The registration could be done by :
+ * <ul>
+ * <li>returning an instance of implementation in the return value of
+ * {@link Provider#getProviderFunctionality()}
+ * <li>passing an instance of implementation and {@link DataStoreIdentifier} of
+ * rpc as arguments to the
+ * {@link DataProviderService#addCommitHandler(DataStoreIdentifier, DataCommitHandler)}
+ * </ul>
+ *
+ *
+ */
+public interface DataCommitHandler extends Provider.ProviderFunctionality {
+
+ /**
+ * A set of Data Stores supported by implementation.
+ *
+ * The set of {@link DataStoreIdentifier}s which identifies target data
+ * stores which are supported by this commit handler. This set is used, when
+ * {@link Provider} is registered to the SAL, to register and expose the
+ * commit handler functionality to affected data stores.
+ *
+ * @return Set of Data Store identifiers
+ */
+ Set<DataStoreIdentifier> getSupportedDataStores();
+
+ /**
+ * The provider (commit handler) starts a commit transaction.
+ *
+ * <p>
+ * The commit handler (provider) prepares an commit scenario, rollback
+ * scenario and validates data.
+ *
+ * <p>
+ * If the provider is aware that at this point the commit would not be
+ * successful, the transaction is not created, but list of errors which
+ * prevented the start of transaction are returned.
+ *
+ * @param store
+ * @return Transaction object representing this commit, errors otherwise.
+ */
+ RpcResult<CommitTransaction> requestCommit(DataStoreIdentifier store);
+
+ public interface CommitTransaction {
+ /**
+ *
+ * @return Data store affected by the transaction
+ */
+ DataStoreIdentifier getDataStore();
+
+ /**
+ * Returns the handler associated with this transaction.
+ *
+ * @return Handler
+ */
+ DataCommitHandler getHandler();
+
+ /**
+ *
+ * Finishes a commit.
+ *
+ * The provider (commit handler) should apply all changes to its state
+ * which are a result of data change-
+ *
+ * @return
+ */
+ RpcResult<Void> finish() throws IllegalStateException;
+
+ /**
+ * Rollbacks a commit.
+ *
+ * @return
+ * @throws IllegalStateException
+ * If the method is invoked after {@link #finish()}
+ */
+ RpcResult<Void> rollback() throws IllegalStateException;
+ }
+}
-/*\r
- * Copyright (c) 2013 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.api.data;\r
-\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-\r
-public interface DataProviderService extends DataBrokerService {\r
-\r
- /**\r
- * Adds {@link DataValidator} for specified Data Store\r
- * \r
- * @param store\r
- * Data Store\r
- * @param validator\r
- * Validator\r
- */\r
- public void addValidator(DataStoreIdentifier store, DataValidator validator);\r
-\r
- /**\r
- * Removes {@link DataValidator} from specified Data Store\r
- * \r
- * @param store\r
- * @param validator\r
- * Validator\r
- */\r
- public void removeValidator(DataStoreIdentifier store,\r
- DataValidator validator);\r
-\r
- /**\r
- * Adds {@link DataCommitHandler} for specified data store\r
- * \r
- * @param store\r
- * @param provider\r
- */\r
- void addCommitHandler(DataStoreIdentifier store, DataCommitHandler provider);\r
-\r
- /**\r
- * Removes {@link DataCommitHandler} from specified data store\r
- * \r
- * @param store\r
- * @param provider\r
- */\r
- void removeCommitHandler(DataStoreIdentifier store,\r
- DataCommitHandler provider);\r
-\r
- /**\r
- * Adds {@link DataRefresher} for specified data store\r
- * \r
- * @param store\r
- * @param refresher\r
- */\r
- void addRefresher(DataStoreIdentifier store, DataRefresher refresher);\r
-\r
- /**\r
- * Removes {@link DataRefresher} from specified data store\r
- * \r
- * @param store\r
- * @param refresher\r
- */\r
- void removeRefresher(DataStoreIdentifier store, DataRefresher refresher);\r
-\r
- public interface DataRefresher extends Provider.ProviderFunctionality {\r
-\r
- /**\r
- * Fired when some component explicitly requested the data refresh.\r
- * \r
- * The provider which exposed the {@link DataRefresher} should republish\r
- * its provided data by editing the data in all affected data stores.\r
- */\r
- void refreshData();\r
- }\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.data;
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.core.api.Provider;
+
+public interface DataProviderService extends DataBrokerService {
+
+ /**
+ * Adds {@link DataValidator} for specified Data Store
+ *
+ * @param store
+ * Data Store
+ * @param validator
+ * Validator
+ */
+ public void addValidator(DataStoreIdentifier store, DataValidator validator);
+
+ /**
+ * Removes {@link DataValidator} from specified Data Store
+ *
+ * @param store
+ * @param validator
+ * Validator
+ */
+ public void removeValidator(DataStoreIdentifier store,
+ DataValidator validator);
+
+ /**
+ * Adds {@link DataCommitHandler} for specified data store
+ *
+ * @param store
+ * @param provider
+ */
+ void addCommitHandler(DataStoreIdentifier store, DataCommitHandler provider);
+
+ /**
+ * Removes {@link DataCommitHandler} from specified data store
+ *
+ * @param store
+ * @param provider
+ */
+ void removeCommitHandler(DataStoreIdentifier store,
+ DataCommitHandler provider);
+
+ /**
+ * Adds {@link DataRefresher} for specified data store
+ *
+ * @param store
+ * @param refresher
+ */
+ void addRefresher(DataStoreIdentifier store, DataRefresher refresher);
+
+ /**
+ * Removes {@link DataRefresher} from specified data store
+ *
+ * @param store
+ * @param refresher
+ */
+ void removeRefresher(DataStoreIdentifier store, DataRefresher refresher);
+
+ public interface DataRefresher extends Provider.ProviderFunctionality {
+
+ /**
+ * Fired when some component explicitly requested the data refresh.
+ *
+ * The provider which exposed the {@link DataRefresher} should republish
+ * its provided data by editing the data in all affected data stores.
+ */
+ void refreshData();
+ }
+}
-/*\r
- * Copyright (c) 2013 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.api.data;\r
-\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-\r
-\r
-/**\r
- * {@link Provider}-supplied Validator of the data.\r
- * \r
- * <p>\r
- * The registration could be done by :\r
- * <ul>\r
- * <li>returning an instance of implementation in the return value of\r
- * {@link Provider#getProviderFunctionality()}\r
- * <li>passing an instance of implementation and {@link DataStoreIdentifier} rpc\r
- * as arguments to the\r
- * {@link DataProviderService#addValidator(DataStoreIdentifier, DataValidator)}\r
- * </ul>\r
- * \r
- **/\r
-public interface DataValidator extends Provider.ProviderFunctionality {\r
-\r
- /**\r
- * A set of Data Stores supported by implementation.\r
- * \r
- * The set of {@link DataStoreIdentifier}s which identifies target data\r
- * stores which are supported by this implementation. This set is used, when\r
- * {@link Provider} is registered to the SAL, to register and expose the\r
- * validation functionality to affected data stores.\r
- * \r
- * @return Set of Data Store identifiers\r
- */\r
- Set<DataStoreIdentifier> getSupportedDataStores();\r
-\r
- /**\r
- * Performs validation on supplied data.\r
- * \r
- * @param toValidate\r
- * Data to validate\r
- * @return Validation result. The\r
- * <code>{@link RpcResult#isSuccessful()} == true</code> if the data\r
- * passed validation, otherwise contains list of errors.\r
- */\r
- RpcResult<Void> validate(CompositeNode toValidate);\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.data;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+
+/**
+ * {@link Provider}-supplied Validator of the data.
+ *
+ * <p>
+ * The registration could be done by :
+ * <ul>
+ * <li>returning an instance of implementation in the return value of
+ * {@link Provider#getProviderFunctionality()}
+ * <li>passing an instance of implementation and {@link DataStoreIdentifier} rpc
+ * as arguments to the
+ * {@link DataProviderService#addValidator(DataStoreIdentifier, DataValidator)}
+ * </ul>
+ *
+ **/
+public interface DataValidator extends Provider.ProviderFunctionality {
+
+ /**
+ * A set of Data Stores supported by implementation.
+ *
+ * The set of {@link DataStoreIdentifier}s which identifies target data
+ * stores which are supported by this implementation. This set is used, when
+ * {@link Provider} is registered to the SAL, to register and expose the
+ * validation functionality to affected data stores.
+ *
+ * @return Set of Data Store identifiers
+ */
+ Set<DataStoreIdentifier> getSupportedDataStores();
+
+ /**
+ * Performs validation on supplied data.
+ *
+ * @param toValidate
+ * Data to validate
+ * @return Validation result. The
+ * <code>{@link RpcResult#isSuccessful()} == true</code> if the data
+ * passed validation, otherwise contains list of errors.
+ */
+ RpcResult<Void> validate(CompositeNode toValidate);
+
+}
-/*\r
- * Copyright (c) 2013 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
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
package org.opendaylight.controller.sal.core.api.data;
\ No newline at end of file
-/*\r
- * Copyright (c) 2013 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.api.model;\r
-\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-import org.opendaylight.controller.yang.model.api.Module;\r
-import org.opendaylight.controller.yang.model.api.SchemaContext;\r
-\r
-public interface SchemaService extends BrokerService {\r
-\r
- /**\r
- * Registers a YANG module to session and global context \r
- * \r
- * @param module\r
- */\r
- void addModule(Module module);\r
- \r
- /**\r
- * Unregisters a YANG module from session context\r
- * \r
- * @param module\r
- */\r
- void removeModule(Module module);\r
- \r
- /**\r
- * Returns session specific YANG schema context\r
- * @return\r
- */\r
- SchemaContext getSessionContext();\r
- \r
- /**\r
- * Returns global schema context\r
- * \r
- * @return\r
- */\r
- SchemaContext getGlobalContext();\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.model;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public interface SchemaService extends BrokerService {
+
+ /**
+ * Registers a YANG module to session and global context
+ *
+ * @param module
+ */
+ void addModule(Module module);
+
+ /**
+ * Unregisters a YANG module from session context
+ *
+ * @param module
+ */
+ void removeModule(Module module);
+
+ /**
+ * Returns session specific YANG schema context
+ * @return
+ */
+ SchemaContext getSessionContext();
+
+ /**
+ * Returns global schema context
+ *
+ * @return
+ */
+ SchemaContext getGlobalContext();
+}
-/*\r
- * Copyright (c) 2013 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.api.notify;\r
-\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.core.api.Consumer;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-\r
-\r
-/**\r
- * Notification listener for SAL notifications.\r
- */\r
-public interface NotificationListener extends Consumer.ConsumerFunctionality {\r
- /**\r
- * A set of notification types supported by listeners.\r
- * \r
- * The set of notification {@link QName}s which are supported by this\r
- * listener. This set is used, when {@link Consumer} is registered to the\r
- * SAL, to automatically register the listener.\r
- * \r
- * @return Set of QNames identifying supported notifications.\r
- */\r
- Set<QName> getSupportedNotifications();\r
-\r
- /**\r
- * Fired when the notification occurs.\r
- * \r
- * The type of the notification could be learned by\r
- * <code>QName type = notification.getNodeType();</code>\r
- * \r
- * @param notification\r
- * Notification content\r
- */\r
- void onNotification(CompositeNode notification);\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.notify;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+
+/**
+ * Notification listener for SAL notifications.
+ */
+public interface NotificationListener extends Consumer.ConsumerFunctionality {
+ /**
+ * A set of notification types supported by listeners.
+ *
+ * The set of notification {@link QName}s which are supported by this
+ * listener. This set is used, when {@link Consumer} is registered to the
+ * SAL, to automatically register the listener.
+ *
+ * @return Set of QNames identifying supported notifications.
+ */
+ Set<QName> getSupportedNotifications();
+
+ /**
+ * Fired when the notification occurs.
+ *
+ * The type of the notification could be learned by
+ * <code>QName type = notification.getNodeType();</code>
+ *
+ * @param notification
+ * Notification content
+ */
+ void onNotification(CompositeNode notification);
+}
-/*\r
- * Copyright (c) 2013 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.api.notify;\r
-\r
-import org.opendaylight.controller.sal.core.api.Broker;\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-\r
-\r
-/**\r
- * Notification Publishing Service\r
- * \r
- * The simplified process of the notification publishing is following:\r
- * \r
- * <ol>\r
- * <li> {@link Provider} invokes {@link #sendNotification(CompositeNode)}\r
- * <li> {@link Broker} finds {@link NotificationListener}s which subscribed for\r
- * the notification type.\r
- * \r
- * <li>For each subscriber {@link Broker} invokes\r
- * {@link NotificationListener#onNotification(CompositeNode)}\r
- * </ol>\r
- * \r
- * \r
- * \r
- */\r
-public interface NotificationProviderService extends NotificationService {\r
-\r
- /**\r
- * Publishes a notification.\r
- * \r
- * Notification type is determined by the\r
- * {@link CompositeNode#getNodeType()} of the\r
- * <code>notification<code> parameter.\r
- * \r
- * @param notification\r
- * Notification to publish\r
- */\r
- void sendNotification(CompositeNode notification);\r
-\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.notify;
+
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+
+/**
+ * Notification Publishing Service
+ *
+ * The simplified process of the notification publishing is following:
+ *
+ * <ol>
+ * <li> {@link Provider} invokes {@link #sendNotification(CompositeNode)}
+ * <li> {@link Broker} finds {@link NotificationListener}s which subscribed for
+ * the notification type.
+ *
+ * <li>For each subscriber {@link Broker} invokes
+ * {@link NotificationListener#onNotification(CompositeNode)}
+ * </ol>
+ *
+ *
+ *
+ */
+public interface NotificationProviderService extends NotificationService {
+
+ /**
+ * Publishes a notification.
+ *
+ * Notification type is determined by the
+ * {@link CompositeNode#getNodeType()} of the
+ * <code>notification<code> parameter.
+ *
+ * @param notification
+ * Notification to publish
+ */
+ void sendNotification(CompositeNode notification);
+
+}
-/*\r
- * Copyright (c) 2013 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.api.notify;\r
-\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-import org.opendaylight.controller.sal.core.api.RpcImplementation;\r
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
-import org.opendaylight.controller.yang.common.QName;\r
-\r
-\r
-/**\r
- * NotificationService provides access to the notification functionality of the\r
- * SAL.\r
- * \r
- * NotificationService allows for consumption of notifications by registering\r
- * implementations of NotificationListener.\r
- * \r
- * The registration of notification listeners could be done by:\r
- * <ul>\r
- * <li>returning an instance of implementation in the return value of\r
- * {@link Provider#getProviderFunctionality()}\r
- * <li>passing an instance of implementation and {@link QName} of rpc as an\r
- * arguments to the\r
- * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}\r
- * </ul>\r
- * \r
- * \r
- */\r
-public interface NotificationService extends BrokerService {\r
-\r
- /**\r
- * Registers a notification listener for supplied notification type.\r
- * \r
- * @param notification\r
- * @param listener\r
- */\r
- void addNotificationListener(QName notification,\r
- NotificationListener listener);\r
-\r
- /**\r
- * Removes a notification listener for supplied notification type.\r
- * \r
- * @param notification\r
- * @param listener\r
- */\r
- void removeNotificationListener(QName notification,\r
- NotificationListener listener);\r
-}\r
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.notify;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.yangtools.yang.common.QName;
+
+
+/**
+ * NotificationService provides access to the notification functionality of the
+ * SAL.
+ *
+ * NotificationService allows for consumption of notifications by registering
+ * implementations of NotificationListener.
+ *
+ * The registration of notification listeners could be done by:
+ * <ul>
+ * <li>returning an instance of implementation in the return value of
+ * {@link Provider#getProviderFunctionality()}
+ * <li>passing an instance of implementation and {@link QName} of rpc as an
+ * arguments to the
+ * {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}
+ * </ul>
+ *
+ *
+ */
+public interface NotificationService extends BrokerService {
+
+ /**
+ * Registers a notification listener for supplied notification type.
+ *
+ * @param notification
+ * @param listener
+ */
+ void addNotificationListener(QName notification,
+ NotificationListener listener);
+
+ /**
+ * Removes a notification listener for supplied notification type.
+ *
+ * @param notification
+ * @param listener
+ */
+ void removeNotificationListener(QName notification,
+ NotificationListener listener);
+}
-/*\r
- * Copyright (c) 2013 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
-/**\r
- * SAL Notification functionality\r
- */\r
+/*
+ * 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
+ */
+/**
+ * SAL Notification functionality
+ */
package org.opendaylight.controller.sal.core.api.notify;
\ No newline at end of file
-/*\r
- * Copyright (c) 2013 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
-/**\r
- * Core binding-independent SAL contracts and components\r
- */\r
-package org.opendaylight.controller.sal.core.api;\r
+/*
+ * 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
+ */
+/**
+ * Core binding-independent SAL contracts and components
+ */
+package org.opendaylight.controller.sal.core.api;
<modelVersion>4.0.0</modelVersion>\r
<parent>\r
<groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
+ <artifactId>sal-parent</artifactId>\r
<version>1.0-SNAPSHOT</version>\r
</parent>\r
<artifactId>sal-core-demo</artifactId>\r
\r
</plugins>\r
\r
- </build>
+ </build>\r
</project>
\ No newline at end of file
<modelVersion>4.0.0</modelVersion>\r
<parent>\r
<groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
+ <artifactId>sal-parent</artifactId>\r
<version>1.0-SNAPSHOT</version>\r
</parent>\r
<artifactId>sal-core-spi</artifactId>\r
<modelVersion>4.0.0</modelVersion>\r
<parent>\r
<groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
+ <artifactId>sal-parent</artifactId>\r
<version>1.0-SNAPSHOT</version>\r
</parent>\r
<artifactId>sal-data-api</artifactId>\r
<modelVersion>4.0.0</modelVersion>\r
<parent>\r
<groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
+ <artifactId>sal-parent</artifactId>\r
<version>1.0-SNAPSHOT</version>\r
</parent>\r
<artifactId>sal-schema-repository-api</artifactId>\r
--- /dev/null
+<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>
+ <artifactId>sal-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <groupId>org.opendaylight.controller</groupId>
+ </parent>
+ <packaging>pom</packaging>
+ <artifactId>sal-samples</artifactId>
+ <modules>
+ <module>toaster</module>
+ <module>toaster-consumer</module>
+ <module>toaster-it</module>
+ <module>toaster-provider</module>
+ </modules>
+ <groupId>org.opendaylight.controller.samples</groupId>
+</project>
--- /dev/null
+<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>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sample-toaster-consumer</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Export-Package>org.opendaylight.controller.sample.toaster.provider.api</Export-Package>
+ <Private-Package>org.opendaylight.controller.sample.toaster.provider.impl</Private-Package>
+ <Bundle-Activator>org.opendaylight.controller.sample.toaster.provider.impl.ToastConsumerImpl</Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /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.sample.toaster.provider.api;
+
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToastType;
+
+public interface ToastConsumer {
+
+ boolean createToast(Class<? extends ToastType> type,int doneness);
+
+}
--- /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.sample.toaster.provider.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.common.GlobalDataStore;
+import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.MakeToastInputBuilder;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToastDone;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToastType;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.Toaster;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToasterData;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToasterService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ToastConsumerImpl extends AbstractBindingAwareConsumer implements BundleActivator, BindingAwareConsumer, ToastConsumer,
+ NotificationListener<ToastDone> {
+
+ private static final Logger log = LoggerFactory.getLogger(ToastConsumerImpl.class);
+
+ private ToasterService toaster;
+
+ private ConsumerContext session;
+
+ @Override
+ public boolean createToast(Class<? extends ToastType> type, int doneness) {
+ MakeToastInputBuilder toastInput = new MakeToastInputBuilder();
+ toastInput.setToasterDoneness((long) doneness);
+ toastInput.setToasterToastType(type);
+
+ try {
+ RpcResult<Void> result = getToastService().makeToast(toastInput.build()).get();
+
+ if (result.isSuccessful()) {
+ log.info("Toast was successfuly finished");
+ } else {
+ log.info("Toast was not successfuly finished");
+ }
+ return result.isSuccessful();
+ } catch (InterruptedException | ExecutionException e) {
+ log.info("Error occured during toast creation");
+ }
+ return false;
+
+ }
+
+ @Override
+ @Deprecated
+ protected void startImpl(BundleContext context) {
+ context.registerService(ToastConsumer.class, this, new Hashtable<String,String>());
+ }
+
+ @Override
+ public void onSessionInitialized(ConsumerContext session) {
+ this.session = session;
+ NotificationService notificationService = session.getSALService(NotificationService.class);
+ notificationService.addNotificationListener(ToastDone.class, this);
+
+
+ }
+
+ private void loadToasterData() {
+ // We request data store service implementation
+ DataBrokerService brokerService = session.getSALService(DataBrokerService.class);
+
+ ToasterData data = brokerService.getData(GlobalDataStore.RuntimeInfo, ToasterData.class);
+ Toaster toaster = data.getToaster();
+ log.info("Available toaster is: ", toaster.getToasterManufacturer(),toaster.getToasterModelNumber());
+ }
+
+ @Override
+ public void onNotification(ToastDone notification) {
+ log.info("ToastDone Notification Received: {} ",notification.getToastStatus());
+
+ }
+
+ private ToasterService getToastService() {
+ if (toaster == null) {
+ toaster = session.getRpcService(ToasterService.class);
+ }
+ return toaster;
+ }
+
+}
--- /dev/null
+<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>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sample-toaster-it</artifactId>
+
+ <properties>
+ <exam.version>3.0.0</exam.version>
+ <url.version>1.5.0</url.version>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>maven-paxexam-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-config</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings
+ only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>
+ org.ops4j.pax.exam
+ </groupId>
+ <artifactId>
+ maven-paxexam-plugin
+ </artifactId>
+ <versionRange>
+ [1.2.4,)
+ </versionRange>
+ <goals>
+ <goal>
+ generate-depends-file
+ </goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-consumer</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-provider</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-native</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-link-mvn</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.osgi</artifactId>
+ <version>3.8.1.v20120830-144521</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ <version>1.7.2</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>1.0.9</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.0.9</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+package org.opendaylight.controller.sample.toaster.it;
+
+import static org.junit.Assert.*;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.CoreOptions.maven;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sample.toaster.provider.ToasterProvider;
+import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToasterService;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.WhiteBread;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(PaxExam.class)
+public class ToasterTest {
+
+ public static final String ODL = "org.opendaylight.controller";
+ public static final String YANG = "org.opendaylight.yangtools";
+ public static final String SAMPLE = "org.opendaylight.controller.samples";
+
+ @Test
+ public void properInitialized() throws Exception {
+
+ Collection<ServiceReference<ToasterService>> references = ctx
+ .getServiceReferences(ToasterService.class, null);
+ assertEquals(2, references.size());
+
+ consumer.createToast(WhiteBread.class, 5);
+
+ }
+
+ @Inject
+ BindingAwareBroker broker;
+
+ @Inject
+ ToastConsumer consumer;
+
+ @Inject
+ BundleContext ctx;
+
+ @Configuration
+ public Option[] config() {
+ return options(systemProperty("osgi.console").value("2401"),
+ mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(),
+ mavenBundle("org.slf4j", "log4j-over-slf4j")
+ .versionAsInProject(),
+ mavenBundle("ch.qos.logback", "logback-core")
+ .versionAsInProject(),
+ mavenBundle("ch.qos.logback", "logback-classic")
+ .versionAsInProject(),
+ mavenBundle(ODL, "sal-binding-api").versionAsInProject(),
+ mavenBundle(ODL, "sal-binding-broker-impl")
+ .versionAsInProject(), mavenBundle(ODL, "sal-common")
+ .versionAsInProject(),
+ mavenBundle(ODL, "sal-common-util").versionAsInProject(),
+ mavenBundle(SAMPLE, "sample-toaster").versionAsInProject(),
+ mavenBundle(SAMPLE, "sample-toaster-consumer")
+ .versionAsInProject(),
+ mavenBundle(SAMPLE, "sample-toaster-provider")
+ .versionAsInProject(),
+ mavenBundle(YANG, "yang-binding").versionAsInProject(),
+ mavenBundle(YANG, "yang-common").versionAsInProject(),
+ mavenBundle("com.google.guava", "guava").versionAsInProject(),
+ junitBundles(), mavenBundle("org.javassist", "javassist")
+ .versionAsInProject());
+ }
+
+}
--- /dev/null
+<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>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sample-toaster-provider</artifactId>
+ <packaging>bundle</packaging>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.sample.toaster.provider.ToasterProvider</Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+package org.opendaylight.controller.sample.toaster.provider;
+
+import java.util.Collections;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.common.util.Futures;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.DisplayString;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.MakeToastInput;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToastDone.ToastStatus;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToastDoneBuilder;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.Toaster;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.Toaster.ToasterStatus;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToasterBuilder;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToasterData;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToasterService;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OpendaylightToaster implements ToasterData, ToasterService {
+
+ private static final Logger log = LoggerFactory.getLogger(OpendaylightToaster.class);
+
+ private static final DisplayString toasterManufacturer = new DisplayString("Opendaylight");
+ private static final DisplayString toasterModelNumber = new DisplayString("Model 1 - Binding Aware");
+ private ToasterStatus toasterStatus;
+
+ private NotificationProviderService notificationProvider;
+ private final ExecutorService executor;
+
+ private Future<RpcResult<Void>> currentTask;
+
+ public OpendaylightToaster() {
+ toasterStatus = ToasterStatus.Down;
+ executor = Executors.newFixedThreadPool(1);
+ }
+
+ @Override
+ public Toaster getToaster() {
+ ToasterBuilder tb = new ToasterBuilder();
+ tb //
+ .setToasterManufacturer(toasterManufacturer) //
+ .setToasterModelNumber(toasterModelNumber) //
+ .setToasterStatus(toasterStatus);
+
+ return tb.build();
+ }
+
+ @Override
+ public Future<RpcResult<Void>> cancelToast() {
+ if (currentTask != null) {
+ cancelToastImpl();
+ }
+ return null;
+ }
+
+ @Override
+ public Future<RpcResult<Void>> makeToast(MakeToastInput input) {
+ // TODO Auto-generated method stub
+ log.info("makeToast - Received input for toast");
+ logToastInput(input);
+ if (currentTask != null) {
+ return inProgressError();
+ }
+ currentTask = executor.submit(new MakeToastTask(input));
+ return currentTask;
+ }
+
+ private Future<RpcResult<Void>> inProgressError() {
+ RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Collections.<RpcError> emptySet());
+ return Futures.immediateFuture(result);
+ }
+
+ private void cancelToastImpl() {
+ currentTask.cancel(true);
+ ToastDoneBuilder toastDone = new ToastDoneBuilder();
+ toastDone.setToastStatus(ToastStatus.Cancelled);
+ notificationProvider.notify(toastDone.build());
+ }
+
+ public void setNotificationProvider(NotificationProviderService salService) {
+ this.notificationProvider = salService;
+ }
+
+ private void logToastInput(MakeToastInput input) {
+ String toastType = input.getToasterToastType().getName();
+ String toastDoneness = input.getToasterDoneness().toString();
+ log.info("Toast: {} doneness: {}", toastType, toastDoneness);
+ }
+
+ private class MakeToastTask implements Callable<RpcResult<Void>> {
+
+ final MakeToastInput toastRequest;
+
+ public MakeToastTask(MakeToastInput toast) {
+ toastRequest = toast;
+ }
+
+ @Override
+ public RpcResult<Void> call() throws Exception {
+ Thread.sleep(1000);
+
+ ToastDoneBuilder notifyBuilder = new ToastDoneBuilder();
+ notifyBuilder.setToastStatus(ToastStatus.Done);
+ notificationProvider.notify(notifyBuilder.build());
+ log.info("Toast Done");
+ logToastInput(toastRequest);
+ return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
+ }
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sample.toaster.provider;
+
+public class ToasterActivator {
+
+}
--- /dev/null
+package org.opendaylight.controller.sample.toaster.provider;
+import java.util.Collection;
+import java.util.Collections;
+
+
+import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev20091120.ToasterService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class ToasterProvider extends AbstractBindingAwareProvider {
+ private static final Logger log = LoggerFactory.getLogger(ToasterProvider.class);
+
+ private ConsumerContext consumerContext;
+ private ProviderContext providerContext;
+ private OpendaylightToaster toaster;
+
+
+ public ToasterProvider() {
+ toaster = new OpendaylightToaster();
+ }
+
+ @Override
+ public void onSessionInitialized(ConsumerContext session) {
+ log.info("Consumer Session initialized");
+ this.consumerContext = session;
+
+ }
+
+ @Override
+ public void onSessionInitiated(ProviderContext session) {
+ log.info("Provider Session initialized");
+
+ this.providerContext = session;
+ toaster.setNotificationProvider(session.getSALService(NotificationProviderService.class));
+ providerContext.addRpcImplementation(ToasterService.class, toaster);
+ }
+
+
+ @Override
+ public Collection<? extends RpcService> getImplementations() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection<? extends ProviderFunctionality> getFunctionality() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ @Deprecated
+ protected void startImpl(BundleContext context) {
+ // TODO Auto-generated method stub
+
+ }
+}
--- /dev/null
+<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>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sample-toaster</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>0.5.7-SNAPSHOT</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ target/generated-sources/sal
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>false</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>0.5.7-SNAPSHOT</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>target/generated-sources/sal</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse
+ m2e settings only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>
+ org.opendaylight.yangtools
+ </groupId>
+ <artifactId>
+ yang-maven-plugin
+ </artifactId>
+ <versionRange>
+ [0.5,)
+ </versionRange>
+ <goals>
+ <goal>
+ generate-sources
+ </goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+module toaster {
+
+ yang-version 1;
+
+ namespace
+ "http://netconfcentral.org/ns/toaster";
+
+ prefix toast;
+
+ organization "Netconf Central";
+
+ contact
+ "Andy Bierman <andy@netconfcentral.org>";
+
+ description
+ "YANG version of the TOASTER-MIB.";
+
+ revision "2009-11-20" {
+ description
+ "Toaster module in progress.";
+ }
+
+
+ identity toast-type {
+ description
+ "Base for all bread types supported by the toaster.
+ New bread types not listed here nay be added in the
+ future.";
+ }
+
+ identity white-bread {
+ base toast:toast-type;
+ description "White bread.";
+ }
+
+ identity wheat-bread {
+ base toast-type;
+ description "Wheat bread.";
+ }
+
+ identity wonder-bread {
+ base toast-type;
+ description "Wonder bread.";
+ }
+
+ identity frozen-waffle {
+ base toast-type;
+ description "Frozen waffle.";
+ }
+
+ identity frozen-bagel {
+ base toast-type;
+ description "Frozen bagel.";
+ }
+
+ identity hash-brown {
+ base toast-type;
+ description "Hash browned potatos.";
+ }
+
+ typedef DisplayString {
+ type string;
+ description
+ "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION.";
+ reference
+ "RFC 2579, section 2.";
+
+ }
+
+ container toaster {
+ presence
+ "Indicates the toaster service is available";
+ description
+ "Top-level container for all toaster database objects.";
+ leaf toasterManufacturer {
+ type DisplayString;
+ config false;
+ mandatory true;
+ description
+ "The name of the toaster's manufacturer. For instance,
+ Microsoft Toaster.";
+ }
+
+ leaf toasterModelNumber {
+ type DisplayString;
+ config false;
+ mandatory true;
+ description
+ "The name of the toaster's model. For instance,
+ Radiant Automatic.";
+ }
+
+ leaf toasterStatus {
+ type enumeration {
+ enum "up" {
+ value 1;
+ description
+ "The toaster knob position is up.
+ No toast is being made now.";
+ }
+ enum "down" {
+ value 2;
+ description
+ "The toaster knob position is down.
+ Toast is being made now.";
+ }
+ }
+ config false;
+ mandatory true;
+ description
+ "This variable indicates the current state of
+ the toaster.";
+ }
+ } // container toaster
+
+ rpc make-toast {
+ description
+ "Make some toast.
+ The toastDone notification will be sent when
+ the toast is finished.
+ An 'in-use' error will be returned if toast
+ is already being made.
+ A 'resource-denied' error will be returned
+ if the toaster service is disabled.";
+ input {
+ leaf toasterDoneness {
+ type uint32 {
+ range "1 .. 10";
+ }
+ default '5';
+ description
+ "This variable controls how well-done is the
+ ensuing toast. It should be on a scale of 1 to 10.
+ Toast made at 10 generally is considered unfit
+ for human consumption; toast made at 1 is warmed
+ lightly.";
+ }
+
+ leaf toasterToastType {
+ type identityref {
+ base toast:toast-type;
+ }
+ default 'wheat-bread';
+ description
+ "This variable informs the toaster of the type of
+ material that is being toasted. The toaster
+ uses this information, combined with
+ toasterDoneness, to compute for how
+ long the material must be toasted to achieve
+ the required doneness.";
+ }
+ }
+ } // rpc make-toast
+
+ rpc cancel-toast {
+ description
+ "Stop making toast, if any is being made.
+ A 'resource-denied' error will be returned
+ if the toaster service is disabled.";
+ } // rpc cancel-toast
+
+ notification toastDone {
+ description
+ "Indicates that the toast in progress has completed.";
+ leaf toastStatus {
+ type enumeration {
+ enum "done" {
+ value 0;
+ description "The toast is done.";
+ }
+ enum "cancelled" {
+ value 1;
+ description
+ "The toast was cancelled.";
+ }
+ enum "error" {
+ value 2;
+ description
+ "The toaster service was disabled or
+ the toaster is broken.";
+ }
+ }
+ description
+ "Indicates the final toast status";
+ }
+ } // notification toastDone
+ } // module toaster
try {
clusterContainerService.createCache("forwarding.ipswitch.rules",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheExistException cee) {
log.error("\nCache already exists - destroy and recreate if needed");
} catch (CacheConfigException cce) {
try {
clusterContainerService.createCache("statisticsmanager.flowStatistics",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("statisticsmanager.nodeConnectorStatistics",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("statisticsmanager.tableStatistics",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("statisticsmanager.descriptionStatistics",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheConfigException cce) {
log.error("Statistics cache configuration invalid - check cache mode");
import java.util.Set;
import org.apache.felix.dm.Component;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.configuration.IConfigurationContainerAware;
import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
@Override
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(SwitchManager.class)) {
- Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
- Set<String> propSet = new HashSet<String>();
- propSet.add("switchmanager.configSaveEvent");
- props.put("cachenames", propSet);
// export the service
c.setInterface(new String[] {
IListenInventoryUpdates.class.getName(),
ISwitchManager.class.getName(),
- ICacheUpdateAware.class.getName(),
- IConfigurationContainerAware.class.getName() }, props);
+ IConfigurationContainerAware.class.getName() }, null);
// Now lets add a service dependency to make sure the
// provider of service exists
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Date;
import java.util.Dictionary;
import java.util.EnumSet;
import java.util.Enumeration;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.opendaylight.controller.clustering.services.CacheConfigException;
import org.opendaylight.controller.clustering.services.CacheExistException;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
import org.opendaylight.controller.configuration.IConfigurationContainerAware;
* instance per container of the network. All the node/nodeConnector properties
* are maintained in the default container only.
*/
-public class SwitchManager implements ISwitchManager,
-IConfigurationContainerAware, IObjectReader,
-ICacheUpdateAware<Long, String>, IListenInventoryUpdates,
-CommandProvider {
+public class SwitchManager implements ISwitchManager, IConfigurationContainerAware,
+ IObjectReader, IListenInventoryUpdates, CommandProvider {
private static Logger log = LoggerFactory.getLogger(SwitchManager.class);
private static String ROOT = GlobalConstants.STARTUPHOME.toString();
- private static final String SAVE = "Save";
private String subnetFileName, spanFileName, switchConfigFileName;
private final List<NodeConnector> spanNodeConnectors = new CopyOnWriteArrayList<NodeConnector>();
// Collection of Subnets keyed by the InetAddress
private ConcurrentMap<SpanConfig, SpanConfig> spanConfigList;
// manually configured parameters for the node such as name, tier, mode
private ConcurrentMap<String, SwitchConfig> nodeConfigList;
- private ConcurrentMap<Long, String> configSaveEvent;
private ConcurrentMap<Node, Map<String, Property>> nodeProps;
private ConcurrentMap<NodeConnector, Map<String, Property>> nodeConnectorProps;
private ConcurrentMap<Node, Map<String, NodeConnector>> nodeConnectorNames;
try {
clusterContainerService.createCache(
"switchmanager.subnetsConfigList",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("switchmanager.spanConfigList",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("switchmanager.nodeConfigList",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("switchmanager.subnets",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache(
"switchmanager.configSaveEvent",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache("switchmanager.nodeProps",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache(
"switchmanager.nodeConnectorProps",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache(
"switchmanager.nodeConnectorNames",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterContainerService.createCache(
"switchmanager.controllerProps",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheConfigException cce) {
log.error("\nCache configuration invalid - check cache mode");
} catch (CacheExistException ce) {
log.error("\nFailed to get cache for subnets");
}
- configSaveEvent = (ConcurrentMap<Long, String>) clusterContainerService
- .getCache("switchmanager.configSaveEvent");
- if (configSaveEvent == null) {
- log.error("\nFailed to get cache for configSaveEvent");
- }
-
nodeProps = (ConcurrentMap<Node, Map<String, Property>>) clusterContainerService
.getCache("switchmanager.nodeProps");
if (nodeProps == null) {
spanConfigList = new ConcurrentHashMap<SpanConfig, SpanConfig>();
nodeConfigList = new ConcurrentHashMap<String, SwitchConfig>();
subnets = new ConcurrentHashMap<InetAddress, Subnet>();
- configSaveEvent = new ConcurrentHashMap<Long, String>();
nodeProps = new ConcurrentHashMap<Node, Map<String, Property>>();
nodeConnectorProps = new ConcurrentHashMap<NodeConnector, Map<String, Property>>();
nodeConnectorNames = new ConcurrentHashMap<Node, Map<String, NodeConnector>>();
switchConfig = new SwitchConfig(nodeId, updateProperties);
} else {
// check if description is configured or was published by any other node
- for (Node n : nodeProps.keySet()) {
+ for (Map.Entry<Node, Map<String, Property>> entry : nodeProps.entrySet()) {
+ Node n = entry.getKey();
Description desc = (Description) getNodeProp(n, Description.propertyName);
NodeDescription nDesc = (this.statisticsManager == null) ? null : this.statisticsManager
.getNodeDescription(n);
return new Status(StatusCode.SUCCESS);
}
Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
- for (String prop : prevNodeProperties.keySet()) {
+ for (Map.Entry<String, Property> entry : prevNodeProperties.entrySet()) {
+ String prop = entry.getKey();
if (!updateProperties.containsKey(prop)) {
if (prop.equals(Description.propertyName)) {
if (!advertisedDesc.isEmpty()) {
Map<String, Property> propMapCurr = nodeProps.get(node);
if ((propMapCurr != null) && (nodeProperties != null) && (!nodeProperties.isEmpty())) {
Map<String, Property> propMap = new HashMap<String, Property>(propMapCurr);
- for (String prop : nodeProperties.keySet()) {
+ for (Map.Entry<String, Property> entry : nodeProperties.entrySet()) {
+ String prop = entry.getKey();
if (prop.equals(Description.propertyName)) {
Map<Node, Map<String, Property>> nodeProp = this.inventoryService.getNodeProps();
if (nodeProp.get(node) != null) {
@Override
public Status saveSwitchConfig() {
- // Publish the save config event to the cluster nodes
- configSaveEvent.put(new Date().getTime(), SAVE);
return saveSwitchConfigInternal();
}
return ncList;
}
- @Override
- public void entryCreated(Long key, String cacheName, boolean local) {
- }
-
- @Override
- public void entryUpdated(Long key, String newValue, String cacheName,
- boolean originLocal) {
- saveSwitchConfigInternal();
- }
-
- @Override
- public void entryDeleted(Long key, String cacheName, boolean originLocal) {
- }
-
private void addNode(Node node, Set<Property> props) {
log.trace("{} added, props: {}", node, props);
if (nodeProps == null) {
return;
}
nodeProps.remove(node);
+ nodeConnectorNames.remove(node);
+ Set<NodeConnector> removeNodeConnectorSet = new HashSet<NodeConnector>();
+ for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+ NodeConnector nodeConnector = entry.getKey();
+ if (nodeConnector.getNode().equals(node)) {
+ removeNodeConnectorSet.add(nodeConnector);
+ }
+ }
+ for (NodeConnector nc : removeNodeConnectorSet) {
+ nodeConnectorProps.remove(nc);
+ }
// check if span ports need to be cleaned up
removeSpanPorts(node);
public void updateNodeConnector(NodeConnector nodeConnector,
UpdateType type, Set<Property> props) {
Map<String, Property> propMap = new HashMap<String, Property>();
+ boolean update = true;
log.debug("updateNodeConnector: {} type {} props {} for container {}",
new Object[] { nodeConnector, type, props, containerName });
switch (type) {
case ADDED:
- case CHANGED:
if (props != null) {
for (Property prop : props) {
addNodeConnectorProp(nodeConnector, prop);
addSpanPort(nodeConnector);
break;
+ case CHANGED:
+ if (!nodeConnectorProps.containsKey(nodeConnector) || (props == null)) {
+ update = false;
+ } else {
+ for (Property prop : props) {
+ addNodeConnectorProp(nodeConnector, prop);
+ propMap.put(prop.getName(), prop);
+ }
+ }
+ break;
case REMOVED:
+ if (!nodeConnectorProps.containsKey(nodeConnector)) {
+ update = false;
+ }
removeNodeConnectorAllProps(nodeConnector);
// clean up span config
removeSpanPort(nodeConnector);
break;
default:
+ update = false;
break;
}
- notifyNodeConnector(nodeConnector, type, propMap);
+ if (update) {
+ notifyNodeConnector(nodeConnector, type, propMap);
+ }
}
@Override
}
Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
- for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+ for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+ NodeConnector nodeConnector = entry.getKey();
if (!nodeConnector.getNode().equals(node)) {
continue;
}
}
Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
- for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+ for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+ NodeConnector nodeConnector = entry.getKey();
if (!nodeConnector.getNode().equals(node)) {
continue;
}
}
Set<NodeConnector> nodeConnectorSet = new HashSet<NodeConnector>();
- for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+ for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+ NodeConnector nodeConnector = entry.getKey();
if (!nodeConnector.getNode().equals(node)
|| isSpecial(nodeConnector)) {
continue;
Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
if (mapCurr != null) {
- for (String s : mapCurr.keySet()) {
+ for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
+ String s = entry.getKey();
try {
- map.put(s, new NodeConnector(mapCurr.get(s)));
+ map.put(s, new NodeConnector(entry.getValue()));
} catch (ConstructionException e) {
e.printStackTrace();
}
Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
if (mapCurr != null) {
Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
- for (String s : mapCurr.keySet()) {
+ for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
+ String s = entry.getKey();
try {
- map.put(s, new NodeConnector(mapCurr.get(s)));
+ map.put(s, new NodeConnector(entry.getValue()));
} catch (ConstructionException e) {
e.printStackTrace();
}
Map<String, NodeConnector> mapCurr = nodeConnectorNames.get(node);
if (mapCurr != null) {
Map<String, NodeConnector> map = new HashMap<String, NodeConnector>();
- for (String s : mapCurr.keySet()) {
+ for (Map.Entry<String, NodeConnector> entry : mapCurr.entrySet()) {
+ String s = entry.getKey();
try {
- map.put(s, new NodeConnector(mapCurr.get(s)));
+ map.put(s, new NodeConnector(entry.getValue()));
} catch (ConstructionException e) {
e.printStackTrace();
}
service.notifyNode(node, type, propMap);
}
- for (NodeConnector nodeConnector : nodeConnectorProps.keySet()) {
+ for (Map.Entry<NodeConnector, Map<String, Property>> entry : nodeConnectorProps.entrySet()) {
+ NodeConnector nodeConnector = entry.getKey();
propMap = nodeConnectorProps.get(nodeConnector);
service.notifyNodeConnector(nodeConnector, type, propMap);
}
try {
this.edgesDB =
(ConcurrentMap<Edge, Set<Property>>) this.clusterContainerService.createCache(TOPOEDGESDB,
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheExistException cee) {
log.debug(TOPOEDGESDB + " Cache already exists - destroy and recreate if needed");
} catch (CacheConfigException cce) {
try {
this.hostsDB =
(ConcurrentMap<NodeConnector, Set<ImmutablePair<Host, Set<Property>>>>) this.clusterContainerService.createCache(
- TOPOHOSTSDB, EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ TOPOHOSTSDB, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheExistException cee) {
log.debug(TOPOHOSTSDB + " Cache already exists - destroy and recreate if needed");
} catch (CacheConfigException cce) {
try {
this.nodeConnectorsDB =
(ConcurrentMap<NodeConnector, Set<Property>>) this.clusterContainerService.createCache(
- TOPONODECONNECTORDB, EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ TOPONODECONNECTORDB, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheExistException cee) {
log.debug(TOPONODECONNECTORDB + " Cache already exists - destroy and recreate if needed");
} catch (CacheConfigException cce) {
try {
this.userLinksDB =
(ConcurrentMap<String, TopologyUserLinkConfig>) this.clusterContainerService.createCache(
- TOPOUSERLINKSDB, EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ TOPOUSERLINKSDB, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheExistException cee) {
log.debug(TOPOUSERLINKSDB + " Cache already exists - destroy and recreate if needed");
} catch (CacheConfigException cce) {
import java.util.Set;
import org.apache.felix.dm.Component;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
import org.opendaylight.controller.configuration.IConfigurationAware;
import org.opendaylight.controller.containermanager.IContainerAuthorization;
*/
protected void configureGlobalInstance(Component c, Object imp) {
if (imp.equals(UserManagerImpl.class)) {
- // export the service
- Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
- Set<String> propSet = new HashSet<String>();
- propSet.add("usermanager.localUserSaveConfigEvent");
- propSet.add("usermanager.remoteServerSaveConfigEvent");
- propSet.add("usermanager.authorizationSaveConfigEvent");
- props.put("cachenames", propSet);
// export the service
- c.setInterface(new String[] { ICacheUpdateAware.class.getName(),
+ c.setInterface(new String[] {
IUserManager.class.getName(),
- IConfigurationAware.class.getName() }, props);
+ IConfigurationAware.class.getName() }, null);
c.add(createServiceDependency().setService(
IClusterGlobalServices.class).setCallbacks(
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.opendaylight.controller.clustering.services.CacheConfigException;
import org.opendaylight.controller.clustering.services.CacheExistException;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
import org.opendaylight.controller.configuration.IConfigurationAware;
* The internal implementation of the User Manager.
*/
public class UserManagerImpl implements IUserManager, IObjectReader,
- IConfigurationAware, ICacheUpdateAware<Long, String>, CommandProvider,
- AuthenticationProvider {
+ IConfigurationAware, CommandProvider, AuthenticationProvider {
private static final Logger logger = LoggerFactory
.getLogger(UserManagerImpl.class);
private static final String defaultAdmin = "admin";
try {
clusterGlobalService.createCache("usermanager.localUserConfigList",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterGlobalService.createCache(
"usermanager.remoteServerConfigList",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterGlobalService.createCache(
"usermanager.authorizationConfList",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterGlobalService.createCache("usermanager.activeUsers",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterGlobalService.createCache(
"usermanager.localUserSaveConfigEvent",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterGlobalService.createCache(
"usermanager.remoteServerSaveConfigEvent",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
clusterGlobalService.createCache(
"usermanager.authorizationSaveConfigEvent",
- EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
} catch (CacheConfigException cce) {
logger.error("Cache configuration invalid - check cache mode");
} catch (CacheExistException ce) {
@Override
public Status saveLocalUserList() {
- // Publish the save config event to the cluster nodes
- localUserListSaveConfigEvent.put(new Date().getTime(), SAVE);
return saveLocalUserListInternal();
}
@Override
public Status saveAAAServerList() {
- // Publish the save config event to the cluster nodes
- remoteServerSaveConfigEvent.put(new Date().getTime(), SAVE);
return saveAAAServerListInternal();
}
@Override
public Status saveAuthorizationList() {
- // Publish the save config event to the cluster nodes
- authorizationSaveConfigEvent.put(new Date().getTime(), SAVE);
return saveAuthorizationListInternal();
}
}
for (UserConfig conf : confList.values()) {
- addLocalUser(conf);
+ addRemoveLocalUserInternal(conf, false);
}
}
/*
* Interaction with GUI START
*/
- public Status addRemoveLocalUser(UserConfig AAAconf, boolean delete) {
+ private Status addRemoveLocalUser(UserConfig AAAconf, boolean delete) {
// UserConfig Validation check
Status validCheck = AAAconf.validate();
if (!validCheck.isSuccess()) {
return validCheck;
}
+ String user = AAAconf.getUser();
+
+ // Check default admin user
+ if (user.equals(UserManagerImpl.defaultAdmin)) {
+ String msg = "Invalid Request: Default Network Admin User cannot be " + ((delete)? "removed" : "added");
+ logger.debug(msg);
+ return new Status(StatusCode.NOTALLOWED, msg);
+ }
+
+ // Check user presence/conflict
+ StatusCode statusCode = null;
+ String reason = null;
+ if (delete && !localUserConfigList.containsKey(user)) {
+ reason = "not found";
+ statusCode = StatusCode.NOTFOUND;
+ } else if (!delete && localUserConfigList.containsKey(user)) {
+ reason = "already present";
+ statusCode = StatusCode.CONFLICT;
+ }
+ if (statusCode != null) {
+ String msg = String.format("User %s %s in configuration database", user, reason);
+ logger.debug(msg);
+ return new Status(statusCode, msg);
+ }
+
+ return addRemoveLocalUserInternal(AAAconf, delete);
+ }
+
+ private Status addRemoveLocalUserInternal(UserConfig AAAconf, boolean delete) {
// Update Config database
if (delete) {
- if (AAAconf.getUser().equals(UserManagerImpl.defaultAdmin)) {
- String msg = "Invalid Request: Default Network Admin User "
- + "cannot be deleted";
- logger.debug(msg);
- return new Status(StatusCode.NOTALLOWED, msg);
- }
localUserConfigList.remove(AAAconf.getUser());
/*
* A user account has been removed form local database, we assume
*/
removeUserFromActiveList(AAAconf.getUser());
} else {
- if (AAAconf.getUser().equals(UserManagerImpl.defaultAdmin)) {
- String msg = "Invalid Request: Default Network Admin User "
- + "cannot be added";
- logger.debug(msg);
- return new Status(StatusCode.NOTALLOWED, msg);
- }
localUserConfigList.put(AAAconf.getUser(), AAAconf);
}
- return new Status(StatusCode.SUCCESS, null);
+ return new Status(StatusCode.SUCCESS);
}
private Status addRemoveAAAServer(ServerConfig AAAconf, boolean delete) {
remoteServerConfigList.put(AAAconf.getAddress(), AAAconf);
}
- return new Status(StatusCode.SUCCESS, null);
+ return new Status(StatusCode.SUCCESS);
}
private Status addRemoveAuthInfo(AuthorizationConfig AAAconf, boolean delete) {
authorizationConfList.put(AAAconf.getUser(), AAAconf);
}
- return new Status(StatusCode.SUCCESS, null);
+ return new Status(StatusCode.SUCCESS);
}
@Override
if (userName == null || userName.trim().isEmpty()) {
return new Status(StatusCode.BADREQUEST, "Invalid user name");
}
+
if (!localUserConfigList.containsKey(userName)) {
return new Status(StatusCode.NOTFOUND, "User does not exist");
}
+
return addRemoveLocalUser(localUserConfigList.get(userName), true);
}
return loggedInList;
}
- /*
- * Interaction with GUI END
- */
-
- /*
- * Cluster notifications
- */
-
- @Override
- public void entryCreated(Long key, String cacheName, boolean originLocal) {
- // don't react on this event
- }
-
- @Override
- public void entryUpdated(Long key, String new_value, String cacheName,
- boolean originLocal) {
- if (cacheName.equals("localUserSaveConfigEvent")) {
- this.saveLocalUserListInternal();
- } else if (cacheName.equals("remoteServerSaveConfigEvent")) {
- this.saveAAAServerListInternal();
- } else if (cacheName.equals("authorizationSaveConfigEvent")) {
- this.saveAuthorizationListInternal();
- }
- }
-
- @Override
- public void entryDeleted(Long key, String cacheName, boolean originLocal) {
- // don't react on this event
- }
-
public void _umAddUser(CommandInterpreter ci) {
String userName = ci.nextArgument();
String password = ci.nextArgument();
}
if (success) {
- return new Status(StatusCode.SUCCESS, null);
+ return new Status(StatusCode.SUCCESS);
}
- return new Status(StatusCode.INTERNALERROR,
- "Failed to save user configurations");
+ return new Status(StatusCode.INTERNALERROR, "Failed to save user configurations");
}
@Override
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
State portState = ((State) switchManager
.getNodeConnectorProp(nodeConnector,
State.StatePropName));
-
String nodeConnectorName = (ncName != null) ? ncName
.getValue() : "";
nodeConnectorName += " (" + nodeConnector.getID() + ")";
Map<String, String> subnet = new HashMap<String, String>();
subnet.put("name", conf.getName());
subnet.put("subnet", conf.getSubnet());
- subnet.put("json", gson.toJson(conf));
+ List<SubnetGatewayPortBean> portsList = new ArrayList<SubnetGatewayPortBean>();
+ Iterator<NodeConnector> itor = conf.getSubnetNodeConnectors().iterator();
+ while(itor.hasNext()) {
+ SubnetGatewayPortBean bean = new SubnetGatewayPortBean();
+ NodeConnector nodeConnector = itor.next();
+ String nodeName = getNodeDesc(nodeConnector.getNode().toString(), containerName);
+ Name ncName = ((Name) switchManager.getNodeConnectorProp(nodeConnector, Name.NamePropName));
+ String nodeConnectorName = (ncName != null) ? ncName.getValue() : "";
+ nodeConnectorName += " (" + nodeConnector.getID() + ")";
+ bean.setNodeName(nodeName);
+ bean.setNodePortName(nodeConnectorName);
+ bean.setNodeId(nodeConnector.getNode().toString());
+ bean.setNodePortId(nodeConnector.getID().toString());
+ portsList.add(bean);
+ }
+ subnet.put("nodePorts", gson.toJson(portsList));
subnets.add(subnet);
}
}
@RequestMapping(value = "/nodeports")
@ResponseBody
- public Map<String, Object> getNodePorts(HttpServletRequest request,
+ public List<NodeJsonBean> getNodePorts(HttpServletRequest request,
@RequestParam(required = false) String container) {
String containerName = (container == null) ? GlobalConstants.DEFAULT
.toString() : container;
return null;
}
-
ISwitchManager switchManager = (ISwitchManager) ServiceHelper
.getInstance(ISwitchManager.class, containerName, this);
if (switchManager == null) {
return null;
}
-
- Map<String, Object> nodes = new HashMap<String, Object>();
- Map<Short, String> port;
+ List<NodeJsonBean> nodeJsonBeans = new ArrayList<NodeJsonBean>();
for (Switch node : switchManager.getNetworkDevices()) {
- port = new HashMap<Short, String>(); // new port
+ NodeJsonBean nodeJsonBean = new NodeJsonBean();
+ List<String> port = new ArrayList<String>();
Set<NodeConnector> nodeConnectorSet = node.getNodeConnectors();
if (nodeConnectorSet != null) {
String nodeConnectorName = ((Name) switchManager
.getNodeConnectorProp(nodeConnector,
Name.NamePropName)).getValue();
- port.put((Short) nodeConnector.getID(), nodeConnectorName
+ port.add(nodeConnectorName
+ "(" + nodeConnector.getID() + ")");
}
}
-
- nodes.put(node.getNode().toString(), port);
+ nodeJsonBean.setNodeId(node.getNode().toString());
+ nodeJsonBean.setNodeName(getNodeDesc(node.getNode().toString(), containerName));
+ nodeJsonBean.setNodePorts(port);
+ nodeJsonBeans.add(nodeJsonBean);
}
- return nodes;
+ return nodeJsonBeans;
}
@RequestMapping(value = "/spanPorts/add", method = RequestMethod.GET)
--- /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.devices.web;
+
+import java.util.List;
+
+public class NodeJsonBean {
+ String nodeId;
+ String nodeName;
+ List<String> nodePorts;
+
+ public String getNodeId() {
+ return nodeId;
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public List<String> getNodePorts() {
+ return nodePorts;
+ }
+
+ public void setNodeId(String nodeId) {
+ this.nodeId = nodeId;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public void setNodePorts(List<String> port) {
+ this.nodePorts = port;
+ }
+
+}
--- /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.devices.web;
+
+public class SubnetGatewayPortBean {
+ private String nodeName;
+ private String nodeId;
+ private String nodePortId;
+ private String nodePortName;
+
+ public String getNodeName() {
+ return nodeName;
+ }
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+ public String getNodeId() {
+ return nodeId;
+ }
+ public void setNodeId(String nodeId) {
+ this.nodeId = nodeId;
+ }
+ public String getNodePortId() {
+ return nodePortId;
+ }
+ public void setNodePortId(String nodePortId) {
+ this.nodePortId = nodePortId;
+ }
+ public String getNodePortName() {
+ return nodePortName;
+ }
+ public void setNodePortName(String nodePortName) {
+ this.nodePortName = nodePortName;
+ }
+}
// specify dashlets and layouts
one.f.dashlet = {
- nodesLearnt : {
- id : 'nodesLearnt',
- name : 'Nodes Learnt'
- },
+ nodesLearnt : {
+ id : 'nodesLearnt',
+ name : 'Nodes Learnt'
+ },
staticRouteConfig : {
id : 'staticRouteConfig',
name : 'Static Route Configuration'
/**Devices Modules */
one.f.switchmanager = {
- rootUrl: "controller/web/devices",
- createTable: function(columnNames, body) {
- var tableAttributes = ["table-striped", "table-bordered", "table-condensed"];
- var $table = one.lib.dashlet.table.table(tableAttributes);
- var tableHeaders = columnNames;
- var $thead = one.lib.dashlet.table.header(tableHeaders);
- var $tbody = one.lib.dashlet.table.body(body, tableHeaders);
- $table.append($thead)
- .append($tbody);
- return $table;
- },
- validateName: function(name) {
+ rootUrl: "controller/web/devices",
+ createTable: function(columnNames, body) {
+ var tableAttributes = ["table-striped", "table-bordered", "table-condensed"];
+ var $table = one.lib.dashlet.table.table(tableAttributes);
+ var tableHeaders = columnNames;
+ var $thead = one.lib.dashlet.table.header(tableHeaders);
+ var $tbody = one.lib.dashlet.table.body(body, tableHeaders);
+ $table.append($thead)
+ .append($tbody);
+ return $table;
+ },
+ validateName: function(name) {
return (name.length < 256);
- }
+ }
};
one.f.switchmanager.nodesLearnt = {
- id: {
- dashlet: {
- popout: "one_f_switchmanager_nodesLearnt_id_dashlet_popout"
- },
- modal: {
- modal: "one_f_switchmanager_nodesLearnt_id_modal_modal",
- save: "one_f_switchmanager_nodesLearnt_id_modal_save",
- form: {
- nodeId: "one_f_switchmanager_nodesLearnt_id_modal_form_nodeid",
- nodeName : "one_f_switchmanager_nodesLearnt_id_modal_form_nodename",
- portStatus : "one_f_switchmanager_nodesLearnt_id_modal_form_portstatus",
- tier: "one_f_switchmanager_nodesLearnt_id_modal_form_tier",
- operationMode: "one_f_switchmanager_nodesLearnt_id_modal_form_opmode"
- }
- }
- },
- dashlet: function($dashlet) {
- var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
- one.lib.dashlet.empty($dashlet);
- $dashlet.append(one.lib.dashlet.header(one.f.dashlet.nodesLearnt.name));
-
- one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
- var body = one.f.switchmanager.nodesLearnt.data.abridged(content);
- var $table = one.f.switchmanager.createTable(["Node Name", "Node ID", "Ports"], body);
- $dashlet.append($table);
- });
- },
- ajax : {
- main : function(url, callback) {
- $.getJSON(url, function(data) {
- callback(data);
- });
- }
- },
- modal : {
- initialize: {
- updateNode: function(evt) {
- one.f.switchmanager.nodesLearnt.ajax.main(one.f.switchmanager.rootUrl + "/tiers", function(tiers) {
+ id: {
+ dashlet: {
+ popout: "one_f_switchmanager_nodesLearnt_id_dashlet_popout",
+ datagrid: "one_f_switchmanager_nodesLearnt_id_dashlet_datagrid"
+ },
+ modal: {
+ modal: "one_f_switchmanager_nodesLearnt_id_modal_modal",
+ configure: "one_f_switchmanager_nodesLearnt_id_modal_configure",
+ ports: "one_f_switchmanager_nodesLearnt_id_modal_ports",
+ save: "one_f_switchmanager_nodesLearnt_id_modal_save",
+ datagrid: "one_f_switchmanager_nodesLearnt_id_modal_datagrid",
+ portsDatagrid: "one_f_switchmanager_nodesLearnt_id_modal_portsDatagrid",
+ form: {
+ nodeId: "one_f_switchmanager_nodesLearnt_id_modal_form_nodeid",
+ nodeName : "one_f_switchmanager_nodesLearnt_id_modal_form_nodename",
+ portStatus : "one_f_switchmanager_nodesLearnt_id_modal_form_portstatus",
+ tier: "one_f_switchmanager_nodesLearnt_id_modal_form_tier",
+ operationMode: "one_f_switchmanager_nodesLearnt_id_modal_form_opmode"
+ }
+ }
+ },
+ dashlet: function($dashlet) {
+ var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
+ one.lib.dashlet.empty($dashlet);
+ $dashlet.append(one.lib.dashlet.header(one.f.dashlet.nodesLearnt.name));
+
+ one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.abridged(content);
+ $("#" + one.f.switchmanager.nodesLearnt.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+ $(this).find("tbody a").click(one.f.switchmanager.nodesLearnt.modal.initialize.updateNode);
+ });
+
+ $("#" + one.f.switchmanager.nodesLearnt.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+ $(this).find("tbody span").click(function(){
+ one.f.switchmanager.nodesLearnt.modal.initialize.displayPorts($(this));
+ });
+ });
+
+ });
+ },
+ ajax : {
+ main : function(url, callback) {
+ $.getJSON(url, function(data) {
+ callback(data);
+ });
+ }
+ },
+ modal : {
+ initialize: {
+ updateNode: function(evt) {
+ one.f.switchmanager.nodesLearnt.ajax.main(one.f.switchmanager.rootUrl + "/tiers", function(tiers) {
var nodeId = decodeURIComponent(evt.target.id);
var h3;
var footer = [];
- var $body = one.f.switchmanager.nodesLearnt.modal.body.updateNode(nodeId, evt.target.switchDetails, tiers);
- if (evt.target.privilege == 'WRITE'){
+ var $body = one.f.switchmanager.nodesLearnt.modal.body.updateNode(nodeId, JSON.parse(decodeURIComponent(evt.target.getAttribute("switchDetails"))), tiers);
+ if (evt.target.getAttribute("privilege") == 'WRITE'){
h3 = "Update Node Information";
footer = one.f.switchmanager.nodesLearnt.modal.footer.updateNode();
} else { //disable node edit
h3 = 'Node Information';
}
- var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.modal, h3, "", footer);
+ var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.configure, h3, "", footer);
// bind save button
$('#' + one.f.switchmanager.nodesLearnt.id.modal.save, $modal).click(function() {
one.f.switchmanager.nodesLearnt.modal.save($modal);
});
- // inject body (nodePorts)
- one.lib.modal.inject.body($modal, $body);
- $modal.modal();
- });
- },
- popout: function() {
- var h3 = "Nodes Learnt";
- var footer = one.f.switchmanager.nodesLearnt.modal.footer.popout();
- var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.modal, h3, "", footer);
- var $body = one.f.switchmanager.nodesLearnt.modal.body.popout($modal);
- return $modal;
- }
- },
- body: {
- updateNode: function(nodeId, switchDetails, tiers) {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // node ID. not editable.
- var $label = one.lib.form.label("Node ID");
- var $input = one.lib.form.input("node id");
- $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeId);
- $input.attr("disabled", true);
- $input.attr("value", nodeId);
- $fieldset.append($label).append($input);
- // node name
- var $label = one.lib.form.label("Node Name");
- var $input = one.lib.form.input("Node Name");
- $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeName);
- if(switchDetails["nodeName"] != null) {
- $input.attr('value', switchDetails["nodeName"]);
- }
- $fieldset.append($label).append($input);
- // node tier
- var $label = one.lib.form.label("Tier");
- var $select = one.lib.form.select.create(tiers);
- $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.tier);
- $select.val(switchDetails["tier"]);
- $fieldset.append($label).append($select);
- // operation mode
- var $label = one.lib.form.label("Operation Mode");
- var $select = one.lib.form.select.create(
- ["Allow reactive forwarding", "Proactive forwarding only"]);
- $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.operationMode);
- if ((one.main.registry != undefined) && (one.main.registry.container != 'default')) {
- $select.attr("disabled", true);
- }
- $select.val(switchDetails["mode"]);
- $fieldset.append($label).append($select);
- $form.append($fieldset);
- return $form;
- },
- popout: function($modal) {
- var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
- one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
- var tableContent = one.f.switchmanager.nodesLearnt.data.popout(content);
- var $table = one.f.switchmanager.createTable(content.columnNames, tableContent);
- one.lib.modal.inject.body($modal, $table);
- });
- }
- },
- save: function($modal) {
- var result = {};
+ // inject body (nodePorts)
+ one.lib.modal.inject.body($modal, $body);
+ $modal.modal();
+ });
+ },
+ popout: function() {
+ var h3 = "Nodes Learnt";
+ var footer = one.f.switchmanager.nodesLearnt.modal.footer.popout();
+ var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.modal, h3, "", footer);
+ var $body = one.f.switchmanager.nodesLearnt.modal.body.popout($modal);
+ return $modal;
+ },
+ displayPorts: function(ports) {
+ var content = JSON.parse(decodeURIComponent(ports.attr("ports")));
+
+ var h3 = ((ports.attr("nodeName") == "None")? ports.attr("nodeId") : ports.attr("nodeName"))
+ var footer = [];
+ var $modal = one.lib.modal.spawn(one.f.switchmanager.nodesLearnt.id.modal.ports, h3, "", footer);
+
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.modal.portsDatagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true,
+ popout: true
+ }, "table-striped table-condensed");
+ one.lib.modal.inject.body($modal, $gridHTML);
+ $modal.on("shown", function() {
+ var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.displayPorts(content);
+ $("#" + one.f.switchmanager.nodesLearnt.id.modal.portsDatagrid).datagrid({
+ dataSource: dataSource,
+ stretchHeight: false
+ });
+ });
+ $modal.modal();
+ }
+ },
+ body: {
+ updateNode: function(nodeId, switchDetails, tiers) {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ // node ID. not editable.
+ var $label = one.lib.form.label("Node ID");
+ var $input = one.lib.form.input("node id");
+ $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeId);
+ $input.attr("disabled", true);
+ $input.attr("value", nodeId);
+ $fieldset.append($label).append($input);
+ // node name
+ var $label = one.lib.form.label("Node Name");
+ var $input = one.lib.form.input("Node Name");
+ $input.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.nodeName);
+ if(switchDetails["nodeName"] != null) {
+ $input.attr('value', switchDetails["nodeName"]);
+ }
+ $fieldset.append($label).append($input);
+ // node tier
+ var $label = one.lib.form.label("Tier");
+ var $select = one.lib.form.select.create(tiers);
+ $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.tier);
+ $select.val(switchDetails["tier"]);
+ $fieldset.append($label).append($select);
+ // operation mode
+ var $label = one.lib.form.label("Operation Mode");
+ var $select = one.lib.form.select.create(
+ ["Allow reactive forwarding", "Proactive forwarding only"]);
+ $select.attr('id', one.f.switchmanager.nodesLearnt.id.modal.form.operationMode);
+ if ((one.main.registry != undefined) && (one.main.registry.container != 'default')) {
+ $select.attr("disabled", true);
+ }
+ $select.val(switchDetails["mode"]);
+ $fieldset.append($label).append($select);
+ $form.append($fieldset);
+ return $form;
+ },
+ popout: function($modal) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.nodesLearnt.id.modal.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true,
+ popout: true
+ }, "table-striped table-condensed");
+ one.lib.modal.inject.body($modal, $gridHTML);
+ // attach to shown event of modal
+ $modal.on("shown", function() {
+ var url = one.f.switchmanager.rootUrl + "/nodesLearnt";
+ one.f.switchmanager.nodesLearnt.ajax.main(url, function(content) {
+ var dataSource = one.f.switchmanager.nodesLearnt.data.gridDataSource.popout(content);
+ $("#" + one.f.switchmanager.nodesLearnt.id.modal.datagrid).datagrid({
+ dataSource: dataSource,
+ stretchHeight: false
+ })
+ .on("loaded", function() {
+ $("#" + one.f.switchmanager.nodesLearnt.id.modal.datagrid).find("tbody span").click(function(){
+ one.f.switchmanager.nodesLearnt.modal.initialize.displayPorts($(this));
+ });
+ });
+ });
+ });
+ }
+ },
+ save: function($modal) {
+ var result = {};
result['nodeName'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.nodeName, $modal).val();
if(!one.f.switchmanager.validateName(result['nodeName'])) {
- alert("Node name can contain upto 255 characters");
- return;
+ alert("Node name can contain upto 255 characters");
+ return;
}
result['nodeId'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.nodeId, $modal).val();
result['tier'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.tier, $modal).val();
result['operationMode'] = $('#' + one.f.switchmanager.nodesLearnt.id.modal.form.operationMode, $modal).val();
one.f.switchmanager.nodesLearnt.modal.ajax(result,
- function(response) {
- if(response.status == true) {
- $modal.modal('hide');
- one.topology.update(); // refresh visual topology with new name
- // TODO: Identify dashlet by inserting a nodesLearnt div
- // in the dashlet() instead
- one.f.switchmanager.nodesLearnt.dashlet($("#left-top .dashlet"));
- } else {
- alert(response.message);
- }
-
- });
- },
- ajax: function(requestData, callback) {
- $.getJSON(one.f.switchmanager.rootUrl + "/nodesLearnt/update", requestData, function(response) {
- callback(response);
- });
- },
- footer: {
- updateNode: function() {
- var footer = [];
+ function(response) {
+ if(response.status == true) {
+ $modal.modal('hide');
+ one.topology.update(); // refresh visual topology with new name
+ // TODO: Identify dashlet by inserting a nodesLearnt div
+ // in the dashlet() instead
+ one.f.switchmanager.nodesLearnt.dashlet($("#left-top .dashlet"));
+ } else {
+ alert(response.message);
+ }
+
+ });
+ },
+ ajax: function(requestData, callback) {
+ $.getJSON(one.f.switchmanager.rootUrl + "/nodesLearnt/update", requestData, function(response) {
+ callback(response);
+ });
+ },
+ footer: {
+ updateNode: function() {
+ var footer = [];
var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.nodesLearnt.id.modal.save, "btn-success", "");
var $saveButton = one.lib.dashlet.button.button(saveButton);
footer.push($saveButton);
- return footer;
- },
- popout: function() {
- // TODO: Maybe put a close button in the footer?
- return [];
- }
- }
- },
- // data functions
- data : {
- abridged : function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- var entry = [];
- var nodenameentry = value["nodeName"] ? value["nodeName"] : "Click to update";
-
- // TODO: Move anchor tag creation to one.lib.form.
- var aTag;
+ return footer;
+ },
+ popout: function() {
+ // TODO: Maybe put a close button in the footer?
+ return [];
+ }
+ }
+ },
+ // data functions
+ data : {
+ gridDataSource: {
+ abridged: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Node Name',
+ sortable: true
+ },
+ {
+ property: 'nodeId',
+ label: 'Node ID',
+ sortable: true
+ },
+ {
+ property: 'ports',
+ label: 'Ports',
+ sortable: true
+ }
+ ],
+ data: data.nodeData,
+ formatter: function(items) {
+ $.each(items, function (index, item) {
+ var nodeName = item.nodeName;
+ var nodeNameEntry = item.nodeName ? item.nodeName : "Click to update";
+ item.nodeName = '<a href="#" id="' + item.nodeId + '" switchDetails=' + encodeURIComponent(JSON.stringify(item)) +
+ ' privilege=' + data.privilege + '>' + nodeNameEntry + '</a>';
+
+ var ports = item.ports;
+ item.ports = '<span class="nodePorts" style="cursor:pointer;color: #08c" ports='+encodeURIComponent(JSON.stringify(item.ports)) + ' nodeId=' + item.nodeId
+ + ' nodeName=' + nodeName
+ + '>' + ports.match(/<\/span>/g).length+'</span>';
+ });
+ },
+ delay: 0
+ });
+ return source;
+
+ },
+ popout: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Node Name',
+ sortable: true
+ },
+ {
+ property: 'nodeId',
+ label: 'Node ID',
+ sortable: true
+ },
+ {
+ property: 'tierName',
+ label: 'Tier Name',
+ sortable: true
+ },
+ {
+ property: 'mac',
+ label: 'MAC',
+ sortable: true
+ },
+ {
+ property: 'ports',
+ label: 'Ports',
+ sortable: true
+ }
+ ],
+ data: data.nodeData,
+ formatter: function(items) {
+ $.each(items, function (index, item) {
+ var ports = item.ports;
+ item.ports = '<span class="nodePorts" style="cursor: pointer;color: #08c" ports='+encodeURIComponent(JSON.stringify(item.ports)) + ' nodeId=' + item.nodeId
+ + ' nodeName=' + item.nodeName
+ + '>' + ports.match(/<\/span>/g).length+'</span>';
+ });
+ },
+ delay: 0
+ });
+ return source;
+ },
+ displayPorts: function(content){
+ var data=[];
+ var start=0;;
+ var finish=content.indexOf("<br>",start);
+ while(finish != -1){
+ data.push({"ports":content.substring(start,finish)});
+ start=finish+4
+ finish=content.indexOf("<br>",start);
+ }
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'ports',
+ label: 'Ports',
+ sortable: true
+ }
+ ],
+ data:data,
+ delay: 0
+ });
+
+ return source;
+ }
+ },
+ abridged : function(data) {
+ var result = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ var entry = [];
+ var nodeNameEntry = value["nodeName"] ? value["nodeName"] : "Click to update";
+
+ // TODO: Move anchor tag creation to one.lib.form.
+ var aTag;
aTag = document.createElement("a");
aTag.privilege = data.privilege;
aTag.addEventListener("click", one.f.switchmanager.nodesLearnt.modal.initialize.updateNode);
}, false);
aTag.setAttribute("id", encodeURIComponent(value["nodeId"]));
aTag.switchDetails = value;
- aTag.innerHTML = nodenameentry;
- entry.push(aTag);
- entry.push(value["nodeId"]);
- entry.push(value["ports"]);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- },
- popout : function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- // fill up all the td's
- var entry = [];
- var nodenameentry = value["nodeName"] ? value["nodeName"] : "No name provided";
- entry.push(nodenameentry);
- entry.push(value["nodeId"]);
- entry.push(value["tierName"]);
- entry.push(value["mac"]);
- entry.push(value["ports"]);
- entry.push(value["portStatus"]);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- }
- }
+ aTag.innerHTML = nodeNameEntry;
+ entry.push(aTag);
+ entry.push(value["nodeId"]);
+ entry.push(value["ports"]);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ },
+ popout : function(data) {
+ var result = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ // fill up all the td's
+ var entry = [];
+ var nodenameentry = value["nodeName"] ? value["nodeName"] : "No name provided";
+ entry.push(nodenameentry);
+ entry.push(value["nodeId"]);
+ entry.push(value["tierName"]);
+ entry.push(value["mac"]);
+ entry.push(value["ports"]);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ }
+ }
};
one.f.switchmanager.subnetGatewayConfig = {
- id: {
- dashlet: {
- addIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addIP",
- addPorts: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addPorts",
- removeIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_removeIP"
- },
- modal: {
- modal: "one_f_switchmanager_subnetGatewayConfig_id_modal_modal",
- save: "one_f_switchmanager_subnetGatewayConfig_id_modal_save",
- form: {
- name : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayname",
- gatewayIPAddress : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayipaddress",
- nodeId: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_nodeid",
- ports: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_ports"
- }
- }
- },
- // device ajax calls
- dashlet: function($dashlet) {
- one.lib.dashlet.empty($dashlet);
- $dashlet.append(one.lib.dashlet.header(one.f.dashlet.subnetGatewayConfig.name));
- // Add gateway IP Address button
- var url = one.f.switchmanager.rootUrl + "/subnets";
- one.f.switchmanager.subnetGatewayConfig.ajax.main(url, {}, function(content) {
+ id: {
+ dashlet: {
+ addIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addIP",
+ addPorts: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_addPorts",
+ removeIPAddress: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_removeIP",
+ datagrid: "one_f_switchmanager_subnetGatewayConfig_id_dashlet_datagrid"
+ },
+ modal: {
+ modal: "one_f_switchmanager_subnetGatewayConfig_id_modal_modal",
+ save: "one_f_switchmanager_subnetGatewayConfig_id_modal_save",
+ form: {
+ name : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayname",
+ gatewayIPAddress : "one_f_switchmanager_subnetGatewayConfig_id_modal_form_gatewayipaddress",
+ nodeId: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_nodeid",
+ ports: "one_f_switchmanager_subnetGatewayConfig_id_modal_form_ports"
+ }
+ }
+ },
+ // device ajax calls
+ dashlet: function($dashlet) {
+ one.lib.dashlet.empty($dashlet);
+ $dashlet.append(one.lib.dashlet.header(one.f.dashlet.subnetGatewayConfig.name));
+ // Add gateway IP Address button
+ var url = one.f.switchmanager.rootUrl + "/subnets";
+ one.f.switchmanager.subnetGatewayConfig.ajax.main(url, {}, function(content) {
if (content.privilege === 'WRITE') {
var button = one.lib.dashlet.button.single("Add Gateway IP Address",
$button.click(function() {
var requestData = {};
var gatewaysToDelete = [];
- var checkedCheckBoxes = $("input:checked", $(this).closest(".dashlet").find("table"));
+ var checkedCheckBoxes = $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).find("tbody input:checked")
checkedCheckBoxes.each(function(index, value) {
gatewaysToDelete.push(checkedCheckBoxes[index].id);
});
});
$dashlet.append($button);
}
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.switchmanager.subnetGatewayConfig.data.devicesgrid(content);
+ $("#" + one.f.switchmanager.subnetGatewayConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource});
+ });
+ },
+ ajax : {
+ main : function(url, requestData, callback) {
+ $.getJSON(url, requestData, function(data) {
+ callback(data);
+ });
+ }
+ },
+ registry: {},
+ modal : {
+ initialize: {
+ gateway: function() {
+ var h3 = "Add Gateway IP Address";
+ var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
+ var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, "", footer);
+ // bind save button
+ $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
+ one.f.switchmanager.subnetGatewayConfig.modal.save.gateway($modal);
+ });
+ var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.gateway();
+ one.lib.modal.inject.body($modal, $body);
+ return $modal;
+ },
+ ports: function() {
+ var h3 = "Add Ports";
+ var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
+ var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, "", footer);
+ // bind save button
+ $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
+ one.f.switchmanager.subnetGatewayConfig.modal.save.ports($modal);
+ });
+
+ // TODO: Change to subnetGateway instead.
+ one.f.switchmanager.spanPortConfig.modal.ajax.nodes(function(nodes, nodeports) {
+ var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.ports(nodes, nodeports);
+ one.lib.modal.inject.body($modal, $body);
+ });
+ return $modal;
+ }
+ },
+ save: {
+ gateway: function($modal) {
+ var result = {};
+ result['gatewayName'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
+ if(!one.f.switchmanager.validateName(result['gatewayName'])) {
+ alert("Gateway name can contain upto 255 characters");
+ return;
+ }
+ result['gatewayIPAddress'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress, $modal).val();
+ one.f.switchmanager.subnetGatewayConfig.modal.ajax.gateway(result,
+ function(response) {
+ if(response.status == true) {
+ $modal.modal('hide');
+ one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+ } else {
+ alert(response.message);
+ }
+ });
+ },
+ ports: function($modal) {
+ var result = {};
+ var gatewayRegistryIndex = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
+ result['portsName'] = one.f.switchmanager.subnetGatewayConfig.registry.gateways[gatewayRegistryIndex];
+ result['nodeId'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId, $modal).val();
+ result['ports'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports, $modal).val();
+ if(!result['portsName'] || result['portsName'] == "") {
+ alert("No gateway chosen. Cannot add port");
+ return;
+ }
+ if(!result['nodeId'] || result['nodeId'] == "") {
+ alert("Please select a node.");
+ return;
+ }
+ if(!result['ports'] || result['ports'] == "") {
+ alert("Please choose a port.");
+ return;
+ }
+ one.f.switchmanager.subnetGatewayConfig.modal.ajax.ports(result,
+ function(response) {
+ if(response.status == true) {
+ $modal.modal('hide');
+ one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+ } else {
+ alert(response.message);
+ }
+ });
+ }
+ },
+ body: {
+ gateway: function() {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ // gateway name
+ var $label = one.lib.form.label("Name");
+ var $input = one.lib.form.input("Name");
+ $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
+ $fieldset.append($label).append($input);
+ // gateway IP Mask
+ var $label = one.lib.form.label("Gateway IP Address/Mask");
+ var $input = one.lib.form.input("Gateway IP Address/Mask");
+ $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress);
+ $fieldset.append($label).append($input);
+
+ $form.append($fieldset);
+ return $form;
+ },
+ ports: function(nodes, nodeports) {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ // gateways drop down
+ var $label = one.lib.form.label("Gateway Name");
+ var $select = one.lib.form.select.create(one.f.switchmanager.subnetGatewayConfig.registry.gateways);
+ $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
+ $select.val($select.find("option:first").val());
+ $fieldset.append($label).append($select);
- var body = one.f.switchmanager.subnetGatewayConfig.data.devices(content);
- // first column contains checkbox. no need for header
- content.columnNames.splice(0,0," ");
- var $table = one.f.switchmanager.createTable(content.columnNames, body);
- $dashlet.append($table);
- });
- },
- ajax : {
- main : function(url, requestData, callback) {
- $.getJSON(url, requestData, function(data) {
- callback(data);
- });
- }
- },
- registry: {},
- modal : {
- initialize: {
- gateway: function() {
- var h3 = "Add Gateway IP Address";
- var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
- var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, "", footer);
- // bind save button
- $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
- one.f.switchmanager.subnetGatewayConfig.modal.save.gateway($modal);
- });
- var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.gateway();
- one.lib.modal.inject.body($modal, $body);
- return $modal;
- },
- ports: function() {
- var h3 = "Add Ports";
- var footer = one.f.switchmanager.subnetGatewayConfig.modal.footer();
- var $modal = one.lib.modal.spawn(one.f.switchmanager.subnetGatewayConfig.id.modal.modal, h3, "", footer);
- // bind save button
- $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.save, $modal).click(function() {
- one.f.switchmanager.subnetGatewayConfig.modal.save.ports($modal);
- });
-
- // TODO: Change to subnetGateway instead.
- one.f.switchmanager.spanPortConfig.modal.ajax.nodes(function(nodes, nodeports) {
- var $body = one.f.switchmanager.subnetGatewayConfig.modal.body.ports(nodes, nodeports);
- one.lib.modal.inject.body($modal, $body);
- });
- return $modal;
- }
- },
- save: {
- gateway: function($modal) {
- var result = {};
- result['gatewayName'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
- if(!one.f.switchmanager.validateName(result['gatewayName'])) {
- alert("Gateway name can contain upto 255 characters");
- return;
- }
- result['gatewayIPAddress'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress, $modal).val();
- one.f.switchmanager.subnetGatewayConfig.modal.ajax.gateway(result,
- function(response) {
- if(response.status == true) {
- $modal.modal('hide');
- one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
- } else {
- alert(response.message);
- }
- });
- },
- ports: function($modal) {
- var result = {};
- var gatewayRegistryIndex = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.name, $modal).val();
- result['portsName'] = one.f.switchmanager.subnetGatewayConfig.registry.gateways[gatewayRegistryIndex];
- result['nodeId'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId, $modal).val();
- result['ports'] = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports, $modal).val();
- if(!result['portsName'] || result['portsName'] == "") {
- alert("No gateway chosen. Cannot add port");
- return;
- }
- if(!result['nodeId'] || result['nodeId'] == "") {
- alert("Please select a node.");
- return;
- }
- if(!result['ports'] || result['ports'] == "") {
- alert("Please choose a port.");
- return;
- }
- one.f.switchmanager.subnetGatewayConfig.modal.ajax.ports(result,
- function(response) {
- if(response.status == true) {
- $modal.modal('hide');
- one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
- } else {
- alert(response.message);
- }
- });
- }
- },
- body: {
- gateway: function() {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // gateway name
- var $label = one.lib.form.label("Name");
- var $input = one.lib.form.input("Name");
- $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
- $fieldset.append($label).append($input);
- // gateway IP Mask
- var $label = one.lib.form.label("Gateway IP Address/Mask");
- var $input = one.lib.form.input("Gateway IP Address/Mask");
- $input.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.gatewayIPAddress);
- $fieldset.append($label).append($input);
-
- $form.append($fieldset);
- return $form;
- },
- ports: function(nodes, nodeports) {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // gateways drop down
- var $label = one.lib.form.label("Gateway Name");
- var $select = one.lib.form.select.create(one.f.switchmanager.subnetGatewayConfig.registry.gateways);
- $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.name);
- $select.val($select.find("option:first").val());
- $fieldset.append($label).append($select);
-
- // node ID
- var $label = one.lib.form.label("Node ID");
- var $select = one.lib.form.select.create(nodes);
- $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId);
- one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
- $select.val($select.find("option:first").val());
- $fieldset.append($label).append($select);
-
- // bind onchange
- $select.change(function() {
- // retrieve port value
- var node = $(this).find('option:selected').attr('value');
- one.f.switchmanager.subnetGatewayConfig.registry['currentNode'] = node;
- var $ports = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
- var ports = nodeports[node];
- one.lib.form.select.inject($ports, ports);
- one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
- $ports.val($ports.find("option:first").val());
- });
-
- // ports
- var $label = one.lib.form.label("Select Port");
- var $select = one.lib.form.select.create();
- $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
- $fieldset.append($label).append($select);
-
- $form.append($fieldset);
- return $form;
- }
- },
- ajax: {
- gateway: function(requestData, callback) {
- $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/add", requestData, function(data) {
- callback(data);
- });
- },
- ports: function(requestData, callback) {
- $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/ports/add", requestData, function(data) {
- callback(data);
- });
- }
- },
- footer : function() {
+ // node ID
+ var $label = one.lib.form.label("Node ID");
+ var $select = one.lib.form.select.create(nodes);
+ $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.nodeId);
+ one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
+ $select.val($select.find("option:first").val());
+ $fieldset.append($label).append($select);
+
+ // bind onchange
+ $select.change(function() {
+ // retrieve port value
+ var node = $(this).find('option:selected').attr('value');
+ one.f.switchmanager.subnetGatewayConfig.registry['currentNode'] = node;
+ var $ports = $('#' + one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
+ var ports = nodeports[node];
+ one.lib.form.select.inject($ports, ports);
+ one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
+ $ports.val($ports.find("option:first").val());
+ });
+
+ // ports
+ var $label = one.lib.form.label("Select Port");
+ var $select = one.lib.form.select.create();
+ $select.attr('id', one.f.switchmanager.subnetGatewayConfig.id.modal.form.ports);
+ $fieldset.append($label).append($select);
+
+ $form.append($fieldset);
+ return $form;
+ }
+ },
+ ajax: {
+ gateway: function(requestData, callback) {
+ $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/add", requestData, function(data) {
+ callback(data);
+ });
+ },
+ ports: function(requestData, callback) {
+ $.getJSON(one.f.switchmanager.rootUrl + "/subnetGateway/ports/add", requestData, function(data) {
+ callback(data);
+ });
+ }
+ },
+ footer : function() {
var footer = [];
var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.subnetGatewayConfig.id.modal.save, "btn-success", "");
var $saveButton = one.lib.dashlet.button.button(saveButton);
footer.push($saveButton);
return footer;
}
- },
- // data functions
- data : {
- devices : function(data) {
- var result = [];
- one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- // fill up all the td's
- var subnetConfigObject = $.parseJSON(value["json"]);
- var nodePorts = subnetConfigObject.nodePorts;
- var $nodePortsContainer = $(document.createElement("div"));
-
- for(var i = 0; i < nodePorts.length; i++) {
- var nodePort = nodePorts[i];
- $nodePortsContainer.append(nodePort + " ");
- // add delete anchor tag to delete ports
- var aTag = document.createElement("a");
- aTag.setAttribute("id", encodeURIComponent(nodePort));
- aTag.gatewayName = value["name"];
- aTag.addEventListener("click", function(evt) {
- var htmlPortAnchor = evt.target;
- var requestData = {};
- requestData["gatewayName"] = evt.target.gatewayName;
- requestData["nodePort"] = decodeURIComponent(evt.target.id);
- // make ajax call to delete port
- var url = one.f.switchmanager.rootUrl + "/subnetGateway/ports/delete";
- one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
- if(response.status == true) {
- // refresh dashlet by passing dashlet div as param
- one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
- } else {
- alert(response.message);
- }
- });
-
- });
- aTag.addEventListener("mouseover", function(evt) {
- evt.target.style.cursor = "pointer";
- }, false);
- aTag.innerHTML = "Delete";
- $nodePortsContainer.append(aTag);
- $nodePortsContainer.append("<br/>");
- }
-
- // store gateways in the registry so that they can be used in the add ports popup
- one.f.switchmanager.subnetGatewayConfig.registry.gateways.push(value["name"]);
- var entry = [];
- var checkbox = document.createElement("input");
- checkbox.setAttribute("type", "checkbox");
- checkbox.setAttribute("id", value["name"]);
- entry.push(checkbox);
- entry.push(value["name"]);
- entry.push(value["subnet"]);
- entry.push($nodePortsContainer);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- }
- }
+ },
+ // data functions
+ data : {
+ devicesgrid: function(data) {
+ one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'selector',
+ label: ' ',
+ sortable: false
+ },
+ {
+ property: 'name',
+ label: 'Name',
+ sortable: true
+ },
+ {
+ property: 'subnet',
+ label: 'Gateway IP Address/Mask',
+ sortable: true
+ },
+ {
+ property: 'nodePorts',
+ label: 'Node/Ports',
+ sortable: false
+ }
+ ],
+ data: data.nodeData,
+ formatter: function(items) {
+ $.each(items, function(index, tableRow) {
+ tableRow["selector"] = '<input type="checkbox" id=' + tableRow["name"] + '></input>';
+ var json = tableRow["nodePorts"];
+ var nodePorts = JSON.parse(json);
+ var nodePortHtml = "<div>";
+ $.each(nodePorts, function(index, nodePort) {
+ var nodePortID = nodePort["nodeId"] + "/" + nodePort["nodePortId"];
+ nodePortHtml += nodePort["nodeName"] + " / " + nodePort["nodePortName"];
+ nodePortHtml += " ";
+ nodePortHtml += '<a href="#" id=' + encodeURIComponent(nodePortID) +
+ ' gatewayName=' + tableRow["name"] +
+ ' onclick="javascript:one.f.switchmanager.subnetGatewayConfig.actions.deleteNodePort(this);">Delete</a>';
+ nodePortHtml += "<br/>";
+ });
+ nodePortHtml += "</div>";
+ tableRow["nodePorts"] = nodePortHtml;
+ });
+
+ },
+ delay: 0
+ });
+ // populate the registry with subnet names
+ one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
+ $.each(data.nodeData, function(key, value) {
+ one.f.switchmanager.subnetGatewayConfig.registry.gateways.push(value["name"]);
+ });
+ return source;
+ },
+ devices : function(data) {
+ var result = [];
+ one.f.switchmanager.subnetGatewayConfig.registry.gateways = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ // fill up all the td's
+ var subnetConfigObject = $.parseJSON(value["json"]);
+ var nodePorts = subnetConfigObject.nodePorts;
+ var $nodePortsContainer = $(document.createElement("div"));
+
+ for(var i = 0; i < nodePorts.length; i++) {
+ var nodePort = nodePorts[i];
+ $nodePortsContainer.append(nodePort + " ");
+ // add delete anchor tag to delete ports
+ var aTag = document.createElement("a");
+ aTag.setAttribute("id", encodeURIComponent(nodePort));
+ aTag.gatewayName = value["name"];
+ aTag.addEventListener("click", function(evt) {
+ var htmlPortAnchor = evt.target;
+ var requestData = {};
+ requestData["gatewayName"] = evt.target.gatewayName;
+ requestData["nodePort"] = decodeURIComponent(evt.target.id);
+ // make ajax call to delete port
+ var url = one.f.switchmanager.rootUrl + "/subnetGateway/ports/delete";
+ one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
+ if(response.status == true) {
+ // refresh dashlet by passing dashlet div as param
+ one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+ } else {
+ alert(response.message);
+ }
+ });
+
+ });
+ aTag.addEventListener("mouseover", function(evt) {
+ evt.target.style.cursor = "pointer";
+ }, false);
+ aTag.innerHTML = "Delete";
+ $nodePortsContainer.append(aTag);
+ $nodePortsContainer.append("<br/>");
+ }
+
+ // store gateways in the registry so that they can be used in the add ports popup
+ one.f.switchmanager.subnetGatewayConfig.registry.gateways.push(value["name"]);
+ var entry = [];
+ var checkbox = document.createElement("input");
+ checkbox.setAttribute("type", "checkbox");
+ checkbox.setAttribute("id", value["name"]);
+ entry.push(checkbox);
+ entry.push(value["name"]);
+ entry.push(value["subnet"]);
+ entry.push($nodePortsContainer);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ }
+ },
+ actions: {
+ deleteNodePort: function(htmlPortAnchor) {
+ var requestData = {};
+ requestData["gatewayName"] = htmlPortAnchor.getAttribute("gatewayName");
+ requestData["nodePort"] = decodeURIComponent(htmlPortAnchor.id);
+ // make ajax call to delete port
+ var url = one.f.switchmanager.rootUrl + "/subnetGateway/ports/delete";
+ one.f.switchmanager.subnetGatewayConfig.ajax.main(url, requestData, function(response) {
+ if(response.status == true) {
+ // refresh dashlet by passing dashlet div as param
+ one.f.switchmanager.subnetGatewayConfig.dashlet($("#right-bottom .dashlet"));
+ } else {
+ alert(response.message);
+ }
+ });
+ }
+ }
}
one.f.switchmanager.staticRouteConfig = {
- id: {
- dashlet: {
- add: "one_f_switchmanager_staticRouteConfig_id_dashlet_add",
- remove: "one_f_switchmanager_staticRouteConfig_id_dashlet_remove"
- },
- modal: {
- modal: "one_f_switchmanager_staticRouteConfig_id_modal_modal",
- save: "one_f_switchmanager_staticRouteConfig_id_modal_save",
- form: {
- routeName : "one_f_switchmanager_staticRouteConfig_id_modal_form_routename",
- staticRoute : "one_f_switchmanager_staticRouteConfig_id_modal_form_staticroute",
+ id: {
+ dashlet: {
+ add: "one_f_switchmanager_staticRouteConfig_id_dashlet_add",
+ remove: "one_f_switchmanager_staticRouteConfig_id_dashlet_remove",
+ datagrid: "one_f_switchmanager_staticRouteConfig_id_dashlet_datagrid"
+ },
+ modal: {
+ modal: "one_f_switchmanager_staticRouteConfig_id_modal_modal",
+ save: "one_f_switchmanager_staticRouteConfig_id_modal_save",
+ form: {
+ routeName : "one_f_switchmanager_staticRouteConfig_id_modal_form_routename",
+ staticRoute : "one_f_switchmanager_staticRouteConfig_id_modal_form_staticroute",
nextHop : "one_f_switchmanager_staticRouteConfig_id_modal_form_nexthop",
- }
- }
- },
- dashlet: function($dashlet) {
- one.lib.dashlet.empty($dashlet);
-
-
- var url = one.f.switchmanager.rootUrl + "/staticRoutes";
- one.f.switchmanager.staticRouteConfig.ajax.main(url, {}, function(content) {
+ }
+ }
+ },
+ dashlet: function($dashlet) {
+ one.lib.dashlet.empty($dashlet);
+ var url = one.f.switchmanager.rootUrl + "/staticRoutes";
+ one.f.switchmanager.staticRouteConfig.ajax.main(url, {}, function(content) {
if (content.privilege === 'WRITE') {
// Add static route button
$button.click(function() {
var requestData = {};
var routesToDelete = [];
- var checkedCheckBoxes = $("input:checked", $(this).closest(".dashlet").find("table"));
+ //var checkedCheckBoxes = $("input:checked", $(this).closest(".dashlet").find("table"));
+ var checkedCheckBoxes = $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).find("tbody input:checked");
checkedCheckBoxes.each(function(index, value) {
routesToDelete.push(checkedCheckBoxes[index].id);
});
});
$dashlet.append($button);
}
-
- var body = one.f.switchmanager.staticRouteConfig.data.staticRouteConfig(content);
- // first column contains checkbox. no need for header
- content.columnNames.splice(0,0," ");
- var tableHeaders = ['', 'Name', 'Static Route', 'Next Hop Address'];
- var $table = one.f.switchmanager.createTable(tableHeaders, body);
- $dashlet.append($table);
- });
- },
- // device ajax calls
- ajax : {
- main : function(url, requestData, callback) {
- $.getJSON(url, requestData, function(data) {
- callback(data);
- });
- }
- },
- registry: {},
- modal : {
- initialize: function() {
- var h3 = "Add Static Route";
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.switchmanager.staticRouteConfig.data.staticRouteConfigGrid(content);
+ $("#" + one.f.switchmanager.staticRouteConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource});
+ });
+ },
+ // device ajax calls
+ ajax : {
+ main : function(url, requestData, callback) {
+ $.getJSON(url, requestData, function(data) {
+ callback(data);
+ });
+ }
+ },
+ registry: {},
+ modal : {
+ initialize: function() {
+ var h3 = "Add Static Route";
var footer = one.f.switchmanager.staticRouteConfig.modal.footer();
var $modal = one.lib.modal.spawn(one.f.switchmanager.staticRouteConfig.id.modal.modal, h3, "", footer);
// bind save button
$('#' + one.f.switchmanager.staticRouteConfig.id.modal.save, $modal).click(function() {
- one.f.switchmanager.staticRouteConfig.modal.save($modal);
+ one.f.switchmanager.staticRouteConfig.modal.save($modal);
});
var $body = one.f.switchmanager.staticRouteConfig.modal.body();
one.lib.modal.inject.body($modal, $body);
return $modal;
- },
- save: function($modal) {
- var result = {};
+ },
+ save: function($modal) {
+ var result = {};
result['routeName'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.routeName, $modal).val();
result['staticRoute'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.staticRoute, $modal).val();
result['nextHop'] = $('#' + one.f.switchmanager.staticRouteConfig.id.modal.form.nextHop, $modal).val();
- one.f.switchmanager.staticRouteConfig.modal.ajax.staticRouteConfig(result, function(response) {
- if(response.status == true) {
- $modal.modal('hide');
- // refresh dashlet by passing dashlet div as param
- one.f.switchmanager.staticRouteConfig.dashlet($("#left-bottom .dashlet"));
- } else {
- // TODO: Show error message in a error message label instead.
- alert(response.message);
- }
- });
- },
- body: function() {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // static route name
- var $label = one.lib.form.label("Name");
- var $input = one.lib.form.input("Name");
- $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.routeName);
- $fieldset.append($label).append($input);
- // static route IP Mask
- var $label = one.lib.form.label("Static Route");
- var $input = one.lib.form.input("Static Route");
- $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.staticRoute);
- $fieldset.append($label).append($input);
- // static route IP Mask
- var $label = one.lib.form.label("Next Hop");
- var $input = one.lib.form.input("Next Hop");
- $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.nextHop);
- $fieldset.append($label).append($input);
- // return
- $form.append($fieldset);
- return $form;
- },
- ajax: {
- staticRouteConfig: function(requestData, callback) {
- $.getJSON(one.f.switchmanager.rootUrl + "/staticRoute/add", requestData, function(data) {
- callback(data);
- });
- }
- },
- data : {
+ one.f.switchmanager.staticRouteConfig.modal.ajax.staticRouteConfig(result, function(response) {
+ if(response.status == true) {
+ $modal.modal('hide');
+ // refresh dashlet by passing dashlet div as param
+ one.f.switchmanager.staticRouteConfig.dashlet($("#left-bottom .dashlet"));
+ } else {
+ // TODO: Show error message in a error message label instead.
+ alert(response.message);
+ }
+ });
+ },
+ body: function() {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ // static route name
+ var $label = one.lib.form.label("Name");
+ var $input = one.lib.form.input("Name");
+ $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.routeName);
+ $fieldset.append($label).append($input);
+ // static route IP Mask
+ var $label = one.lib.form.label("Static Route");
+ var $input = one.lib.form.input("Static Route");
+ $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.staticRoute);
+ $fieldset.append($label).append($input);
+ // static route IP Mask
+ var $label = one.lib.form.label("Next Hop");
+ var $input = one.lib.form.input("Next Hop");
+ $input.attr('id', one.f.switchmanager.staticRouteConfig.id.modal.form.nextHop);
+ $fieldset.append($label).append($input);
+ // return
+ $form.append($fieldset);
+ return $form;
+ },
+ ajax: {
+ staticRouteConfig: function(requestData, callback) {
+ $.getJSON(one.f.switchmanager.rootUrl + "/staticRoute/add", requestData, function(data) {
+ callback(data);
+ });
+ }
+ },
+ data : {
},
- footer : function() {
+ footer : function() {
var footer = [];
var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.staticRouteConfig.id.modal.save, "btn-success", "");
var $saveButton = one.lib.dashlet.button.button(saveButton);
footer.push($saveButton);
return footer;
}
- },
- // data functions
- data : {
- staticRouteConfig : function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- // fill up all the td's
- var entry = [];
- var checkbox = document.createElement("input");
- checkbox.setAttribute("type", "checkbox");
- checkbox.setAttribute("id", value["name"]);
- entry.push(checkbox);
- entry.push(value["name"]);
- entry.push(value["staticRoute"]);
- entry.push(value["nextHop"]);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- }
- }
+ },
+ // data functions
+ data : {
+ staticRouteConfigGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'selector',
+ label: ' ',
+ sortable: false
+ },
+ {
+ property: 'name',
+ label: 'Name',
+ sortable: true
+ },
+ {
+ property: 'staticRoute',
+ label: 'Static Route',
+ sortable: true
+ },
+ {
+ property: 'nextHop',
+ label: 'Next Hop Address',
+ sortable: true
+ }
+ ],
+ data: data.nodeData,
+ formatter: function(items) {
+ $.each(items, function(index, item) {
+ item["selector"] = '<input type="checkbox" id=' + item["name"] + '></input>';
+ });
+
+ },
+ delay: 0
+ });
+ return source;
+ },
+ staticRouteConfig : function(data) {
+ var result = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ // fill up all the td's
+ var entry = [];
+ var checkbox = document.createElement("input");
+ checkbox.setAttribute("type", "checkbox");
+ checkbox.setAttribute("id", value["name"]);
+ entry.push(checkbox);
+ entry.push(value["name"]);
+ entry.push(value["staticRoute"]);
+ entry.push(value["nextHop"]);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ }
+ }
}
one.f.switchmanager.spanPortConfig = {
- id: {
- dashlet: {
- add: "one_f_switchmanager_spanPortConfig_id_dashlet_add",
- remove: "one_f_switchmanager_spanPortConfig_id_dashlet_remove"
- },
- modal: {
- modal: "one_f_switchmanager_spanPortConfig_id_modal_modal",
- save: "one_f_switchmanager_spanPortConfig_id_modal_save",
- form: {
- name : "one_f_switchmanager_spanPortConfig_id_modal_form_name",
- nodes : "one_f_switchmanager_spanPortConfig_id_modal_form_nodes",
+ id: {
+ dashlet: {
+ add: "one_f_switchmanager_spanPortConfig_id_dashlet_add",
+ remove: "one_f_switchmanager_spanPortConfig_id_dashlet_remove",
+ datagrid: "one_f_switchmanager_spanPortConfig_id_dashlet_datagrid"
+ },
+ modal: {
+ modal: "one_f_switchmanager_spanPortConfig_id_modal_modal",
+ save: "one_f_switchmanager_spanPortConfig_id_modal_save",
+ form: {
+ name : "one_f_switchmanager_spanPortConfig_id_modal_form_name",
+ nodes : "one_f_switchmanager_spanPortConfig_id_modal_form_nodes",
port : "one_f_switchmanager_spanPortConfig_id_modal_form_port",
- }
- }
- },
- dashlet: function($dashlet) {
- one.lib.dashlet.empty($dashlet);
+ }
+ }
+ },
+ dashlet: function($dashlet) {
+ one.lib.dashlet.empty($dashlet);
//populate table in dashlet
- var url = one.f.switchmanager.rootUrl + "/spanPorts";
- one.f.switchmanager.spanPortConfig.ajax.main(url, {}, function(content) {
+ var url = one.f.switchmanager.rootUrl + "/spanPorts";
+ one.f.switchmanager.spanPortConfig.ajax.main(url, {}, function(content) {
if (content.privilege === 'WRITE') {
// Add span port button
- var button = one.lib.dashlet.button.single("Add Span Port", one.f.switchmanager.spanPortConfig.id.dashlet.add, "btn-primary", "btn-mini");
+ var button = one.lib.dashlet.button.single("Add SPAN Port", one.f.switchmanager.spanPortConfig.id.dashlet.add, "btn-primary", "btn-mini");
var $button = one.lib.dashlet.button.button(button);
$button.click(function() {
var $button = one.lib.dashlet.button.button(button);
$button.click(function() {
- var checkedCheckBoxes = $("input:checked", $(this).closest(".dashlet").find("table"));
+ var checkedCheckBoxes = $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).find("tbody input:checked");
if (checkedCheckBoxes.length > 0) {
var spanPortsToDelete = "";
checkedCheckBoxes.each(function(index, value) {
- spanPortsToDelete += checkedCheckBoxes[index].spanPort + "###";
+ spanPortsToDelete += decodeURIComponent(checkedCheckBoxes[index].getAttribute("spanPort")) + "###";
});
var requestData = {};
});
$dashlet.append($button);
}
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.switchmanager.spanPortConfig.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.switchmanager.spanPortConfig.data.spanPortConfigGrid(content);
+ $("#" + one.f.switchmanager.spanPortConfig.id.dashlet.datagrid).datagrid({dataSource: dataSource});
+
+
- var body = one.f.switchmanager.spanPortConfig.data.devices(content);
- // first column contains the checkbox. no header required.
- content.columnNames.splice(0,0," ");
- var $table = one.f.switchmanager.createTable(content.columnNames, body);
- $dashlet.append($table);
- });
- },
- // device ajax calls
- ajax : {
- main : function(url, requestData, callback) {
- $.getJSON(url, requestData, function(data) {
- callback(data);
- });
- }
- },
- registry: {},
- modal : {
- initialize: function() {
- var h3 = "Add SPAN Port";
+ });
+ },
+ // device ajax calls
+ ajax : {
+ main : function(url, requestData, callback) {
+ $.getJSON(url, requestData, function(data) {
+ callback(data);
+ });
+ }
+ },
+ registry: {},
+ modal : {
+ initialize: function() {
+ var h3 = "Add SPAN Port";
var footer = one.f.switchmanager.spanPortConfig.modal.footer();
var $modal = one.lib.modal.spawn(one.f.switchmanager.spanPortConfig.id.modal.modal, h3, "", footer);
// bind save button
$('#' + one.f.switchmanager.spanPortConfig.id.modal.save, $modal).click(function() {
- one.f.switchmanager.spanPortConfig.modal.save($modal);
+ one.f.switchmanager.spanPortConfig.modal.save($modal);
});
one.f.switchmanager.spanPortConfig.modal.ajax.nodes(function(nodes, nodeports) {
one.lib.modal.inject.body($modal, $body);
});
return $modal;
- },
- save: function($modal) {
- var result = {};
+ },
+ save: function($modal) {
+ var result = {};
result['nodeId'] = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.nodes, $modal).val();
result['spanPort'] = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.port, $modal).val();
- one.f.switchmanager.spanPortConfig.modal.ajax.saveSpanPortConfig(result,
- function(response) {
- if(response.status == true) {
- $modal.modal('hide');
- one.f.switchmanager.spanPortConfig.dashlet($("#right-bottom .dashlet"));
- } else {
- alert(response.message);
- }
-
- });
- },
- body: function(nodes, nodeports) {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // 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.attr('id', one.f.switchmanager.spanPortConfig.id.modal.form.nodes);
-
- // bind onchange
- $select.change(function() {
- // retrieve port value
- var node = $(this).find('option:selected').attr('value');
- one.f.switchmanager.spanPortConfig.registry['currentNode'] = node;
- var $ports = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.port);
- var ports = nodeports[node];
- one.lib.form.select.inject($ports, ports);
- });
+ one.f.switchmanager.spanPortConfig.modal.ajax.saveSpanPortConfig(result,
+ function(response) {
+ if(response.status == true) {
+ $modal.modal('hide');
+ one.f.switchmanager.spanPortConfig.dashlet($("#right-bottom .dashlet"));
+ } else {
+ alert(response.message);
+ }
+
+ });
+ },
+ body: function(nodes, nodeports) {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ // 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.attr('id', one.f.switchmanager.spanPortConfig.id.modal.form.nodes);
+
+ // bind onchange
+ $select.change(function() {
+ // retrieve port value
+ var nodeId = $(this).find('option:selected').attr('value');
+ one.f.switchmanager.spanPortConfig.registry['currentNode'] = nodeId;
+ var $ports = $('#' + one.f.switchmanager.spanPortConfig.id.modal.form.port);
+ var ports = one.f.switchmanager.spanPortConfig.registry['nodePorts'][nodeId]
+ one.lib.form.select.inject($ports, ports);
+ });
$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.switchmanager.spanPortConfig.id.modal.form.port);
- $fieldset.append($label).append($select);
-
- // return
- $form.append($fieldset);
- return $form;
- },
- ajax: {
- nodes: function(callback) {
- $.getJSON(one.f.switchmanager.rootUrl + "/nodeports", function(data) {
- var nodes = one.f.switchmanager.spanPortConfig.modal.data.nodes(data);
- var nodeports = data;
- one.f.switchmanager.spanPortConfig.registry['nodeports'] = nodeports;
- callback(nodes, nodeports);
+ // input port
+ var $label = one.lib.form.label("Input Port");
+ var $select = one.lib.form.select.create();
+ $select.attr('id', one.f.switchmanager.spanPortConfig.id.modal.form.port);
+ $fieldset.append($label).append($select);
+
+ // return
+ $form.append($fieldset);
+ return $form;
+ },
+ ajax: {
+ nodes: function(callback) {
+ $.getJSON(one.f.switchmanager.rootUrl + "/nodeports", function(data) {
+ var nodes = {};
+ var nodePorts = {};
+ $(data).each(function(index, node) {
+ nodes[node.nodeId] = node.nodeName;
+ nodePorts[node.nodeId] = node.nodePorts;
+ });
+ one.f.switchmanager.spanPortConfig.registry['nodePorts'] = nodePorts;
+ callback(nodes, nodePorts);
});
- },
- saveSpanPortConfig: function(requestData, callback) {
- var resource = {};
- resource["jsonData"] = JSON.stringify(requestData);
- $.getJSON(one.f.switchmanager.rootUrl + "/spanPorts/add", resource, function(data) {
- callback(data);
- });
- }
- },
- data : {
- nodes : function(data) {
- result = {};
- $.each(data, function(key, value) {
- result[key] = key;
+ },
+ saveSpanPortConfig: function(requestData, callback) {
+ var resource = {};
+ resource["jsonData"] = JSON.stringify(requestData);
+ $.getJSON(one.f.switchmanager.rootUrl + "/spanPorts/add", resource, function(data) {
+ callback(data);
});
- return result;
}
},
- footer : function() {
+ footer : function() {
var footer = [];
var saveButton = one.lib.dashlet.button.single("Save", one.f.switchmanager.spanPortConfig.id.modal.save, "btn-success", "");
var $saveButton = one.lib.dashlet.button.button(saveButton);
footer.push($saveButton);
return footer;
}
- },
- // data functions
- data : {
- devices : function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- // fill up all the td's
- var entry = [];
- var checkbox = document.createElement("input");
- checkbox.setAttribute("type", "checkbox");
- checkbox.spanPort = value.json;
- entry.push(checkbox);
- entry.push(value["nodeName"]);
- entry.push(value["spanPort"]);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- }
- }
+ },
+ // data functions
+ data : {
+ spanPortConfigGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'selector',
+ label: ' ',
+ sortable: false
+ },
+ {
+ property: 'nodeName',
+ label: 'Node',
+ sortable: true
+ },
+ {
+ property: 'spanPort',
+ label: 'SPAN Port',
+ sortable: true
+ },
+ ],
+ data: data.nodeData,
+ formatter: function(items) {
+ $.each(items, function(index, item) {
+ item["selector"] = '<input type="checkbox" spanPort=' + encodeURIComponent(item["json"]) + '></input>';
+ });
+ },
+ delay: 0
+ });
+ return source;
+ },
+ devices : function(data) {
+ var result = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ // fill up all the td's
+ var entry = [];
+ var checkbox = document.createElement("input");
+ checkbox.setAttribute("type", "checkbox");
+ checkbox.spanPort = value.json;
+ entry.push(checkbox);
+ entry.push(value["nodeName"]);
+ entry.push(value["spanPort"]);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ }
+ }
}
/** INIT **/
});
one.f.addPopOut = function() {
- $img1 = $(document.createElement("img"));
- $img1.attr("src", "/img/Expand16T.png");
- $img1.attr("style", "float: right;");
- $img1.hover(function() {
- $img1.css("cursor", "pointer");
- });
- $img1.click(function() {
- var $modal = one.f.switchmanager.nodesLearnt.modal.initialize.popout();
- $modal.css('width', 'auto');
- $modal.css('margin-left', '-40%');
+ $img1 = $(document.createElement("img"));
+ $img1.attr("src", "/img/Expand16T.png");
+ $img1.attr("style", "float: right;");
+ $img1.hover(function() {
+ $img1.css("cursor", "pointer");
+ });
+ $img1.click(function() {
+ var $modal = one.f.switchmanager.nodesLearnt.modal.initialize.popout();
+ $modal.css({
+ 'margin-left': '-45%',
+ 'margin-top': '-3%',
+ 'width':$(document).width() * 0.8,
+ 'height':$(document).height() * 0.9
+ });
+ $(".modal-body", $modal).css({
+ "max-height": $(document).height() * 0.75,
+ });
$modal.modal();
- });
- $dash1 = $($("#left-top .nav")[0]);
- $dash1.append($img1);
+ });
+ $dash1 = $($("#left-top .nav")[0]);
+ $dash1.append($img1);
};
one.f.addPopOut();
var menu = one.f.dashlet;
switch (id) {
case menu.nodesLearnt.id:
- one.f.switchmanager.nodesLearnt.dashlet($dashlet);
+ one.f.switchmanager.nodesLearnt.dashlet($dashlet);
break;
case menu.staticRouteConfig.id:
- one.f.switchmanager.staticRouteConfig.dashlet($dashlet);
+ one.f.switchmanager.staticRouteConfig.dashlet($dashlet);
break;
case menu.subnetGatewayConfig.id:
- one.f.switchmanager.subnetGatewayConfig.dashlet($dashlet);
+ one.f.switchmanager.subnetGatewayConfig.dashlet($dashlet);
break;
case menu.spanPortConfig.id:
- one.f.switchmanager.spanPortConfig.dashlet($dashlet);
+ one.f.switchmanager.spanPortConfig.dashlet($dashlet);
break;
};
});
package org.opendaylight.controller.flows.web;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@RequestMapping(value = "/main")
@ResponseBody
- public Map<String, Object> getFlows(HttpServletRequest request, @RequestParam(required = false) String container) {
- String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+ public Map<String, Object> getFlows(HttpServletRequest request,
+ @RequestParam(required = false) String container) {
+ String containerName = (container == null) ? GlobalConstants.DEFAULT
+ .toString() : container;
// Derive the privilege this user has on the current container
String userName = request.getUserPrincipal().getName();
- Privilege privilege = DaylightWebUtil.getContainerPrivilege(userName, containerName, this);
- if (privilege == Privilege.NONE) {
+ Privilege privilege = DaylightWebUtil.getContainerPrivilege(userName,
+ containerName, this);
+ if (privilege == Privilege.NONE) {
return null;
}
flowSet.add(entry);
}
- Map <String, Object> output = new HashMap<String, Object>(2);
+ Map<String, Object> output = new HashMap<String, Object>(2);
output.put("flows", flowSet);
output.put("privilege", privilege);
return output;
@RequestMapping(value = "/node-ports")
@ResponseBody
- public Map<String, Object> getNodePorts(HttpServletRequest request, @RequestParam(required = false) String container) {
- String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+ public Map<String, Object> getNodePorts(HttpServletRequest request,
+ @RequestParam(required = false) String container) {
+ String containerName = (container == null) ? GlobalConstants.DEFAULT
+ .toString() : container;
// Derive the privilege this user has on the current container
String userName = request.getUserPrincipal().getName();
- if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
+ if (DaylightWebUtil
+ .getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
return null;
}
@RequestMapping(value = "/node-flows")
@ResponseBody
- public Map<String, Object> getNodeFlows(HttpServletRequest request, @RequestParam(required = false) String container) {
- String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+ public Map<String, Object> getNodeFlows(HttpServletRequest request,
+ @RequestParam(required = false) String container) {
+ String containerName = (container == null) ? GlobalConstants.DEFAULT
+ .toString() : container;
// Derive the privilege this user has on the current container
String userName = request.getUserPrincipal().getName();
- if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
+ if (DaylightWebUtil
+ .getContainerPrivilege(userName, containerName, this) == Privilege.NONE) {
return null;
}
List<FlowConfig> flows = frm.getStaticFlows(node);
String nodeDesc = node.toString();
- SwitchConfig config = switchManager.getSwitchConfig(node
- .toString());
- if ((config != null) && (config.getProperty(Description.propertyName) != null)) {
- nodeDesc = ((Description) config.getProperty(Description.propertyName)).getValue();
+ SwitchConfig config = switchManager
+ .getSwitchConfig(node.toString());
+ if ((config != null)
+ && (config.getProperty(Description.propertyName) != null)) {
+ nodeDesc = ((Description) config
+ .getProperty(Description.propertyName)).getValue();
}
nodes.put(nodeDesc, flows.size());
public String actionFlow(@RequestParam(required = true) String action,
@RequestParam(required = false) String body,
@RequestParam(required = true) String nodeId,
- HttpServletRequest request, @RequestParam(required = false) String container) {
- String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+ HttpServletRequest request,
+ @RequestParam(required = false) String container) {
+ String containerName = (container == null) ? GlobalConstants.DEFAULT
+ .toString() : container;
// Authorization check
String userName = request.getUserPrincipal().getName();
- if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+ if (DaylightWebUtil
+ .getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
return "Operation not authorized";
}
Status result = new Status(StatusCode.BADREQUEST, "Invalid request");
if (action.equals("add")) {
result = frm.addStaticFlow(flow);
- DaylightWebUtil.auditlog("Flow", userName, "added", flow.getName(), containerName);
+ DaylightWebUtil.auditlog("Flow", userName, "added", flow.getName(),
+ containerName);
}
return (result.isSuccess()) ? StatusCode.SUCCESS.toString() : result
public String removeFlow(@PathVariable("nodeId") String nodeId,
@PathVariable("name") String name,
@RequestParam(required = true) String action,
- HttpServletRequest request, @RequestParam(required = false) String container) {
- String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+ HttpServletRequest request,
+ @RequestParam(required = false) String container) {
+ String containerName = (container == null) ? GlobalConstants.DEFAULT
+ .toString() : container;
// Authorization check
String userName = request.getUserPrincipal().getName();
- if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+ if (DaylightWebUtil
+ .getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
return "Operation not authorized";
}
}
if (action.equals("remove")) {
result = frm.removeStaticFlow(name, node);
- if(result.isSuccess()) {
- DaylightWebUtil.auditlog("Flow", userName, "removed", name, containerName);
+ if (result.isSuccess()) {
+ DaylightWebUtil.auditlog("Flow", userName, "removed", name,
+ containerName);
}
} else if (action.equals("toggle")) {
result = frm.toggleStaticFlowStatus(name, node);
- if(result.isSuccess()) {
- DaylightWebUtil.auditlog("Flow", userName, "toggled", name, containerName);
+ if (result.isSuccess()) {
+ DaylightWebUtil.auditlog("Flow", userName, "toggled", name,
+ containerName);
}
} else {
result = new Status(StatusCode.BADREQUEST, "Unknown action");
.getDescription();
}
+ @SuppressWarnings("unchecked")
+ @RequestMapping(value = "/flow/deleteFlows", method = RequestMethod.POST)
+ @ResponseBody
+ public String removeSelectedFlows(
+ @RequestParam(required = false) String body,
+ HttpServletRequest request,
+ @RequestParam(required = false) String container) {
+ 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";
+ }
+
+ IForwardingRulesManager frm = (IForwardingRulesManager) ServiceHelper
+ .getInstance(IForwardingRulesManager.class, containerName, this);
+ if (frm == null) {
+ return "Forwarding Rules Manager is not available";
+ }
+
+ Gson gson = new Gson();
+ List<Map<String, String>> flowList = new ArrayList<Map<String, String>>();
+ flowList = gson.fromJson(body, flowList.getClass());
+ Status result = new Status(StatusCode.BADREQUEST, "Invalid request");
+ String status = "";
+ for (Map<String, String> flowEntry : flowList) {
+ Node node = Node.fromString(flowEntry.get("node"));
+ result = frm.removeStaticFlow(flowEntry.get("name"), node);
+ if (result.isSuccess()) {
+ DaylightWebUtil.auditlog("Flow", userName, "removed",
+ flowEntry.get("name"), containerName);
+ } else {
+ status = flowEntry.get("name") + ", " + status;
+ }
+ }
+ if (!status.equals("")) {
+ return "Could not remove "
+ + status.substring(0, status.length() - 2) + " Flow(s)";
+ } else {
+ return "Success";
+ }
+ }
+
private String getNodeDesc(Node node, ISwitchManager switchManager) {
- Description desc = (Description) switchManager.getNodeProp(node, Description.propertyName);
+ Description desc = (Description) switchManager.getNodeProp(node,
+ Description.propertyName);
String description = (desc == null) ? "" : desc.getValue();
- return (description.isEmpty() || description.equalsIgnoreCase("none")) ? node.toString() : description;
+ return (description.isEmpty() || description.equalsIgnoreCase("none")) ? node
+ .toString() : description;
}
}
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
root : "/controller/web/flows",
flows : {
main : "/main",
- flows : "/node-flows",
+ flows : "/node-flows",
nodes : "/node-ports",
- flow : "/flow"
+ flow : "/flow",
+ deleteFlows:"/flow/deleteFlows"
}
}
/** NODES **/
one.f.nodes = {
- id : {},
+ id : {
+ dashlet: {
+ datagrid: "one_f_nodes_id_dashlet_datagrid"
+ }
+ },
registry : {},
dashlet : function($dashlet) {
var $h4 = one.lib.dashlet.header("Nodes");
$dashlet.append($h4);
- // load body
- one.f.nodes.ajax.dashlet(function($table) {
- // total nodes count
- var nodeCount = $table.find('tbody').find('tr').size();
- // prompt output
- var nodeText = "node";
- var verb = "is";
- if (nodeCount != 1) {
- nodeText += "s";
- verb = "are";
- }
- var out = "There "+verb+" "+nodeCount+" "+nodeText;
- $p = $(document.createElement('p'));
- $p.append(out);
- $dashlet.append($p);
- // add to dashlet
- $dashlet.append($table);
+ 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) {
- var body = one.f.nodes.data.dashlet(data);
- var $body = one.f.nodes.body.dashlet(body, callback);
- callback($body);
+ callback(data);
});
}
},
data : {
- dashlet : function(data) {
- var body = [];
- $.each(data, function(key, value) {
- var tr = {};
- var entry = [];
- entry.push(key);
- // parse ports
- entry.push(value);
- // add entry to tr
- tr['entry'] = entry;
- body.push(tr);
+ 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);
});
- return body;
+
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Node',
+ sortable: true
+ },
+ {
+ property: 'flows',
+ label: 'Flows',
+ sortable: true
+ }
+ ],
+ data: gridData,
+ delay: 0
+ });
+ return source;
}
},
body : {
// details
if (details == undefined) {
- var $none = $(document.createElement('div'));
- $none.addClass('none');
+ 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');
$dashlet.append($none)
- .append($p);
+ .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;
- },
+ 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 = [];
+ 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;
+ 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) {
- actions += value + ', ';
- });
- actions = actions.slice(0,-2);
- entry.push(actions);
-
- 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) {
+ 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);
+ // 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);
+ 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);
+ },
+ 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;
- }
+ }
}
}
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"
+ toggle : "one_f_flows_id_dashlet_toggle",
+ datagrid : "one_f_flows_id_dashlet_datagrid",
+ selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
},
modal : {
- install : "one_f_flows_id_modal_install",
+ install : "one_f_flows_id_modal_install",
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",
+ modal : "one_f_flows_id_modal_dialog_modal",
remove : "one_f_flows_id_modal_dialog_remove",
close : "one_f_flows_id_modal_dialog_close"
},
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"
- }
+ 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",
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",
+ 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",
dashlet : function($dashlet, callback) {
// load body
- one.f.flows.ajax.dashlet(function($table) {
+ one.f.flows.ajax.dashlet(function(data) {
var $h4 = one.lib.dashlet.header("Flow Entries");
$modal.modal();
});
$dashlet.append($button);
+ var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-primary", "btn-mini");
+ var $button = one.lib.dashlet.button.button(button);
+
+ $button.click(function() {
+ var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
+
+ var requestData = [];
+
+ var resource = {};
+ checkedCheckBoxes.each(function(index, value) {
+ var flowEntry = {};
+ flowEntry['name'] = checkedCheckBoxes[index].name;
+ flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
+ requestData.push(flowEntry);
+ });
+ resource['body'] = JSON.stringify(requestData);
+
+ $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
+ if(response == "Success") {
+ one.lib.alert("Flow(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);
+ });
+ });
+ $dashlet.append($button);
}
- // table bindings
- $table.find('tbody').find('tr').click(function() {
- var id = $($(this).find('td')[0]).text();
- var node = $(this).data('id');
- one.f.flows.detail(id, node);
+ 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(){
+ 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);
+ }
+ event.stopPropagation();
+ });
+ });
});
-
- // total flows
- var flowCount = $table.find('tbody').find('tr').size();
- // prompt output
- var flowText = "flow";
- var verb = "is";
- if (flowCount != 1) {
- flowText += "s";
- verb = "are";
- }
- var out = "There "+verb+" "+flowCount+" "+flowText;
- $p = $(document.createElement('p'));
- $p.append(out);
- $dashlet.append($p);
- // add table to dashlet
- $dashlet.append($table);
+
// details callback
if(callback != undefined) callback();
});
}
});
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();
- });
- // 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($toggle);
+ // 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();
+ });
+ // 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($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);
+ 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 : {
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['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['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
- result['installInHw'] = install;
+ result['installInHw'] = install;
var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
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'));
- }
+ 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
+ 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);
resource['action'] = 'add';
- resource['nodeId'] = nodeId;
+ resource['nodeId'] = nodeId;
one.f.flows.modal.ajax.saveflow(resource, function(data) {
if (data == "Success") {
one.main.dashlet.left.top.empty();
one.f.flows.dashlet(one.main.dashlet.left.top);
} else {
- alert('Could not add flow: '+data);
+ alert('Could not add flow: '+data);
}
});
},
});
},
removeflow : function(id, node, callback) {
- resource = {};
- resource['action'] = 'remove';
+ 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);
- });
+ resource = {};
+ resource['action'] = 'toggle';
+ $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+ callback(data);
+ });
}
},
data : {
},
body : function(nodes, nodeports) {
var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- // flow description
- var $legend = one.lib.form.legend("Flow Description");
- $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);
- $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);
-
- // 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());
- });
+ var $fieldset = $(document.createElement('fieldset'));
+ // flow description
+ var $legend = one.lib.form.legend("Flow Description");
+ $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);
+ $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);
+
+ // 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());
+ });
$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);
- // 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);
- $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);
- // 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);
- // 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);
- // 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);
- // 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);
- // srcMac
- var $label = one.lib.form.label("Source MAC Address");
- var $input = one.lib.form.input("Source MAC Address");
- $input.attr('id', one.f.flows.id.modal.form.srcMac);
- var $help = one.lib.form.help("Example: 00:11:22:aa:bb:cc");
- $fieldset.append($label).append($input).append($help);
- // dstMac
- var $label = one.lib.form.label("Destination MAC Address");
- var $input = one.lib.form.input("Destination MAC Address");
- $input.attr('id', one.f.flows.id.modal.form.dstMac);
- var $help = one.lib.form.help("Example: 00:11:22:aa:bb:cc");
- $fieldset.append($label).append($input).append($help);
- // 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("Source IP Address");
- $input.attr('id', one.f.flows.id.modal.form.srcIp);
- var $help = one.lib.form.help("Example: 127.0.0.1");
- $fieldset.append($label).append($input).append($help);
- // dstIp
- var $label = one.lib.form.label("Destination IP Address");
- var $input = one.lib.form.input("Destination IP Address");
- $input.attr('id', one.f.flows.id.modal.form.dstIp);
- var $help = one.lib.form.help("Example: 127.0.0.1");
- $fieldset.append($label).append($input).append($help);
- // 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);
- // 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);
- // 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);
- // 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);
- // 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", "Type"];
- 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",
- "softwarePath" : "Software Path",
- "hardwarePath" : "Hardware Path",
- "controller" : "Controller",
- "addOutputPorts" : "Add Output Ports",
- "setVlanId" : "Set VLAN ID",
- "setVlanPriority" : "Set VLAN Priority",
- "stripVlanHeader" : "Strip VLAN Header",
- "modifyDatalayerSourceAddress" : "Modify Datalayer Source Address",
- "modifyDatalayerDestinationAddress" : "Modify Datalayer Destination Address",
- "modifyNetworkSourceAddress" : "Modify Network Source Address",
- "modifyNetworkDestinationAddress" :"Modify Network Destination Address",
- "modifyTosBits" : "Modify TOS Bits",
- "modifyTransportSourcePort" : "Modify Transport Source Port",
- "modifyTransportDestinationPort" : "Modify Transport Destination Port"
- };
+ // 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);
+ // 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);
+ $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);
+ // 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);
+ // 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);
+ // 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);
+ // 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);
+ // srcMac
+ var $label = one.lib.form.label("Source MAC Address");
+ var $input = one.lib.form.input("Source MAC Address");
+ $input.attr('id', one.f.flows.id.modal.form.srcMac);
+ var $help = one.lib.form.help("Example: 00:11:22:aa:bb:cc");
+ $fieldset.append($label).append($input).append($help);
+ // dstMac
+ var $label = one.lib.form.label("Destination MAC Address");
+ var $input = one.lib.form.input("Destination MAC Address");
+ $input.attr('id', one.f.flows.id.modal.form.dstMac);
+ var $help = one.lib.form.help("Example: 00:11:22:aa:bb:cc");
+ $fieldset.append($label).append($input).append($help);
+ // 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("Source IP Address");
+ $input.attr('id', one.f.flows.id.modal.form.srcIp);
+ var $help = one.lib.form.help("Example: 127.0.0.1");
+ $fieldset.append($label).append($input).append($help);
+ // dstIp
+ var $label = one.lib.form.label("Destination IP Address");
+ var $input = one.lib.form.input("Destination IP Address");
+ $input.attr('id', one.f.flows.id.modal.form.dstIp);
+ var $help = one.lib.form.help("Example: 127.0.0.1");
+ $fieldset.append($label).append($input).append($help);
+ // 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);
+ // 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);
+ // 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);
+ // 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);
+ // 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", "Type"];
+ 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",
+ "softwarePath" : "Software Path",
+ "hardwarePath" : "Hardware Path",
+ "controller" : "Controller",
+ "addOutputPorts" : "Add Output Ports",
+ "setVlanId" : "Set VLAN ID",
+ "setVlanPriority" : "Set VLAN Priority",
+ "stripVlanHeader" : "Strip VLAN Header",
+ "modifyDatalayerSourceAddress" : "Modify Datalayer Source Address",
+ "modifyDatalayerDestinationAddress" : "Modify Datalayer Destination Address",
+ "modifyNetworkSourceAddress" : "Modify Network Source Address",
+ "modifyNetworkDestinationAddress" :"Modify Network Destination Address",
+ "modifyTosBits" : "Modify TOS Bits",
+ "modifyTransportSourcePort" : "Modify Transport Source Port",
+ "modifyTransportDestinationPort" : "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;
+ $select[0].selectedIndex = 0;
});
- $fieldset.append($select).append($table);
+ $fieldset.append($select).append($table);
- // return
- $form.append($fieldset);
- return $form;
+ // return
+ $form.append($fieldset);
+ return $form;
},
action : {
parse : function(option) {
var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
$tr.attr('id', 'addOutputPorts');
$tr.data('action', 'OUTPUT='+pid);
- $tr.click(function() {
- one.f.flows.modal.action.add.modal.initialize(this);
- });
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
one.f.flows.modal.action.table.append($tr);
$modal.modal('hide');
},
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);
- });
+ $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 $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);
- });
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
one.f.flows.modal.action.table.append($tr);
$modal.modal('hide');
},
- 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;
- }
- }
+ 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, type) {
footer : function() {
var footer = [];
- 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);
+ 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);
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);
$.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;
- var body = one.f.flows.data.dashlet(data.flows);
- var $body = one.f.flows.body.dashlet(body, callback);
- callback($body);
+ 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.node);
+ $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'];
+ tr['type'] = ['success'];
else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
- tr['type'] = ['warning'];
+ tr['type'] = ['warning'];
else
- tr['type'] = ['warning'];
+ tr['type'] = ['warning'];
tr['entry'] = entry;
tr['id'] = value['nodeId'];
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;
}
}
<!-- Bootstrap CSS - 1 -->
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen">
+<link rel="stylesheet" type="text/css" href="/css/fuelux.min.css">
+<link rel="stylesheet" type="text/css" href="/css/opendaylight.css">
<!-- Core CSS - 2 -->
<link rel="stylesheet/less" type="text/css" href="/css/one.less">
<script src="/js/jquery-1.9.1.min.js"></script>
<!-- Bootstrap JS - 2 -->
-<script src="/js/bootstrap.min.js"></script>
+<script src="/js/underscore-min.js"></script>
+<script src="/js/fuelux/loader.min.js"></script>
+<script src="/js/datasource.js"></script>
<!-- LESS - 3 -->
<script type="text/javascript">
--- /dev/null
+/*!
+ * Bootstrap Responsive v2.3.2
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */.fuelux .clearfix{*zoom:1}.fuelux .clearfix:before,.fuelux .clearfix:after{display:table;line-height:0;content:""}.fuelux .clearfix:after{clear:both}.fuelux .hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.fuelux .input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.fuelux .hidden{display:none;visibility:hidden}.fuelux .visible-phone{display:none!important}.fuelux .visible-tablet{display:none!important}.fuelux .hidden-desktop{display:none!important}.fuelux .visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.fuelux .hidden-desktop{display:inherit!important}.fuelux .visible-desktop{display:none!important}.fuelux .visible-tablet{display:inherit!important}.fuelux .hidden-tablet{display:none!important}}@media(max-width:767px){.fuelux .hidden-desktop{display:inherit!important}.fuelux .visible-desktop{display:none!important}.fuelux .visible-phone{display:inherit!important}.fuelux .hidden-phone{display:none!important}}.fuelux .visible-print{display:none!important}@media print{.fuelux .visible-print{display:inherit!important}.fuelux .hidden-print{display:none!important}}@media(min-width:1200px){.fuelux .row{margin-left:-30px;*zoom:1}.fuelux .row:before,.fuelux .row:after{display:table;line-height:0;content:""}.fuelux .row:after{clear:both}.fuelux [class*="span"]{float:left;min-height:1px;margin-left:30px}.fuelux .container,.fuelux .navbar-static-top .container,.fuelux .navbar-fixed-top .container,.fuelux .navbar-fixed-bottom .container{width:1170px}.fuelux .span12{width:1170px}.fuelux .span11{width:1070px}.fuelux .span10{width:970px}.fuelux .span9{width:870px}.fuelux .span8{width:770px}.fuelux .span7{width:670px}.fuelux .span6{width:570px}.fuelux .span5{width:470px}.fuelux .span4{width:370px}.fuelux .span3{width:270px}.fuelux .span2{width:170px}.fuelux .span1{width:70px}.fuelux .offset12{margin-left:1230px}.fuelux .offset11{margin-left:1130px}.fuelux .offset10{margin-left:1030px}.fuelux .offset9{margin-left:930px}.fuelux .offset8{margin-left:830px}.fuelux .offset7{margin-left:730px}.fuelux .offset6{margin-left:630px}.fuelux .offset5{margin-left:530px}.fuelux .offset4{margin-left:430px}.fuelux .offset3{margin-left:330px}.fuelux .offset2{margin-left:230px}.fuelux .offset1{margin-left:130px}.fuelux .row-fluid{width:100%;*zoom:1}.fuelux .row-fluid:before,.fuelux .row-fluid:after{display:table;line-height:0;content:""}.fuelux .row-fluid:after{clear:both}.fuelux .row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux .row-fluid [class*="span"]:first-child{margin-left:0}.fuelux .row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.fuelux .row-fluid .span12{width:100%;*width:99.94680851063829%}.fuelux .row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.fuelux .row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.fuelux .row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.fuelux .row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.fuelux .row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.fuelux .row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.fuelux .row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.fuelux .row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.fuelux .row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.fuelux .row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.fuelux .row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.fuelux .row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.fuelux .row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.fuelux .row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.fuelux .row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.fuelux .row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.fuelux .row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.fuelux .row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.fuelux .row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.fuelux .row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.fuelux .row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.fuelux .row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.fuelux .row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.fuelux .row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.fuelux .row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.fuelux .row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.fuelux .row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.fuelux .row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.fuelux .row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.fuelux .row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.fuelux .row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.fuelux .row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.fuelux .row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.fuelux .row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.fuelux .row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}.fuelux input,.fuelux textarea,.fuelux .uneditable-input{margin-left:0}.fuelux .controls-row [class*="span"]+[class*="span"]{margin-left:30px}.fuelux input.span12,textarea.span12,.uneditable-input.span12{width:1156px}.fuelux input.span11,textarea.span11,.uneditable-input.span11{width:1056px}.fuelux input.span10,textarea.span10,.uneditable-input.span10{width:956px}.fuelux input.span9,textarea.span9,.uneditable-input.span9{width:856px}.fuelux input.span8,textarea.span8,.uneditable-input.span8{width:756px}.fuelux input.span7,textarea.span7,.uneditable-input.span7{width:656px}.fuelux input.span6,textarea.span6,.uneditable-input.span6{width:556px}.fuelux input.span5,textarea.span5,.uneditable-input.span5{width:456px}.fuelux input.span4,textarea.span4,.uneditable-input.span4{width:356px}.fuelux input.span3,textarea.span3,.uneditable-input.span3{width:256px}.fuelux input.span2,textarea.span2,.uneditable-input.span2{width:156px}.fuelux input.span1,textarea.span1,.uneditable-input.span1{width:56px}.fuelux .thumbnails{margin-left:-30px}.fuelux .thumbnails>li{margin-left:30px}.fuelux .row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.fuelux .row{margin-left:-20px;*zoom:1}.fuelux .row:before,.fuelux .row:after{display:table;line-height:0;content:""}.fuelux .row:after{clear:both}.fuelux [class*="span"]{float:left;min-height:1px;margin-left:20px}.fuelux .container,.fuelux .navbar-static-top .container,.fuelux .navbar-fixed-top .container,.fuelux .navbar-fixed-bottom .container{width:724px}.fuelux .span12{width:724px}.fuelux .span11{width:662px}.fuelux .span10{width:600px}.fuelux .span9{width:538px}.fuelux .span8{width:476px}.fuelux .span7{width:414px}.fuelux .span6{width:352px}.fuelux .span5{width:290px}.fuelux .span4{width:228px}.fuelux .span3{width:166px}.fuelux .span2{width:104px}.fuelux .span1{width:42px}.fuelux .offset12{margin-left:764px}.fuelux .offset11{margin-left:702px}.fuelux .offset10{margin-left:640px}.fuelux .offset9{margin-left:578px}.fuelux .offset8{margin-left:516px}.fuelux .offset7{margin-left:454px}.fuelux .offset6{margin-left:392px}.fuelux .offset5{margin-left:330px}.fuelux .offset4{margin-left:268px}.fuelux .offset3{margin-left:206px}.fuelux .offset2{margin-left:144px}.fuelux .offset1{margin-left:82px}.fuelux .row-fluid{width:100%;*zoom:1}.fuelux .row-fluid:before,.fuelux .row-fluid:after{display:table;line-height:0;content:""}.fuelux .row-fluid:after{clear:both}.fuelux .row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux .row-fluid [class*="span"]:first-child{margin-left:0}.fuelux .row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.fuelux .row-fluid .span12{width:100%;*width:99.94680851063829%}.fuelux .row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.fuelux .row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.fuelux .row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.fuelux .row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.fuelux .row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.fuelux .row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.fuelux .row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.fuelux .row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.fuelux .row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.fuelux .row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.fuelux .row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.fuelux .row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.fuelux .row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.fuelux .row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.fuelux .row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.fuelux .row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.fuelux .row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.fuelux .row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.fuelux .row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.fuelux .row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.fuelux .row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.fuelux .row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.fuelux .row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.fuelux .row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.fuelux .row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.fuelux .row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.fuelux .row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.fuelux .row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.fuelux .row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.fuelux .row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.fuelux .row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.fuelux .row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.fuelux .row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.fuelux .row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.fuelux .row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}.fuelux input,.fuelux textarea,.fuelux .uneditable-input{margin-left:0}.fuelux .controls-row [class*="span"]+[class*="span"]{margin-left:20px}.fuelux input.span12,textarea.span12,.uneditable-input.span12{width:710px}.fuelux input.span11,textarea.span11,.uneditable-input.span11{width:648px}.fuelux input.span10,textarea.span10,.uneditable-input.span10{width:586px}.fuelux input.span9,textarea.span9,.uneditable-input.span9{width:524px}.fuelux input.span8,textarea.span8,.uneditable-input.span8{width:462px}.fuelux input.span7,textarea.span7,.uneditable-input.span7{width:400px}.fuelux input.span6,textarea.span6,.uneditable-input.span6{width:338px}.fuelux input.span5,textarea.span5,.uneditable-input.span5{width:276px}.fuelux input.span4,textarea.span4,.uneditable-input.span4{width:214px}.fuelux input.span3,textarea.span3,.uneditable-input.span3{width:152px}.fuelux input.span2,textarea.span2,.uneditable-input.span2{width:90px}.fuelux input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){.fuelux body{padding-right:20px;padding-left:20px}.fuelux .navbar-fixed-top,.fuelux .navbar-fixed-bottom,.fuelux .navbar-static-top{margin-right:-20px;margin-left:-20px}.fuelux .container-fluid{padding:0}.fuelux .dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.fuelux .dl-horizontal dd{margin-left:0}.fuelux .container{width:auto}.fuelux .row-fluid{width:100%}.fuelux .row,.fuelux .thumbnails{margin-left:0}.fuelux .thumbnails>li{float:none;margin-left:0}.fuelux [class*="span"],.fuelux .uneditable-input[class*="span"],.fuelux .row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux .span12,.fuelux .row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux .row-fluid [class*="offset"]:first-child{margin-left:0}.fuelux .input-large,.fuelux .input-xlarge,.fuelux .input-xxlarge,.fuelux input[class*="span"],.fuelux select[class*="span"],.fuelux textarea[class*="span"],.fuelux .uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux .input-prepend input,.fuelux .input-append input,.fuelux .input-prepend input[class*="span"],.fuelux .input-append input[class*="span"]{display:inline-block;width:auto}.fuelux .controls-row [class*="span"]+[class*="span"]{margin-left:0}.fuelux .modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.fuelux .modal.fade{top:-100px}.fuelux .modal.fade.in{top:20px}}@media(max-width:480px){.fuelux .nav-collapse{-webkit-transform:translate3d(0,0,0)}.fuelux .page-header h1 small{display:block;line-height:20px}.fuelux input[type="checkbox"],.fuelux input[type="radio"]{border:1px solid #ccc}.fuelux .form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.fuelux .form-horizontal .controls{margin-left:0}.fuelux .form-horizontal .control-list{padding-top:0}.fuelux .form-horizontal .form-actions{padding-right:10px;padding-left:10px}.fuelux .media .pull-left,.fuelux .media .pull-right{display:block;float:none;margin-bottom:10px}.fuelux .media-object{margin-right:0;margin-left:0}.fuelux .modal{top:10px;right:10px;left:10px}.fuelux .modal-header .close{padding:10px;margin:-10px}.fuelux .carousel-caption{position:static}}@media(max-width:979px){.fuelux body{padding-top:0}.fuelux .navbar-fixed-top,.fuelux .navbar-fixed-bottom{position:static}.fuelux .navbar-fixed-top{margin-bottom:20px}.fuelux .navbar-fixed-bottom{margin-top:20px}.fuelux .navbar-fixed-top .navbar-inner,.fuelux .navbar-fixed-bottom .navbar-inner{padding:5px}.fuelux .navbar .container{width:auto;padding:0}.fuelux .navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.fuelux .nav-collapse{clear:both}.fuelux .nav-collapse .nav{float:none;margin:0 0 10px}.fuelux .nav-collapse .nav>li{float:none}.fuelux .nav-collapse .nav>li>a{margin-bottom:2px}.fuelux .nav-collapse .nav>.divider-vertical{display:none}.fuelux .nav-collapse .nav .nav-header{color:#777;text-shadow:none}.fuelux .nav-collapse .nav>li>a,.fuelux .nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fuelux .nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux .nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.fuelux .nav-collapse .nav>li>a:hover,.fuelux .nav-collapse .nav>li>a:focus,.fuelux .nav-collapse .dropdown-menu a:hover,.fuelux .nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.fuelux .navbar-inverse .nav-collapse .nav>li>a,.fuelux .navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.fuelux .navbar-inverse .nav-collapse .nav>li>a:hover,.fuelux .navbar-inverse .nav-collapse .nav>li>a:focus,.fuelux .navbar-inverse .nav-collapse .dropdown-menu a:hover,.fuelux .navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.fuelux .nav-collapse.in .btn-group{padding:0;margin-top:5px}.fuelux .nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.fuelux .nav-collapse .open>.dropdown-menu{display:block}.fuelux .nav-collapse .dropdown-menu:before,.fuelux .nav-collapse .dropdown-menu:after{display:none}.fuelux .nav-collapse .dropdown-menu .divider{display:none}.fuelux .nav-collapse .nav>li>.dropdown-menu:before,.fuelux .nav-collapse .nav>li>.dropdown-menu:after{display:none}.fuelux .nav-collapse .navbar-form,.fuelux .nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.fuelux .navbar-inverse .nav-collapse .navbar-form,.fuelux .navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.fuelux .navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.fuelux .nav-collapse,.fuelux .nav-collapse.collapse{height:0;overflow:hidden}.fuelux .navbar .btn-navbar{display:block}.fuelux .navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.fuelux .nav-collapse.collapse{height:auto!important;overflow:visible!important}}
\ No newline at end of file
--- /dev/null
+/*!
+ * Bootstrap v2.3.2
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */.fuelux .clearfix{*zoom:1}.fuelux .clearfix:before,.fuelux .clearfix:after{display:table;line-height:0;content:""}.fuelux .clearfix:after{clear:both}.fuelux .hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.fuelux .input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux article,.fuelux aside,.fuelux details,.fuelux figcaption,.fuelux figure,.fuelux footer,.fuelux header,.fuelux hgroup,.fuelux nav,.fuelux section{display:block}.fuelux audio,.fuelux canvas,.fuelux video{display:inline-block;*display:inline;*zoom:1}.fuelux audio:not([controls]){display:none}.fuelux html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}.fuelux a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.fuelux a:hover,.fuelux a:active{outline:0}.fuelux sub,.fuelux sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}.fuelux sup{top:-0.5em}.fuelux sub{bottom:-0.25em}.fuelux img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}.fuelux #map_canvas img,.fuelux .google-maps img{max-width:none}.fuelux button,.fuelux input,.fuelux select,.fuelux textarea{margin:0;font-size:100%;vertical-align:middle}.fuelux button,.fuelux input{*overflow:visible;line-height:normal}.fuelux button::-moz-focus-inner,.fuelux input::-moz-focus-inner{padding:0;border:0}.fuelux button,.fuelux html input[type="button"],.fuelux input[type="reset"],.fuelux input[type="submit"]{cursor:pointer;-webkit-appearance:button}.fuelux label,.fuelux select,.fuelux button,.fuelux input[type="button"],.fuelux input[type="reset"],.fuelux input[type="submit"],.fuelux input[type="radio"],.fuelux input[type="checkbox"]{cursor:pointer}.fuelux input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}.fuelux input[type="search"]::-webkit-search-decoration,.fuelux input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}.fuelux textarea{overflow:auto;vertical-align:top}@media print{.fuelux *{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}.fuelux a,.fuelux a:visited{text-decoration:underline}.fuelux a[href]:after{content:" (" attr(href) ")"}.fuelux abbr[title]:after{content:" (" attr(title) ")"}.fuelux .ir a:after,.fuelux a[href^="javascript:"]:after,.fuelux a[href^="#"]:after{content:""}.fuelux pre,.fuelux blockquote{border:1px solid #999;page-break-inside:avoid}.fuelux thead{display:table-header-group}.fuelux tr,.fuelux img{page-break-inside:avoid}.fuelux img{max-width:100%!important}@page{margin:.5cm}.fuelux p,.fuelux h2,.fuelux h3{widows:3;orphans:3}.fuelux h2,.fuelux h3{page-break-after:avoid}}.fuelux body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}.fuelux a{color:#08c;text-decoration:none}.fuelux a:hover,.fuelux a:focus{color:#005580;text-decoration:underline}.fuelux .img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.fuelux .img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.fuelux .row{margin-left:-20px;*zoom:1}.fuelux .row:before,.fuelux .row:after{display:table;line-height:0;content:""}.fuelux .row:after{clear:both}.fuelux [class*="span"]{float:left;min-height:1px;margin-left:20px}.fuelux .container,.fuelux .navbar-static-top .container,.fuelux .navbar-fixed-top .container,.fuelux .navbar-fixed-bottom .container{width:940px}.fuelux .span12{width:940px}.fuelux .span11{width:860px}.fuelux .span10{width:780px}.fuelux .span9{width:700px}.fuelux .span8{width:620px}.fuelux .span7{width:540px}.fuelux .span6{width:460px}.fuelux .span5{width:380px}.fuelux .span4{width:300px}.fuelux .span3{width:220px}.fuelux .span2{width:140px}.fuelux .span1{width:60px}.fuelux .offset12{margin-left:980px}.fuelux .offset11{margin-left:900px}.fuelux .offset10{margin-left:820px}.fuelux .offset9{margin-left:740px}.fuelux .offset8{margin-left:660px}.fuelux .offset7{margin-left:580px}.fuelux .offset6{margin-left:500px}.fuelux .offset5{margin-left:420px}.fuelux .offset4{margin-left:340px}.fuelux .offset3{margin-left:260px}.fuelux .offset2{margin-left:180px}.fuelux .offset1{margin-left:100px}.fuelux .row-fluid{width:100%;*zoom:1}.fuelux .row-fluid:before,.fuelux .row-fluid:after{display:table;line-height:0;content:""}.fuelux .row-fluid:after{clear:both}.fuelux .row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux .row-fluid [class*="span"]:first-child{margin-left:0}.fuelux .row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.fuelux .row-fluid .span12{width:100%;*width:99.94680851063829%}.fuelux .row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.fuelux .row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.fuelux .row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.fuelux .row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.fuelux .row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.fuelux .row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.fuelux .row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.fuelux .row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.fuelux .row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.fuelux .row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.fuelux .row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.fuelux .row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.fuelux .row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.fuelux .row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.fuelux .row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.fuelux .row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.fuelux .row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.fuelux .row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.fuelux .row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.fuelux .row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.fuelux .row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.fuelux .row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.fuelux .row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.fuelux .row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.fuelux .row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.fuelux .row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.fuelux .row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.fuelux .row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.fuelux .row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.fuelux .row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.fuelux .row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.fuelux .row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.fuelux .row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.fuelux .row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.fuelux .row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}.fuelux [class*="span"].hide,.fuelux .row-fluid [class*="span"].hide{display:none}.fuelux [class*="span"].pull-right,.fuelux .row-fluid [class*="span"].pull-right{float:right}.fuelux .container{margin-right:auto;margin-left:auto;*zoom:1}.fuelux .container:before,.fuelux .container:after{display:table;line-height:0;content:""}.fuelux .container:after{clear:both}.fuelux .container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.fuelux .container-fluid:before,.fuelux .container-fluid:after{display:table;line-height:0;content:""}.fuelux .container-fluid:after{clear:both}.fuelux p{margin:0 0 10px}.fuelux .lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}.fuelux small{font-size:85%}.fuelux strong{font-weight:bold}.fuelux em{font-style:italic}.fuelux cite{font-style:normal}.fuelux .muted{color:#999}.fuelux a.muted:hover,.fuelux a.muted:focus{color:#808080}.fuelux .text-warning{color:#c09853}.fuelux a.text-warning:hover,.fuelux a.text-warning:focus{color:#a47e3c}.fuelux .text-error{color:#b94a48}.fuelux a.text-error:hover,.fuelux a.text-error:focus{color:#953b39}.fuelux .text-info{color:#3a87ad}.fuelux a.text-info:hover,.fuelux a.text-info:focus{color:#2d6987}.fuelux .text-success{color:#468847}.fuelux a.text-success:hover,.fuelux a.text-success:focus{color:#356635}.fuelux .text-left{text-align:left}.fuelux .text-right{text-align:right}.fuelux .text-center{text-align:center}.fuelux h1,.fuelux h2,.fuelux h3,.fuelux h4,.fuelux h5,.fuelux h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}.fuelux h1 small,.fuelux h2 small,.fuelux h3 small,.fuelux h4 small,.fuelux h5 small,.fuelux h6 small{font-weight:normal;line-height:1;color:#999}.fuelux h1,.fuelux h2,.fuelux h3{line-height:40px}.fuelux h1{font-size:38.5px}.fuelux h2{font-size:31.5px}.fuelux h3{font-size:24.5px}.fuelux h4{font-size:17.5px}.fuelux h5{font-size:14px}.fuelux h6{font-size:11.9px}.fuelux h1 small{font-size:24.5px}.fuelux h2 small{font-size:17.5px}.fuelux h3 small{font-size:14px}.fuelux h4 small{font-size:14px}.fuelux .page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}.fuelux ul,.fuelux ol{padding:0;margin:0 0 10px 25px}.fuelux ul ul,.fuelux ul ol,.fuelux ol ol,.fuelux ol ul{margin-bottom:0}.fuelux li{line-height:20px}.fuelux ul.unstyled,.fuelux ol.unstyled{margin-left:0;list-style:none}.fuelux ul.inline,.fuelux ol.inline{margin-left:0;list-style:none}.fuelux ul.inline>li,.fuelux ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}.fuelux dl{margin-bottom:20px}.fuelux dt,.fuelux dd{line-height:20px}.fuelux dt{font-weight:bold}.fuelux dd{margin-left:10px}.fuelux .dl-horizontal{*zoom:1}.fuelux .dl-horizontal:before,.fuelux .dl-horizontal:after{display:table;line-height:0;content:""}.fuelux .dl-horizontal:after{clear:both}.fuelux .dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.fuelux .dl-horizontal dd{margin-left:180px}.fuelux hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}.fuelux abbr[title],.fuelux abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.fuelux abbr.initialism{font-size:90%;text-transform:uppercase}.fuelux blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}.fuelux blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}.fuelux blockquote small{display:block;line-height:20px;color:#999}.fuelux blockquote small:before{content:'\2014 \00A0'}.fuelux blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}.fuelux blockquote.pull-right p,.fuelux blockquote.pull-right small{text-align:right}.fuelux blockquote.pull-right small:before{content:''}.fuelux blockquote.pull-right small:after{content:'\00A0 \2014'}.fuelux q:before,.fuelux q:after,.fuelux blockquote:before,.fuelux blockquote:after{content:""}.fuelux address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}.fuelux code,.fuelux pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fuelux code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}.fuelux pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux pre.prettyprint{margin-bottom:20px}.fuelux pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.fuelux .pre-scrollable{max-height:340px;overflow-y:scroll}.fuelux form{margin:0 0 20px}.fuelux fieldset{padding:0;margin:0;border:0}.fuelux legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}.fuelux legend small{font-size:15px;color:#999}.fuelux label,.fuelux input,.fuelux button,.fuelux select,.fuelux textarea{font-size:14px;font-weight:normal;line-height:20px}.fuelux input,.fuelux button,.fuelux select,.fuelux textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.fuelux label{display:block;margin-bottom:5px}.fuelux select,.fuelux textarea,.fuelux input[type="text"],.fuelux input[type="password"],.fuelux input[type="datetime"],.fuelux input[type="datetime-local"],.fuelux input[type="date"],.fuelux input[type="month"],.fuelux input[type="time"],.fuelux input[type="week"],.fuelux input[type="number"],.fuelux input[type="email"],.fuelux input[type="url"],.fuelux input[type="search"],.fuelux input[type="tel"],.fuelux input[type="color"],.fuelux .uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux input,.fuelux textarea,.fuelux .uneditable-input{width:206px}.fuelux textarea{height:auto}.fuelux textarea,.fuelux input[type="text"],.fuelux input[type="password"],.fuelux input[type="datetime"],.fuelux input[type="datetime-local"],.fuelux input[type="date"],.fuelux input[type="month"],.fuelux input[type="time"],.fuelux input[type="week"],.fuelux input[type="number"],.fuelux input[type="email"],.fuelux input[type="url"],.fuelux input[type="search"],.fuelux input[type="tel"],.fuelux input[type="color"],.fuelux .uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}.fuelux textarea:focus,.fuelux input[type="text"]:focus,.fuelux input[type="password"]:focus,.fuelux input[type="datetime"]:focus,.fuelux input[type="datetime-local"]:focus,.fuelux input[type="date"]:focus,.fuelux input[type="month"]:focus,.fuelux input[type="time"]:focus,.fuelux input[type="week"]:focus,.fuelux input[type="number"]:focus,.fuelux input[type="email"]:focus,.fuelux input[type="url"]:focus,.fuelux input[type="search"]:focus,.fuelux input[type="tel"]:focus,.fuelux input[type="color"]:focus,.fuelux .uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.fuelux input[type="radio"],.fuelux input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}.fuelux input[type="file"],.fuelux input[type="image"],.fuelux input[type="submit"],.fuelux input[type="reset"],.fuelux input[type="button"],.fuelux input[type="radio"],.fuelux input[type="checkbox"]{width:auto}.fuelux select,.fuelux input[type="file"]{height:30px;*margin-top:4px;line-height:30px}.fuelux select{width:220px;background-color:#fff;border:1px solid #ccc}.fuelux select[multiple],.fuelux select[size]{height:auto}.fuelux select:focus,.fuelux input[type="file"]:focus,.fuelux input[type="radio"]:focus,.fuelux input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.fuelux .uneditable-input,.fuelux .uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.fuelux .uneditable-input{overflow:hidden;white-space:nowrap}.fuelux .uneditable-textarea{width:auto;height:auto}.fuelux input:-moz-placeholder,.fuelux textarea:-moz-placeholder{color:#999}.fuelux input:-ms-input-placeholder,.fuelux textarea:-ms-input-placeholder{color:#999}.fuelux input::-webkit-input-placeholder,.fuelux textarea::-webkit-input-placeholder{color:#999}.fuelux .radio,.fuelux .checkbox{min-height:20px;padding-left:20px}.fuelux .radio input[type="radio"],.fuelux .checkbox input[type="checkbox"]{float:left;margin-left:-20px}.fuelux .controls>.radio:first-child,.fuelux .controls>.checkbox:first-child{padding-top:5px}.fuelux .radio.inline,.fuelux .checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.fuelux .radio.inline+.radio.inline,.fuelux .checkbox.inline+.checkbox.inline{margin-left:10px}.fuelux .input-mini{width:60px}.fuelux .input-small{width:90px}.fuelux .input-medium{width:150px}.fuelux .input-large{width:210px}.fuelux .input-xlarge{width:270px}.fuelux .input-xxlarge{width:530px}.fuelux input[class*="span"],.fuelux select[class*="span"],.fuelux textarea[class*="span"],.fuelux .uneditable-input[class*="span"],.fuelux .row-fluid input[class*="span"],.fuelux .row-fluid select[class*="span"],.fuelux .row-fluid textarea[class*="span"],.fuelux .row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.fuelux .input-append input[class*="span"],.fuelux .input-append .uneditable-input[class*="span"],.fuelux .input-prepend input[class*="span"],.fuelux .input-prepend .uneditable-input[class*="span"],.fuelux .row-fluid input[class*="span"],.fuelux .row-fluid select[class*="span"],.fuelux .row-fluid textarea[class*="span"],.fuelux .row-fluid .uneditable-input[class*="span"],.fuelux .row-fluid .input-prepend [class*="span"],.fuelux .row-fluid .input-append [class*="span"]{display:inline-block}.fuelux input,.fuelux textarea,.fuelux .uneditable-input{margin-left:0}.fuelux .controls-row [class*="span"]+[class*="span"]{margin-left:20px}.fuelux input.span12,textarea.span12,.uneditable-input.span12{width:926px}.fuelux input.span11,textarea.span11,.uneditable-input.span11{width:846px}.fuelux input.span10,textarea.span10,.uneditable-input.span10{width:766px}.fuelux input.span9,textarea.span9,.uneditable-input.span9{width:686px}.fuelux input.span8,textarea.span8,.uneditable-input.span8{width:606px}.fuelux input.span7,textarea.span7,.uneditable-input.span7{width:526px}.fuelux input.span6,textarea.span6,.uneditable-input.span6{width:446px}.fuelux input.span5,textarea.span5,.uneditable-input.span5{width:366px}.fuelux input.span4,textarea.span4,.uneditable-input.span4{width:286px}.fuelux input.span3,textarea.span3,.uneditable-input.span3{width:206px}.fuelux input.span2,textarea.span2,.uneditable-input.span2{width:126px}.fuelux input.span1,textarea.span1,.uneditable-input.span1{width:46px}.fuelux .controls-row{*zoom:1}.fuelux .controls-row:before,.fuelux .controls-row:after{display:table;line-height:0;content:""}.fuelux .controls-row:after{clear:both}.fuelux .controls-row [class*="span"],.fuelux .row-fluid .controls-row [class*="span"]{float:left}.fuelux .controls-row .checkbox[class*="span"],.fuelux .controls-row .radio[class*="span"]{padding-top:5px}.fuelux input[disabled],.fuelux select[disabled],.fuelux textarea[disabled],.fuelux input[readonly],.fuelux select[readonly],.fuelux textarea[readonly]{cursor:not-allowed;background-color:#eee}.fuelux input[type="radio"][disabled],.fuelux input[type="checkbox"][disabled],.fuelux input[type="radio"][readonly],.fuelux input[type="checkbox"][readonly]{background-color:transparent}.fuelux .control-group.warning .control-label,.fuelux .control-group.warning .help-block,.fuelux .control-group.warning .help-inline{color:#c09853}.fuelux .control-group.warning .checkbox,.fuelux .control-group.warning .radio,.fuelux .control-group.warning input,.fuelux .control-group.warning select,.fuelux .control-group.warning textarea{color:#c09853}.fuelux .control-group.warning input,.fuelux .control-group.warning select,.fuelux .control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.fuelux .control-group.warning input:focus,.fuelux .control-group.warning select:focus,.fuelux .control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.fuelux .control-group.warning .input-prepend .add-on,.fuelux .control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.fuelux .control-group.error .control-label,.fuelux .control-group.error .help-block,.fuelux .control-group.error .help-inline{color:#b94a48}.fuelux .control-group.error .checkbox,.fuelux .control-group.error .radio,.fuelux .control-group.error input,.fuelux .control-group.error select,.fuelux .control-group.error textarea{color:#b94a48}.fuelux .control-group.error input,.fuelux .control-group.error select,.fuelux .control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.fuelux .control-group.error input:focus,.fuelux .control-group.error select:focus,.fuelux .control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.fuelux .control-group.error .input-prepend .add-on,.fuelux .control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.fuelux .control-group.success .control-label,.fuelux .control-group.success .help-block,.fuelux .control-group.success .help-inline{color:#468847}.fuelux .control-group.success .checkbox,.fuelux .control-group.success .radio,.fuelux .control-group.success input,.fuelux .control-group.success select,.fuelux .control-group.success textarea{color:#468847}.fuelux .control-group.success input,.fuelux .control-group.success select,.fuelux .control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.fuelux .control-group.success input:focus,.fuelux .control-group.success select:focus,.fuelux .control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.fuelux .control-group.success .input-prepend .add-on,.fuelux .control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.fuelux .control-group.info .control-label,.fuelux .control-group.info .help-block,.fuelux .control-group.info .help-inline{color:#3a87ad}.fuelux .control-group.info .checkbox,.fuelux .control-group.info .radio,.fuelux .control-group.info input,.fuelux .control-group.info select,.fuelux .control-group.info textarea{color:#3a87ad}.fuelux .control-group.info input,.fuelux .control-group.info select,.fuelux .control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.fuelux .control-group.info input:focus,.fuelux .control-group.info select:focus,.fuelux .control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.fuelux .control-group.info .input-prepend .add-on,.fuelux .control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}.fuelux input:focus:invalid,.fuelux textarea:focus:invalid,.fuelux select:focus:invalid{color:#b94a48;border-color:#ee5f5b}.fuelux input:focus:invalid:focus,.fuelux textarea:focus:invalid:focus,.fuelux select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.fuelux .form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.fuelux .form-actions:before,.fuelux .form-actions:after{display:table;line-height:0;content:""}.fuelux .form-actions:after{clear:both}.fuelux .help-block,.fuelux .help-inline{color:#595959}.fuelux .help-block{display:block;margin-bottom:10px}.fuelux .help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.fuelux .input-append,.fuelux .input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.fuelux .input-append input,.fuelux .input-prepend input,.fuelux .input-append select,.fuelux .input-prepend select,.fuelux .input-append .uneditable-input,.fuelux .input-prepend .uneditable-input,.fuelux .input-append .dropdown-menu,.fuelux .input-prepend .dropdown-menu,.fuelux .input-append .popover,.fuelux .input-prepend .popover{font-size:14px}.fuelux .input-append input,.fuelux .input-prepend input,.fuelux .input-append select,.fuelux .input-prepend select,.fuelux .input-append .uneditable-input,.fuelux .input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.fuelux .input-append input:focus,.fuelux .input-prepend input:focus,.fuelux .input-append select:focus,.fuelux .input-prepend select:focus,.fuelux .input-append .uneditable-input:focus,.fuelux .input-prepend .uneditable-input:focus{z-index:2}.fuelux .input-append .add-on,.fuelux .input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.fuelux .input-append .add-on,.fuelux .input-prepend .add-on,.fuelux .input-append .btn,.fuelux .input-prepend .btn,.fuelux .input-append .btn-group>.dropdown-toggle,.fuelux .input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .input-append .active,.fuelux .input-prepend .active{background-color:#a9dba9;border-color:#46a546}.fuelux .input-prepend .add-on,.fuelux .input-prepend .btn{margin-right:-1px}.fuelux .input-prepend .add-on:first-child,.fuelux .input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.fuelux .input-append input,.fuelux .input-append select,.fuelux .input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.fuelux .input-append input+.btn-group .btn:last-child,.fuelux .input-append select+.btn-group .btn:last-child,.fuelux .input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.fuelux .input-append .add-on,.fuelux .input-append .btn,.fuelux .input-append .btn-group{margin-left:-1px}.fuelux .input-append .add-on:last-child,.fuelux .input-append .btn:last-child,.fuelux .input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.fuelux .input-prepend.input-append input,.fuelux .input-prepend.input-append select,.fuelux .input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .input-prepend.input-append input+.btn-group .btn,.fuelux .input-prepend.input-append select+.btn-group .btn,.fuelux .input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.fuelux .input-prepend.input-append .add-on:first-child,.fuelux .input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.fuelux .input-prepend.input-append .add-on:last-child,.fuelux .input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.fuelux .input-prepend.input-append .btn-group:first-child{margin-left:0}.fuelux input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.fuelux .form-search .input-append .search-query,.fuelux .form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.fuelux .form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.fuelux .form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.fuelux .form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.fuelux .form-search input,.fuelux .form-inline input,.fuelux .form-horizontal input,.fuelux .form-search textarea,.fuelux .form-inline textarea,.fuelux .form-horizontal textarea,.fuelux .form-search select,.fuelux .form-inline select,.fuelux .form-horizontal select,.fuelux .form-search .help-inline,.fuelux .form-inline .help-inline,.fuelux .form-horizontal .help-inline,.fuelux .form-search .uneditable-input,.fuelux .form-inline .uneditable-input,.fuelux .form-horizontal .uneditable-input,.fuelux .form-search .input-prepend,.fuelux .form-inline .input-prepend,.fuelux .form-horizontal .input-prepend,.fuelux .form-search .input-append,.fuelux .form-inline .input-append,.fuelux .form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.fuelux .form-search .hide,.fuelux .form-inline .hide,.fuelux .form-horizontal .hide{display:none}.fuelux .form-search label,.fuelux .form-inline label,.fuelux .form-search .btn-group,.fuelux .form-inline .btn-group{display:inline-block}.fuelux .form-search .input-append,.fuelux .form-inline .input-append,.fuelux .form-search .input-prepend,.fuelux .form-inline .input-prepend{margin-bottom:0}.fuelux .form-search .radio,.fuelux .form-search .checkbox,.fuelux .form-inline .radio,.fuelux .form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.fuelux .form-search .radio input[type="radio"],.fuelux .form-search .checkbox input[type="checkbox"],.fuelux .form-inline .radio input[type="radio"],.fuelux .form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.fuelux .control-group{margin-bottom:10px}.fuelux legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.fuelux .form-horizontal .control-group{margin-bottom:20px;*zoom:1}.fuelux .form-horizontal .control-group:before,.fuelux .form-horizontal .control-group:after{display:table;line-height:0;content:""}.fuelux .form-horizontal .control-group:after{clear:both}.fuelux .form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.fuelux .form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.fuelux .form-horizontal .controls:first-child{*padding-left:180px}.fuelux .form-horizontal .help-block{margin-bottom:0}.fuelux .form-horizontal input+.help-block,.fuelux .form-horizontal select+.help-block,.fuelux .form-horizontal textarea+.help-block,.fuelux .form-horizontal .uneditable-input+.help-block,.fuelux .form-horizontal .input-prepend+.help-block,.fuelux .form-horizontal .input-append+.help-block{margin-top:10px}.fuelux .form-horizontal .form-actions{padding-left:180px}.fuelux table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.fuelux .table{width:100%;margin-bottom:20px}.fuelux .table th,.fuelux .table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.fuelux .table th{font-weight:bold}.fuelux .table thead th{vertical-align:bottom}.fuelux .table caption+thead tr:first-child th,.fuelux .table caption+thead tr:first-child td,.fuelux .table colgroup+thead tr:first-child th,.fuelux .table colgroup+thead tr:first-child td,.fuelux .table thead:first-child tr:first-child th,.fuelux .table thead:first-child tr:first-child td{border-top:0}.fuelux .table tbody+tbody{border-top:2px solid #ddd}.fuelux .table .table{background-color:#fff}.fuelux .table-condensed th,.fuelux .table-condensed td{padding:4px 5px}.fuelux .table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux .table-bordered th,.fuelux .table-bordered td{border-left:1px solid #ddd}.fuelux .table-bordered caption+thead tr:first-child th,.fuelux .table-bordered caption+tbody tr:first-child th,.fuelux .table-bordered caption+tbody tr:first-child td,.fuelux .table-bordered colgroup+thead tr:first-child th,.fuelux .table-bordered colgroup+tbody tr:first-child th,.fuelux .table-bordered colgroup+tbody tr:first-child td,.fuelux .table-bordered thead:first-child tr:first-child th,.fuelux .table-bordered tbody:first-child tr:first-child th,.fuelux .table-bordered tbody:first-child tr:first-child td{border-top:0}.fuelux .table-bordered thead:first-child tr:first-child>th:first-child,.fuelux .table-bordered tbody:first-child tr:first-child>td:first-child,.fuelux .table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.fuelux .table-bordered thead:first-child tr:first-child>th:last-child,.fuelux .table-bordered tbody:first-child tr:first-child>td:last-child,.fuelux .table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.fuelux .table-bordered thead:last-child tr:last-child>th:first-child,.fuelux .table-bordered tbody:last-child tr:last-child>td:first-child,.fuelux .table-bordered tbody:last-child tr:last-child>th:first-child,.fuelux .table-bordered tfoot:last-child tr:last-child>td:first-child,.fuelux .table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.fuelux .table-bordered thead:last-child tr:last-child>th:last-child,.fuelux .table-bordered tbody:last-child tr:last-child>td:last-child,.fuelux .table-bordered tbody:last-child tr:last-child>th:last-child,.fuelux .table-bordered tfoot:last-child tr:last-child>td:last-child,.fuelux .table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.fuelux .table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.fuelux .table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.fuelux .table-bordered caption+thead tr:first-child th:first-child,.fuelux .table-bordered caption+tbody tr:first-child td:first-child,.fuelux .table-bordered colgroup+thead tr:first-child th:first-child,.fuelux .table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.fuelux .table-bordered caption+thead tr:first-child th:last-child,.fuelux .table-bordered caption+tbody tr:first-child td:last-child,.fuelux .table-bordered colgroup+thead tr:first-child th:last-child,.fuelux .table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.fuelux .table-striped tbody>tr:nth-child(odd)>td,.fuelux .table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.fuelux .table-hover tbody tr:hover>td,.fuelux .table-hover tbody tr:hover>th{background-color:#f5f5f5}.fuelux table td[class*="span"],.fuelux table th[class*="span"],.fuelux .row-fluid table td[class*="span"],.fuelux .row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.fuelux .table td.span1,.fuelux .table th.span1{float:none;width:44px;margin-left:0}.fuelux .table td.span2,.fuelux .table th.span2{float:none;width:124px;margin-left:0}.fuelux .table td.span3,.fuelux .table th.span3{float:none;width:204px;margin-left:0}.fuelux .table td.span4,.fuelux .table th.span4{float:none;width:284px;margin-left:0}.fuelux .table td.span5,.fuelux .table th.span5{float:none;width:364px;margin-left:0}.fuelux .table td.span6,.fuelux .table th.span6{float:none;width:444px;margin-left:0}.fuelux .table td.span7,.fuelux .table th.span7{float:none;width:524px;margin-left:0}.fuelux .table td.span8,.fuelux .table th.span8{float:none;width:604px;margin-left:0}.fuelux .table td.span9,.fuelux .table th.span9{float:none;width:684px;margin-left:0}.fuelux .table td.span10,.fuelux .table th.span10{float:none;width:764px;margin-left:0}.fuelux .table td.span11,.fuelux .table th.span11{float:none;width:844px;margin-left:0}.fuelux .table td.span12,.fuelux .table th.span12{float:none;width:924px;margin-left:0}.fuelux .table tbody tr.success>td{background-color:#dff0d8}.fuelux .table tbody tr.error>td{background-color:#f2dede}.fuelux .table tbody tr.warning>td{background-color:#fcf8e3}.fuelux .table tbody tr.info>td{background-color:#d9edf7}.fuelux .table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.fuelux .table-hover tbody tr.error:hover>td{background-color:#ebcccc}.fuelux .table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.fuelux .table-hover tbody tr.info:hover>td{background-color:#c4e3f3}.fuelux [class^="icon-"],.fuelux [class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.fuelux .icon-white,.fuelux .nav-pills>.active>a>[class^="icon-"],.fuelux .nav-pills>.active>a>[class*=" icon-"],.fuelux .nav-list>.active>a>[class^="icon-"],.fuelux .nav-list>.active>a>[class*=" icon-"],.fuelux .navbar-inverse .nav>.active>a>[class^="icon-"],.fuelux .navbar-inverse .nav>.active>a>[class*=" icon-"],.fuelux .dropdown-menu>li>a:hover>[class^="icon-"],.fuelux .dropdown-menu>li>a:focus>[class^="icon-"],.fuelux .dropdown-menu>li>a:hover>[class*=" icon-"],.fuelux .dropdown-menu>li>a:focus>[class*=" icon-"],.fuelux .dropdown-menu>.active>a>[class^="icon-"],.fuelux .dropdown-menu>.active>a>[class*=" icon-"],.fuelux .dropdown-submenu:hover>a>[class^="icon-"],.fuelux .dropdown-submenu:focus>a>[class^="icon-"],.fuelux .dropdown-submenu:hover>a>[class*=" icon-"],.fuelux .dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.fuelux .icon-glass{background-position:0 0}.fuelux .icon-music{background-position:-24px 0}.fuelux .icon-search{background-position:-48px 0}.fuelux .icon-envelope{background-position:-72px 0}.fuelux .icon-heart{background-position:-96px 0}.fuelux .icon-star{background-position:-120px 0}.fuelux .icon-star-empty{background-position:-144px 0}.fuelux .icon-user{background-position:-168px 0}.fuelux .icon-film{background-position:-192px 0}.fuelux .icon-th-large{background-position:-216px 0}.fuelux .icon-th{background-position:-240px 0}.fuelux .icon-th-list{background-position:-264px 0}.fuelux .icon-ok{background-position:-288px 0}.fuelux .icon-remove{background-position:-312px 0}.fuelux .icon-zoom-in{background-position:-336px 0}.fuelux .icon-zoom-out{background-position:-360px 0}.fuelux .icon-off{background-position:-384px 0}.fuelux .icon-signal{background-position:-408px 0}.fuelux .icon-cog{background-position:-432px 0}.fuelux .icon-trash{background-position:-456px 0}.fuelux .icon-home{background-position:0 -24px}.fuelux .icon-file{background-position:-24px -24px}.fuelux .icon-time{background-position:-48px -24px}.fuelux .icon-road{background-position:-72px -24px}.fuelux .icon-download-alt{background-position:-96px -24px}.fuelux .icon-download{background-position:-120px -24px}.fuelux .icon-upload{background-position:-144px -24px}.fuelux .icon-inbox{background-position:-168px -24px}.fuelux .icon-play-circle{background-position:-192px -24px}.fuelux .icon-repeat{background-position:-216px -24px}.fuelux .icon-refresh{background-position:-240px -24px}.fuelux .icon-list-alt{background-position:-264px -24px}.fuelux .icon-lock{background-position:-287px -24px}.fuelux .icon-flag{background-position:-312px -24px}.fuelux .icon-headphones{background-position:-336px -24px}.fuelux .icon-volume-off{background-position:-360px -24px}.fuelux .icon-volume-down{background-position:-384px -24px}.fuelux .icon-volume-up{background-position:-408px -24px}.fuelux .icon-qrcode{background-position:-432px -24px}.fuelux .icon-barcode{background-position:-456px -24px}.fuelux .icon-tag{background-position:0 -48px}.fuelux .icon-tags{background-position:-25px -48px}.fuelux .icon-book{background-position:-48px -48px}.fuelux .icon-bookmark{background-position:-72px -48px}.fuelux .icon-print{background-position:-96px -48px}.fuelux .icon-camera{background-position:-120px -48px}.fuelux .icon-font{background-position:-144px -48px}.fuelux .icon-bold{background-position:-167px -48px}.fuelux .icon-italic{background-position:-192px -48px}.fuelux .icon-text-height{background-position:-216px -48px}.fuelux .icon-text-width{background-position:-240px -48px}.fuelux .icon-align-left{background-position:-264px -48px}.fuelux .icon-align-center{background-position:-288px -48px}.fuelux .icon-align-right{background-position:-312px -48px}.fuelux .icon-align-justify{background-position:-336px -48px}.fuelux .icon-list{background-position:-360px -48px}.fuelux .icon-indent-left{background-position:-384px -48px}.fuelux .icon-indent-right{background-position:-408px -48px}.fuelux .icon-facetime-video{background-position:-432px -48px}.fuelux .icon-picture{background-position:-456px -48px}.fuelux .icon-pencil{background-position:0 -72px}.fuelux .icon-map-marker{background-position:-24px -72px}.fuelux .icon-adjust{background-position:-48px -72px}.fuelux .icon-tint{background-position:-72px -72px}.fuelux .icon-edit{background-position:-96px -72px}.fuelux .icon-share{background-position:-120px -72px}.fuelux .icon-check{background-position:-144px -72px}.fuelux .icon-move{background-position:-168px -72px}.fuelux .icon-step-backward{background-position:-192px -72px}.fuelux .icon-fast-backward{background-position:-216px -72px}.fuelux .icon-backward{background-position:-240px -72px}.fuelux .icon-play{background-position:-264px -72px}.fuelux .icon-pause{background-position:-288px -72px}.fuelux .icon-stop{background-position:-312px -72px}.fuelux .icon-forward{background-position:-336px -72px}.fuelux .icon-fast-forward{background-position:-360px -72px}.fuelux .icon-step-forward{background-position:-384px -72px}.fuelux .icon-eject{background-position:-408px -72px}.fuelux .icon-chevron-left{background-position:-432px -72px}.fuelux .icon-chevron-right{background-position:-456px -72px}.fuelux .icon-plus-sign{background-position:0 -96px}.fuelux .icon-minus-sign{background-position:-24px -96px}.fuelux .icon-remove-sign{background-position:-48px -96px}.fuelux .icon-ok-sign{background-position:-72px -96px}.fuelux .icon-question-sign{background-position:-96px -96px}.fuelux .icon-info-sign{background-position:-120px -96px}.fuelux .icon-screenshot{background-position:-144px -96px}.fuelux .icon-remove-circle{background-position:-168px -96px}.fuelux .icon-ok-circle{background-position:-192px -96px}.fuelux .icon-ban-circle{background-position:-216px -96px}.fuelux .icon-arrow-left{background-position:-240px -96px}.fuelux .icon-arrow-right{background-position:-264px -96px}.fuelux .icon-arrow-up{background-position:-289px -96px}.fuelux .icon-arrow-down{background-position:-312px -96px}.fuelux .icon-share-alt{background-position:-336px -96px}.fuelux .icon-resize-full{background-position:-360px -96px}.fuelux .icon-resize-small{background-position:-384px -96px}.fuelux .icon-plus{background-position:-408px -96px}.fuelux .icon-minus{background-position:-433px -96px}.fuelux .icon-asterisk{background-position:-456px -96px}.fuelux .icon-exclamation-sign{background-position:0 -120px}.fuelux .icon-gift{background-position:-24px -120px}.fuelux .icon-leaf{background-position:-48px -120px}.fuelux .icon-fire{background-position:-72px -120px}.fuelux .icon-eye-open{background-position:-96px -120px}.fuelux .icon-eye-close{background-position:-120px -120px}.fuelux .icon-warning-sign{background-position:-144px -120px}.fuelux .icon-plane{background-position:-168px -120px}.fuelux .icon-calendar{background-position:-192px -120px}.fuelux .icon-random{width:16px;background-position:-216px -120px}.fuelux .icon-comment{background-position:-240px -120px}.fuelux .icon-magnet{background-position:-264px -120px}.fuelux .icon-chevron-up{background-position:-288px -120px}.fuelux .icon-chevron-down{background-position:-313px -119px}.fuelux .icon-retweet{background-position:-336px -120px}.fuelux .icon-shopping-cart{background-position:-360px -120px}.fuelux .icon-folder-close{width:16px;background-position:-384px -120px}.fuelux .icon-folder-open{width:16px;background-position:-408px -120px}.fuelux .icon-resize-vertical{background-position:-432px -119px}.fuelux .icon-resize-horizontal{background-position:-456px -118px}.fuelux .icon-hdd{background-position:0 -144px}.fuelux .icon-bullhorn{background-position:-24px -144px}.fuelux .icon-bell{background-position:-48px -144px}.fuelux .icon-certificate{background-position:-72px -144px}.fuelux .icon-thumbs-up{background-position:-96px -144px}.fuelux .icon-thumbs-down{background-position:-120px -144px}.fuelux .icon-hand-right{background-position:-144px -144px}.fuelux .icon-hand-left{background-position:-168px -144px}.fuelux .icon-hand-up{background-position:-192px -144px}.fuelux .icon-hand-down{background-position:-216px -144px}.fuelux .icon-circle-arrow-right{background-position:-240px -144px}.fuelux .icon-circle-arrow-left{background-position:-264px -144px}.fuelux .icon-circle-arrow-up{background-position:-288px -144px}.fuelux .icon-circle-arrow-down{background-position:-312px -144px}.fuelux .icon-globe{background-position:-336px -144px}.fuelux .icon-wrench{background-position:-360px -144px}.fuelux .icon-tasks{background-position:-384px -144px}.fuelux .icon-filter{background-position:-408px -144px}.fuelux .icon-briefcase{background-position:-432px -144px}.fuelux .icon-fullscreen{background-position:-456px -144px}.fuelux .dropup,.fuelux .dropdown{position:relative}.fuelux .dropdown-toggle{*margin-bottom:-3px}.fuelux .dropdown-toggle:active,.fuelux .open .dropdown-toggle{outline:0}.fuelux .caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.fuelux .dropdown .caret{margin-top:8px;margin-left:2px}.fuelux .dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.fuelux .dropdown-menu.pull-right{right:0;left:auto}.fuelux .dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.fuelux .dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.fuelux .dropdown-menu>li>a:hover,.fuelux .dropdown-menu>li>a:focus,.fuelux .dropdown-submenu:hover>a,.fuelux .dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.fuelux .dropdown-menu>.active>a,.fuelux .dropdown-menu>.active>a:hover,.fuelux .dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.fuelux .dropdown-menu>.disabled>a,.fuelux .dropdown-menu>.disabled>a:hover,.fuelux .dropdown-menu>.disabled>a:focus{color:#999}.fuelux .dropdown-menu>.disabled>a:hover,.fuelux .dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .open{*z-index:1000}.fuelux .open>.dropdown-menu{display:block}.fuelux .dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.fuelux .pull-right>.dropdown-menu{right:0;left:auto}.fuelux .dropup .caret,.fuelux .navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.fuelux .dropup .dropdown-menu,.fuelux .navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.fuelux .dropdown-submenu{position:relative}.fuelux .dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.fuelux .dropdown-submenu:hover>.dropdown-menu{display:block}.fuelux .dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.fuelux .dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.fuelux .dropdown-submenu:hover>a:after{border-left-color:#fff}.fuelux .dropdown-submenu.pull-left{float:none}.fuelux .dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.fuelux .dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.fuelux .typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux .well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.fuelux .well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.fuelux .well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fuelux .fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fuelux .fade.in{opacity:1}.fuelux .collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.fuelux .collapse.in{height:auto}.fuelux .close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.fuelux .close:hover,.fuelux .close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}.fuelux button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.fuelux .btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.fuelux .btn:hover,.fuelux .btn:focus,.fuelux .btn:active,.fuelux .btn.active,.fuelux .btn.disabled,.fuelux .btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.fuelux .btn:active,.fuelux .btn.active{background-color:#ccc \9}.fuelux .btn:first-child{*margin-left:0}.fuelux .btn:hover,.fuelux .btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fuelux .btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.fuelux .btn.active,.fuelux .btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.fuelux .btn.disabled,.fuelux .btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.fuelux .btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .btn-large [class^="icon-"],.fuelux .btn-large [class*=" icon-"]{margin-top:4px}.fuelux .btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fuelux .btn-small [class^="icon-"],.fuelux .btn-small [class*=" icon-"]{margin-top:0}.fuelux .btn-mini [class^="icon-"],.fuelux .btn-mini [class*=" icon-"]{margin-top:-1px}.fuelux .btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fuelux .btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fuelux .btn-block+.btn-block{margin-top:5px}.fuelux input[type="submit"].btn-block,.fuelux input[type="reset"].btn-block,.fuelux input[type="button"].btn-block{width:100%}.fuelux .btn-primary.active,.fuelux .btn-warning.active,.fuelux .btn-danger.active,.fuelux .btn-success.active,.fuelux .btn-info.active,.fuelux .btn-inverse.active{color:rgba(255,255,255,0.75)}.fuelux .btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-image:-moz-linear-gradient(top,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .btn-primary:hover,.fuelux .btn-primary:focus,.fuelux .btn-primary:active,.fuelux .btn-primary.active,.fuelux .btn-primary.disabled,.fuelux .btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.fuelux .btn-primary:active,.fuelux .btn-primary.active{background-color:#039 \9}.fuelux .btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .btn-warning:hover,.fuelux .btn-warning:focus,.fuelux .btn-warning:active,.fuelux .btn-warning.active,.fuelux .btn-warning.disabled,.fuelux .btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.fuelux .btn-warning:active,.fuelux .btn-warning.active{background-color:#c67605 \9}.fuelux .btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .btn-danger:hover,.fuelux .btn-danger:focus,.fuelux .btn-danger:active,.fuelux .btn-danger.active,.fuelux .btn-danger.disabled,.fuelux .btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.fuelux .btn-danger:active,.fuelux .btn-danger.active{background-color:#942a25 \9}.fuelux .btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-image:-moz-linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .btn-success:hover,.fuelux .btn-success:focus,.fuelux .btn-success:active,.fuelux .btn-success.active,.fuelux .btn-success.disabled,.fuelux .btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.fuelux .btn-success:active,.fuelux .btn-success.active{background-color:#408140 \9}.fuelux .btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .btn-info:hover,.fuelux .btn-info:focus,.fuelux .btn-info:active,.fuelux .btn-info.active,.fuelux .btn-info.disabled,.fuelux .btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.fuelux .btn-info:active,.fuelux .btn-info.active{background-color:#24748c \9}.fuelux .btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-image:-moz-linear-gradient(top,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .btn-inverse:hover,.fuelux .btn-inverse:focus,.fuelux .btn-inverse:active,.fuelux .btn-inverse.active,.fuelux .btn-inverse.disabled,.fuelux .btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.fuelux .btn-inverse:active,.fuelux .btn-inverse.active{background-color:#080808 \9}.fuelux button.btn,.fuelux input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}.fuelux button.btn::-moz-focus-inner,.fuelux input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}.fuelux button.btn.btn-large,.fuelux input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}.fuelux button.btn.btn-small,.fuelux input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}.fuelux button.btn.btn-mini,.fuelux input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.fuelux .btn-link,.fuelux .btn-link:active,.fuelux .btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.fuelux .btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .btn-link:hover,.fuelux .btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}.fuelux .btn-link[disabled]:hover,.fuelux .btn-link[disabled]:focus{color:#333;text-decoration:none}.fuelux .btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.fuelux .btn-group:first-child{*margin-left:0}.fuelux .btn-group+.btn-group{margin-left:5px}.fuelux .btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.fuelux .btn-toolbar>.btn+.btn,.fuelux .btn-toolbar>.btn-group+.btn,.fuelux .btn-toolbar>.btn+.btn-group{margin-left:5px}.fuelux .btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .btn-group>.btn+.btn{margin-left:-1px}.fuelux .btn-group>.btn,.fuelux .btn-group>.dropdown-menu,.fuelux .btn-group>.popover{font-size:14px}.fuelux .btn-group>.btn-mini{font-size:10.5px}.fuelux .btn-group>.btn-small{font-size:11.9px}.fuelux .btn-group>.btn-large{font-size:17.5px}.fuelux .btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.fuelux .btn-group>.btn:last-child,.fuelux .btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.fuelux .btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.fuelux .btn-group>.btn.large:last-child,.fuelux .btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.fuelux .btn-group>.btn:hover,.fuelux .btn-group>.btn:focus,.fuelux .btn-group>.btn:active,.fuelux .btn-group>.btn.active{z-index:2}.fuelux .btn-group .dropdown-toggle:active,.fuelux .btn-group.open .dropdown-toggle{outline:0}.fuelux .btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.fuelux .btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.fuelux .btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.fuelux .btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.fuelux .btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.fuelux .btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.fuelux .btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.fuelux .btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.fuelux .btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.fuelux .btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.fuelux .btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.fuelux .btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.fuelux .btn .caret{margin-top:8px;margin-left:0}.fuelux .btn-large .caret{margin-top:6px}.fuelux .btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.fuelux .btn-mini .caret,.fuelux .btn-small .caret{margin-top:8px}.fuelux .dropup .btn-large .caret{border-bottom-width:5px}.fuelux .btn-primary .caret,.fuelux .btn-warning .caret,.fuelux .btn-danger .caret,.fuelux .btn-info .caret,.fuelux .btn-success .caret,.fuelux .btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.fuelux .btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.fuelux .btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.fuelux .btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.fuelux .btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.fuelux .btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.fuelux .btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.fuelux .alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux .alert,.fuelux .alert h4{color:#c09853}.fuelux .alert h4{margin:0}.fuelux .alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.fuelux .alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.fuelux .alert-success h4{color:#468847}.fuelux .alert-danger,.fuelux .alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.fuelux .alert-danger h4,.fuelux .alert-error h4{color:#b94a48}.fuelux .alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.fuelux .alert-info h4{color:#3a87ad}.fuelux .alert-block{padding-top:14px;padding-bottom:14px}.fuelux .alert-block>p,.fuelux .alert-block>ul{margin-bottom:0}.fuelux .alert-block p+p{margin-top:5px}.fuelux .nav{margin-bottom:20px;margin-left:0;list-style:none}.fuelux .nav>li>a{display:block}.fuelux .nav>li>a:hover,.fuelux .nav>li>a:focus{text-decoration:none;background-color:#eee}.fuelux .nav>li>a>img{max-width:none}.fuelux .nav>.pull-right{float:right}.fuelux .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.fuelux .nav li+.nav-header{margin-top:9px}.fuelux .nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.fuelux .nav-list>li>a,.fuelux .nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.fuelux .nav-list>li>a{padding:3px 15px}.fuelux .nav-list>.active>a,.fuelux .nav-list>.active>a:hover,.fuelux .nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.fuelux .nav-list [class^="icon-"],.fuelux .nav-list [class*=" icon-"]{margin-right:2px}.fuelux .nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.fuelux .nav-tabs,.fuelux .nav-pills{*zoom:1}.fuelux .nav-tabs:before,.fuelux .nav-pills:before,.fuelux .nav-tabs:after,.fuelux .nav-pills:after{display:table;line-height:0;content:""}.fuelux .nav-tabs:after,.fuelux .nav-pills:after{clear:both}.fuelux .nav-tabs>li,.fuelux .nav-pills>li{float:left}.fuelux .nav-tabs>li>a,.fuelux .nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.fuelux .nav-tabs{border-bottom:1px solid #ddd}.fuelux .nav-tabs>li{margin-bottom:-1px}.fuelux .nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.fuelux .nav-tabs>li>a:hover,.fuelux .nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.fuelux .nav-tabs>.active>a,.fuelux .nav-tabs>.active>a:hover,.fuelux .nav-tabs>.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.fuelux .nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.fuelux .nav-pills>.active>a,.fuelux .nav-pills>.active>a:hover,.fuelux .nav-pills>.active>a:focus{color:#fff;background-color:#08c}.fuelux .nav-stacked>li{float:none}.fuelux .nav-stacked>li>a{margin-right:0}.fuelux .nav-tabs.nav-stacked{border-bottom:0}.fuelux .nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.fuelux .nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.fuelux .nav-tabs.nav-stacked>li>a:hover,.fuelux .nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.fuelux .nav-pills.nav-stacked>li>a{margin-bottom:3px}.fuelux .nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.fuelux .nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.fuelux .nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.fuelux .nav .dropdown-toggle:hover .caret,.fuelux .nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}.fuelux .nav-tabs .dropdown-toggle .caret{margin-top:8px}.fuelux .nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.fuelux .nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.fuelux .nav>.dropdown.active>a:hover,.fuelux .nav>.dropdown.active>a:focus{cursor:pointer}.fuelux .nav-tabs .open .dropdown-toggle,.fuelux .nav-pills .open .dropdown-toggle,.fuelux .nav>li.dropdown.open.active>a:hover,.fuelux .nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}.fuelux .nav li.dropdown.open .caret,.fuelux .nav li.dropdown.open.active .caret,.fuelux .nav li.dropdown.open a:hover .caret,.fuelux .nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.fuelux .tabs-stacked .open>a:hover,.fuelux .tabs-stacked .open>a:focus{border-color:#999}.fuelux .tabbable{*zoom:1}.fuelux .tabbable:before,.fuelux .tabbable:after{display:table;line-height:0;content:""}.fuelux .tabbable:after{clear:both}.fuelux .tab-content{overflow:auto}.fuelux .tabs-below>.nav-tabs,.fuelux .tabs-right>.nav-tabs,.fuelux .tabs-left>.nav-tabs{border-bottom:0}.fuelux .tab-content>.tab-pane,.fuelux .pill-content>.pill-pane{display:none}.fuelux .tab-content>.active,.fuelux .pill-content>.active{display:block}.fuelux .tabs-below>.nav-tabs{border-top:1px solid #ddd}.fuelux .tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.fuelux .tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.fuelux .tabs-below>.nav-tabs>li>a:hover,.fuelux .tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.fuelux .tabs-below>.nav-tabs>.active>a,.fuelux .tabs-below>.nav-tabs>.active>a:hover,.fuelux .tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.fuelux .tabs-left>.nav-tabs>li,.fuelux .tabs-right>.nav-tabs>li{float:none}.fuelux .tabs-left>.nav-tabs>li>a,.fuelux .tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.fuelux .tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.fuelux .tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.fuelux .tabs-left>.nav-tabs>li>a:hover,.fuelux .tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.fuelux .tabs-left>.nav-tabs .active>a,.fuelux .tabs-left>.nav-tabs .active>a:hover,.fuelux .tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.fuelux .tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.fuelux .tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.fuelux .tabs-right>.nav-tabs>li>a:hover,.fuelux .tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.fuelux .tabs-right>.nav-tabs .active>a,.fuelux .tabs-right>.nav-tabs .active>a:hover,.fuelux .tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.fuelux .nav>.disabled>a{color:#999}.fuelux .nav>.disabled>a:hover,.fuelux .nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.fuelux .navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.fuelux .navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.fuelux .navbar-inner:before,.fuelux .navbar-inner:after{display:table;line-height:0;content:""}.fuelux .navbar-inner:after{clear:both}.fuelux .navbar .container{width:auto}.fuelux .nav-collapse.collapse{height:auto;overflow:visible}.fuelux .navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.fuelux .navbar .brand:hover,.fuelux .navbar .brand:focus{text-decoration:none}.fuelux .navbar-text{margin-bottom:0;line-height:40px;color:#777}.fuelux .navbar-link{color:#777}.fuelux .navbar-link:hover,.fuelux .navbar-link:focus{color:#333}.fuelux .navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.fuelux .navbar .btn,.fuelux .navbar .btn-group{margin-top:5px}.fuelux .navbar .btn-group .btn,.fuelux .navbar .input-prepend .btn,.fuelux .navbar .input-append .btn,.fuelux .navbar .input-prepend .btn-group,.fuelux .navbar .input-append .btn-group{margin-top:0}.fuelux .navbar-form{margin-bottom:0;*zoom:1}.fuelux .navbar-form:before,.fuelux .navbar-form:after{display:table;line-height:0;content:""}.fuelux .navbar-form:after{clear:both}.fuelux .navbar-form input,.fuelux .navbar-form select,.fuelux .navbar-form .radio,.fuelux .navbar-form .checkbox{margin-top:5px}.fuelux .navbar-form input,.fuelux .navbar-form select,.fuelux .navbar-form .btn{display:inline-block;margin-bottom:0}.fuelux .navbar-form input[type="image"],.fuelux .navbar-form input[type="checkbox"],.fuelux .navbar-form input[type="radio"]{margin-top:3px}.fuelux .navbar-form .input-append,.fuelux .navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.fuelux .navbar-form .input-append input,.fuelux .navbar-form .input-prepend input{margin-top:0}.fuelux .navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.fuelux .navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.fuelux .navbar-static-top{position:static;margin-bottom:0}.fuelux .navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .navbar-fixed-top,.fuelux .navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.fuelux .navbar-fixed-top .navbar-inner,.fuelux .navbar-static-top .navbar-inner{border-width:0 0 1px}.fuelux .navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.fuelux .navbar-fixed-top .navbar-inner,.fuelux .navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .navbar-static-top .container,.fuelux .navbar-fixed-top .container,.fuelux .navbar-fixed-bottom .container{width:940px}.fuelux .navbar-fixed-top{top:0}.fuelux .navbar-fixed-top .navbar-inner,.fuelux .navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.fuelux .navbar-fixed-bottom{bottom:0}.fuelux .navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.fuelux .navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.fuelux .navbar .nav.pull-right{float:right;margin-right:0}.fuelux .navbar .nav>li{float:left}.fuelux .navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.fuelux .navbar .nav .dropdown-toggle .caret{margin-top:8px}.fuelux .navbar .nav>li>a:focus,.fuelux .navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.fuelux .navbar .nav>.active>a,.fuelux .navbar .nav>.active>a:hover,.fuelux .navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.fuelux .navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.fuelux .navbar .btn-navbar:hover,.fuelux .navbar .btn-navbar:focus,.fuelux .navbar .btn-navbar:active,.fuelux .navbar .btn-navbar.active,.fuelux .navbar .btn-navbar.disabled,.fuelux .navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.fuelux .navbar .btn-navbar:active,.fuelux .navbar .btn-navbar.active{background-color:#ccc \9}.fuelux .navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.fuelux .btn-navbar .icon-bar+.icon-bar{margin-top:3px}.fuelux .navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.fuelux .navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.fuelux .navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.fuelux .navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.fuelux .navbar .nav li.dropdown>a:hover .caret,.fuelux .navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.fuelux .navbar .nav li.dropdown.open>.dropdown-toggle,.fuelux .navbar .nav li.dropdown.active>.dropdown-toggle,.fuelux .navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.fuelux .navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.fuelux .navbar .nav li.dropdown.open>.dropdown-toggle .caret,.fuelux .navbar .nav li.dropdown.active>.dropdown-toggle .caret,.fuelux .navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.fuelux .navbar .pull-right>li>.dropdown-menu,.fuelux .navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.fuelux .navbar .pull-right>li>.dropdown-menu:before,.fuelux .navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.fuelux .navbar .pull-right>li>.dropdown-menu:after,.fuelux .navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.fuelux .navbar .pull-right>li>.dropdown-menu .dropdown-menu,.fuelux .navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.fuelux .navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.fuelux .navbar-inverse .brand,.fuelux .navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.fuelux .navbar-inverse .brand:hover,.fuelux .navbar-inverse .nav>li>a:hover,.fuelux .navbar-inverse .brand:focus,.fuelux .navbar-inverse .nav>li>a:focus{color:#fff}.fuelux .navbar-inverse .brand{color:#999}.fuelux .navbar-inverse .navbar-text{color:#999}.fuelux .navbar-inverse .nav>li>a:focus,.fuelux .navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.fuelux .navbar-inverse .nav .active>a,.fuelux .navbar-inverse .nav .active>a:hover,.fuelux .navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.fuelux .navbar-inverse .navbar-link{color:#999}.fuelux .navbar-inverse .navbar-link:hover,.fuelux .navbar-inverse .navbar-link:focus{color:#fff}.fuelux .navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.fuelux .navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.fuelux .navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.fuelux .navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.fuelux .navbar-inverse .nav li.dropdown>a:hover .caret,.fuelux .navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.fuelux .navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.fuelux .navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.fuelux .navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.fuelux .navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.fuelux .navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.fuelux .navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.fuelux .navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.fuelux .navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.fuelux .navbar-inverse .navbar-search .search-query:focus,.fuelux .navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.fuelux .navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-image:-moz-linear-gradient(top,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.fuelux .navbar-inverse .btn-navbar:hover,.fuelux .navbar-inverse .btn-navbar:focus,.fuelux .navbar-inverse .btn-navbar:active,.fuelux .navbar-inverse .btn-navbar.active,.fuelux .navbar-inverse .btn-navbar.disabled,.fuelux .navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.fuelux .navbar-inverse .btn-navbar:active,.fuelux .navbar-inverse .btn-navbar.active{background-color:#000 \9}.fuelux .breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux .breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.fuelux .breadcrumb>li>.divider{padding:0 5px;color:#ccc}.fuelux .breadcrumb>.active{color:#999}.fuelux .pagination{margin:20px 0}.fuelux .pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.fuelux .pagination ul>li{display:inline}.fuelux .pagination ul>li>a,.fuelux .pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.fuelux .pagination ul>li>a:hover,.fuelux .pagination ul>li>a:focus,.fuelux .pagination ul>.active>a,.fuelux .pagination ul>.active>span{background-color:#f5f5f5}.fuelux .pagination ul>.active>a,.fuelux .pagination ul>.active>span{color:#999;cursor:default}.fuelux .pagination ul>.disabled>span,.fuelux .pagination ul>.disabled>a,.fuelux .pagination ul>.disabled>a:hover,.fuelux .pagination ul>.disabled>a:focus{color:#999;cursor:default;background-color:transparent}.fuelux .pagination ul>li:first-child>a,.fuelux .pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.fuelux .pagination ul>li:last-child>a,.fuelux .pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.fuelux .pagination-centered{text-align:center}.fuelux .pagination-right{text-align:right}.fuelux .pagination-large ul>li>a,.fuelux .pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.fuelux .pagination-large ul>li:first-child>a,.fuelux .pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.fuelux .pagination-large ul>li:last-child>a,.fuelux .pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.fuelux .pagination-mini ul>li:first-child>a,.fuelux .pagination-small ul>li:first-child>a,.fuelux .pagination-mini ul>li:first-child>span,.fuelux .pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.fuelux .pagination-mini ul>li:last-child>a,.fuelux .pagination-small ul>li:last-child>a,.fuelux .pagination-mini ul>li:last-child>span,.fuelux .pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.fuelux .pagination-small ul>li>a,.fuelux .pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.fuelux .pagination-mini ul>li>a,.fuelux .pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.fuelux .pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.fuelux .pager:before,.fuelux .pager:after{display:table;line-height:0;content:""}.fuelux .pager:after{clear:both}.fuelux .pager li{display:inline}.fuelux .pager li>a,.fuelux .pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.fuelux .pager li>a:hover,.fuelux .pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.fuelux .pager .next>a,.fuelux .pager .next>span{float:right}.fuelux .pager .previous>a,.fuelux .pager .previous>span{float:left}.fuelux .pager .disabled>a,.fuelux .pager .disabled>a:hover,.fuelux .pager .disabled>a:focus,.fuelux .pager .disabled>span{color:#999;cursor:default;background-color:#fff}.fuelux .modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.fuelux .modal-backdrop.fade{opacity:0}.fuelux .modal-backdrop,.fuelux .modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.fuelux .modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.fuelux .modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.fuelux .modal.fade.in{top:10%}.fuelux .modal-header{padding:9px 15px;border-bottom:1px solid #eee}.fuelux .modal-header .close{margin-top:2px}.fuelux .modal-header h3{margin:0;line-height:30px}.fuelux .modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.fuelux .modal-form{margin-bottom:0}.fuelux .modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.fuelux .modal-footer:before,.fuelux .modal-footer:after{display:table;line-height:0;content:""}.fuelux .modal-footer:after{clear:both}.fuelux .modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.fuelux .modal-footer .btn-group .btn+.btn{margin-left:-1px}.fuelux .modal-footer .btn-block+.btn-block{margin-left:0}.fuelux .tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.fuelux .tooltip.in{opacity:.8;filter:alpha(opacity=80)}.fuelux .tooltip.top{padding:5px 0;margin-top:-3px}.fuelux .tooltip.right{padding:0 5px;margin-left:3px}.fuelux .tooltip.bottom{padding:5px 0;margin-top:3px}.fuelux .tooltip.left{padding:0 5px;margin-left:-3px}.fuelux .tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux .tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.fuelux .tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.fuelux .tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.fuelux .tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.fuelux .tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.fuelux .popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.fuelux .popover.top{margin-top:-10px}.fuelux .popover.right{margin-left:10px}.fuelux .popover.bottom{margin-top:10px}.fuelux .popover.left{margin-left:-10px}.fuelux .popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.fuelux .popover-title:empty{display:none}.fuelux .popover-content{padding:9px 14px}.fuelux .popover .arrow,.fuelux .popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.fuelux .popover .arrow{border-width:11px}.fuelux .popover .arrow:after{border-width:10px;content:""}.fuelux .popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.fuelux .popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.fuelux .popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.fuelux .popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.fuelux .popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.fuelux .popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.fuelux .popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.fuelux .popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.fuelux .thumbnails{margin-left:-20px;list-style:none;*zoom:1}.fuelux .thumbnails:before,.fuelux .thumbnails:after{display:table;line-height:0;content:""}.fuelux .thumbnails:after{clear:both}.fuelux .row-fluid .thumbnails{margin-left:0}.fuelux .thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.fuelux .thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.fuelux a.thumbnail:hover,.fuelux a.thumbnail:focus{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.fuelux .thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.fuelux .thumbnail .caption{padding:9px;color:#555}.fuelux .media,.fuelux .media-body{overflow:hidden;*overflow:visible;zoom:1}.fuelux .media,.fuelux .media .media{margin-top:15px}.fuelux .media:first-child{margin-top:0}.fuelux .media-object{display:block}.fuelux .media-heading{margin:0 0 5px}.fuelux .media>.pull-left{margin-right:10px}.fuelux .media>.pull-right{margin-left:10px}.fuelux .media-list{margin-left:0;list-style:none}.fuelux .label,.fuelux .badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.fuelux .label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fuelux .badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.fuelux .label:empty,.fuelux .badge:empty{display:none}.fuelux a.label:hover,.fuelux a.label:focus,.fuelux a.badge:hover,.fuelux a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.fuelux .label-important,.fuelux .badge-important{background-color:#b94a48}.fuelux .label-important[href],.fuelux .badge-important[href]{background-color:#953b39}.fuelux .label-warning,.fuelux .badge-warning{background-color:#f89406}.fuelux .label-warning[href],.fuelux .badge-warning[href]{background-color:#c67605}.fuelux .label-success,.fuelux .badge-success{background-color:#468847}.fuelux .label-success[href],.fuelux .badge-success[href]{background-color:#356635}.fuelux .label-info,.fuelux .badge-info{background-color:#3a87ad}.fuelux .label-info[href],.fuelux .badge-info[href]{background-color:#2d6987}.fuelux .label-inverse,.fuelux .badge-inverse{background-color:#333}.fuelux .label-inverse[href],.fuelux .badge-inverse[href]{background-color:#1a1a1a}.fuelux .btn .label,.fuelux .btn .badge{position:relative;top:-1px}.fuelux .btn-mini .label,.fuelux .btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.fuelux .progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.fuelux .progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.fuelux .progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.fuelux .progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.fuelux .progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.fuelux .progress-danger .bar,.fuelux .progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.fuelux .progress-danger.progress-striped .bar,.fuelux .progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.fuelux .progress-success .bar,.fuelux .progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.fuelux .progress-success.progress-striped .bar,.fuelux .progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.fuelux .progress-info .bar,.fuelux .progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.fuelux .progress-info.progress-striped .bar,.fuelux .progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.fuelux .progress-warning .bar,.fuelux .progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.fuelux .progress-warning.progress-striped .bar,.fuelux .progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.fuelux .accordion{margin-bottom:20px}.fuelux .accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fuelux .accordion-heading{border-bottom:0}.fuelux .accordion-heading .accordion-toggle{display:block;padding:8px 15px}.fuelux .accordion-toggle{cursor:pointer}.fuelux .accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.fuelux .carousel{position:relative;margin-bottom:20px;line-height:1}.fuelux .carousel-inner{position:relative;width:100%;overflow:hidden}.fuelux .carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.fuelux .carousel-inner>.item>img,.fuelux .carousel-inner>.item>a>img{display:block;line-height:1}.fuelux .carousel-inner>.active,.fuelux .carousel-inner>.next,.fuelux .carousel-inner>.prev{display:block}.fuelux .carousel-inner>.active{left:0}.fuelux .carousel-inner>.next,.fuelux .carousel-inner>.prev{position:absolute;top:0;width:100%}.fuelux .carousel-inner>.next{left:100%}.fuelux .carousel-inner>.prev{left:-100%}.fuelux .carousel-inner>.next.left,.fuelux .carousel-inner>.prev.right{left:0}.fuelux .carousel-inner>.active.left{left:-100%}.fuelux .carousel-inner>.active.right{left:100%}.fuelux .carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.fuelux .carousel-control.right{right:15px;left:auto}.fuelux .carousel-control:hover,.fuelux .carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.fuelux .carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.fuelux .carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.fuelux .carousel-indicators .active{background-color:#fff}.fuelux .carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.fuelux .carousel-caption h4,.fuelux .carousel-caption p{line-height:20px;color:#fff}.fuelux .carousel-caption h4{margin:0 0 5px}.fuelux .carousel-caption p{margin-bottom:0}.fuelux .hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.fuelux .hero-unit li{line-height:30px}.fuelux .pull-right{float:right}.fuelux .pull-left{float:left}.fuelux .hide{display:none}.fuelux .show{display:block}.fuelux .invisible{visibility:hidden}.fuelux .affix{position:fixed}.fuelux .form-inline .checkbox-custom{padding-left:20px}.fuelux .form-inline .checkbox-custom .checkbox{padding-left:16px}.fuelux .checkbox-custom input[type=checkbox]{position:relative;top:-99999px}.fuelux .checkbox-custom i{width:16px;height:16px;padding-left:16px;margin-right:4px;margin-left:-20px;background-image:url(../img/form.png);background-position:0 1px;background-repeat:no-repeat}.fuelux .checkbox-custom i.checked{background-position:-48px 1px}.fuelux .checkbox-custom i.disabled{background-position:-64px 1px}.fuelux .checkbox-custom i.disabled.checked{background-position:-80px 1px}.fuelux .checkbox-custom:hover i{background-position:-16px 1px}.fuelux .checkbox-custom:hover i.checked{background-position:-32px 1px}.fuelux .checkbox-custom:hover i.disabled{background-position:-64px 1px}.fuelux .checkbox-custom:hover i.disabled.checked{background-position:-80px 1px}.fuelux .combobox{display:inline-block}.fuelux .combobox a{font-size:14px}.fuelux .combobox button.btn{border-radius:0 4px 4px 0}.fuelux .datagrid thead{background-color:#f9f9f9}.fuelux .datagrid thead .datagrid-header-title{float:left;margin-right:10px;font-size:14px;font-weight:normal;line-height:28px}.fuelux .datagrid thead .datagrid-header-left{float:left}.fuelux .datagrid thead .datagrid-header-right{float:right}.fuelux .datagrid thead .datagrid-header-right .search,.fuelux .datagrid thead .datagrid-header-left .search,.fuelux .datagrid thead .datagrid-header-right .filter,.fuelux .datagrid thead .datagrid-header-left .filter{margin-bottom:0;margin-left:8px}.fuelux .datagrid thead .datagrid-header-right .search .dropdown-menu,.fuelux .datagrid thead .datagrid-header-left .search .dropdown-menu,.fuelux .datagrid thead .datagrid-header-right .filter .dropdown-menu,.fuelux .datagrid thead .datagrid-header-left .filter .dropdown-menu{top:auto;left:auto}.fuelux .datagrid thead .sorted{padding-right:30px;color:#333;text-shadow:'none';background-color:#f1f1f1;background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f9f9),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f9f9f9,#e5e5e5);background-image:-o-linear-gradient(top,#f9f9f9,#e5e5e5);background-image:-moz-linear-gradient(top,#f9f9f9,#e5e5e5);background-image:linear-gradient(to bottom,#f9f9f9,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bebebe;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#ffe5e5e5',GradientType=0)}.fuelux .datagrid thead .sorted i{float:right;margin-top:2px;margin-right:-22px}.fuelux .datagrid thead .sortable{cursor:pointer}.fuelux .datagrid thead .sortable:hover{color:#333;text-shadow:'none';background-color:#f1f1f1;background-image:-moz-linear-gradient(top,#f9f9f9,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f9f9f9),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f9f9f9,#e5e5e5);background-image:-o-linear-gradient(top,#f9f9f9,#e5e5e5);background-image:linear-gradient(to bottom,#f9f9f9,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bebebe;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#ffe5e5e5',GradientType=0)}.fuelux .datagrid tfoot{background-color:#f9f9f9}.fuelux .datagrid tfoot .datagrid-footer-left{float:left}.fuelux .datagrid tfoot .datagrid-footer-left .grid-controls{margin-top:7px}.fuelux .datagrid tfoot .datagrid-footer-left .grid-controls select{width:60px;margin:0 5px 1px}.fuelux .datagrid tfoot .datagrid-footer-left .grid-controls .grid-pagesize{display:inline-block;margin-bottom:5px;vertical-align:middle}.fuelux .datagrid tfoot .datagrid-footer-left .grid-controls .grid-pagesize .dropdown-menu{top:auto;left:auto}.fuelux .datagrid tfoot .datagrid-footer-left .grid-controls span{font-weight:normal}.fuelux .datagrid tfoot .datagrid-footer-right{float:right}.fuelux .datagrid tfoot .datagrid-footer-right .grid-pager>span{position:relative;top:8px;font-weight:normal}.fuelux .datagrid tfoot .datagrid-footer-right .grid-pager .dropdown-menu{min-width:50px}.fuelux .datagrid tfoot .datagrid-footer-right .grid-pager .combobox{position:relative;top:-2px;display:inline-block;margin-bottom:4px;vertical-align:baseline}.fuelux .datagrid tfoot .datagrid-footer-right .grid-pager>button{position:relative;top:7px}.fuelux .datagrid-stretch-header{margin-bottom:0;border-bottom:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0}.fuelux .datagrid-stretch-header thead:last-child tr:last-child>th:first-child,.fuelux .datagrid-stretch-header thead:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0}.fuelux .datagrid-stretch-wrapper{overflow:auto;border:1px solid #ddd}.fuelux .datagrid-stretch-wrapper .datagrid{margin-bottom:0;border:0;border-collapse:collapse;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .datagrid-stretch-wrapper .datagrid td,.fuelux .datagrid-stretch-wrapper .datagrid th{border-bottom:1px solid #ddd}.fuelux .datagrid-stretch-wrapper .datagrid td:first-child,.fuelux .datagrid-stretch-wrapper .datagrid th:first-child{border-left:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.fuelux .datagrid-stretch-footer{border-top:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-moz-border-radius-topleft:0}.fuelux .datagrid-stretch-footer th{border-top:0}.fuelux .pillbox{padding:3px}.fuelux .pillbox ul{display:inline-block;margin:0}.fuelux .pillbox li{display:inline-block;float:left;padding:1px 4px 2px;margin:2px;font-size:11.844px;font-weight:bold;line-height:21px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);vertical-align:baseline;cursor:pointer;background-color:#999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fuelux .pillbox li:after{position:relative;top:-2px;float:right;padding-left:4px;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;content:" \00D7";opacity:.2;filter:alpha(opacity=20)}.fuelux .pillbox li:hover:after{opacity:.4;filter:alpha(opacity=40)}.fuelux .pillbox li.status-important{background-color:#b94a48}.fuelux .pillbox li.status-warning{background-color:#f89406}.fuelux .pillbox li.status-success{background-color:#468847}.fuelux .pillbox li.status-info{background-color:#3a87ad}.fuelux .radio-custom input[type=radio]{display:none}.fuelux .radio-custom i{width:16px;height:16px;padding-left:16px;margin-right:4px;margin-left:-20px;background-image:url(../img/form.png);background-position:0 -15px;background-repeat:no-repeat}.fuelux .radio-custom i.checked{background-position:-48px -15px}.fuelux .radio-custom i.disabled{background-position:-64px -15px}.fuelux .radio-custom i.disabled.checked{background-position:-80px -15px}.fuelux .radio-custom:hover i{background-position:-16px -15px}.fuelux .radio-custom:hover i.checked{background-position:-32px -15px}.fuelux .radio-custom:hover i.disabled{background-position:-64px -15px}.fuelux .radio-custom:hover i.disabled.checked{background-position:-80px -15px}.fuelux .spinner input{float:left;width:43px}.fuelux .spinner .btn{position:relative;width:20px;height:14px;padding-top:0;padding-right:9px;padding-left:9px}.fuelux .spinner .btn.disabled{cursor:not-allowed}.fuelux .spinner .spinner-buttons{position:relative;left:-22px;float:left;width:20px;height:28px}.fuelux .spinner .spinner-up{top:2px;padding:0 0 4px 1px}.fuelux .spinner .spinner-up i{position:relative;top:-4px}.fuelux .spinner .spinner-down{top:2px;height:13px;padding:0 0 4px 1px}.fuelux .spinner .spinner-down i{position:relative;top:-5px}.fuelux .search{display:inline-block}.fuelux .select .dropdown-label{display:inline-block;padding:0 10px 0 0;margin:0;font-weight:normal;color:#333;text-align:left}.fuelux #selectTextSize{position:absolute;top:0;display:inline-block;visibility:hidden}.fuelux .tree{position:relative;padding:10px 15px 0 15px;overflow-x:hidden;overflow-y:auto;border:1px solid #bbb;border-radius:4px 4px 4px 4px}.fuelux .tree .tree-folder{width:100%;min-height:20px;margin-top:1px;cursor:pointer}.fuelux .tree .tree-folder .tree-folder-header{position:relative;height:20px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .tree .tree-folder .tree-folder-header:hover{background-color:#dfeef5}.fuelux .tree .tree-folder .tree-folder-header i{position:absolute;top:1px;left:5px;float:left}.fuelux .tree .tree-folder .tree-folder-header .tree-folder-name{padding-left:29px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fuelux .tree .tree-folder .tree-folder-content{margin-left:23px}.fuelux .tree .tree-item{position:relative;width:100%;height:20px;margin-top:1px;cursor:pointer;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .tree .tree-item:hover{background-color:#dfeef5}.fuelux .tree .tree-item .tree-item-name{position:absolute;left:29px}.fuelux .tree .tree-item .tree-dot{position:absolute;top:8px;left:10px;display:block;width:4px;height:4px;background-color:#333;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.fuelux .tree .tree-item .icon-ok{position:absolute;top:1px;left:5px}.fuelux .tree .tree-selected{background-color:#b9dff1}.fuelux .tree .tree-selected:hover{background-color:#b9dff1}.fuelux .wizard{background-color:#f9f9f9;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.fuelux .wizard:before,.fuelux .wizard:after{display:table;line-height:0;content:""}.fuelux .wizard:after{clear:both}.fuelux .wizard ul{padding:0;margin:0;list-style:none outside none}.fuelux .wizard ul li{position:relative;float:left;height:46px;padding:0 20px 0 30px;margin:0;font-size:16px;line-height:46px;color:#999;cursor:default;background:#ededed}.fuelux .wizard ul li .chevron{position:absolute;top:0;right:-14px;display:block;border:24px solid transparent;border-right:0;border-left:14px solid #d4d4d4}.fuelux .wizard ul li .chevron:before{position:absolute;top:-24px;right:1px;display:block;border:24px solid transparent;border-right:0;border-left:14px solid #ededed;content:""}.fuelux .wizard ul li.complete{color:#468847;background:#f3f4f5}.fuelux .wizard ul li.complete:hover{cursor:pointer;background:#e7eff8}.fuelux .wizard ul li.complete:hover .chevron:before{border-left:14px solid #e7eff8}.fuelux .wizard ul li.complete .chevron:before{border-left:14px solid #f3f4f5}.fuelux .wizard ul li.active{color:#3a87ad;background:#f1f6fc}.fuelux .wizard ul li.active .chevron:before{border-left:14px solid #f1f6fc}.fuelux .wizard ul li .badge{margin-right:8px}.fuelux .wizard ul li:nth-child(1){z-index:10;padding-left:20px;border-radius:4px 0 0 4px}.fuelux .wizard ul li:nth-child(2){z-index:9}.fuelux .wizard ul li:nth-child(3){z-index:8}.fuelux .wizard ul li:nth-child(4){z-index:7}.fuelux .wizard ul li:nth-child(5){z-index:6}.fuelux .wizard ul li:nth-child(6){z-index:5}.fuelux .wizard ul li:nth-child(7){z-index:4}.fuelux .wizard ul li:nth-child(8){z-index:3}.fuelux .wizard ul li:nth-child(9){z-index:2}.fuelux .wizard ul li:nth-child(10){z-index:1}.fuelux .wizard .actions{float:right;padding-right:15px;line-height:44px;vertical-align:middle}.fuelux .wizard .actions a{margin-right:8px;font-size:12px;line-height:45px}.fuelux .wizard .actions .btn-prev i{margin-right:5px}.fuelux .wizard .actions .btn-next i{margin-left:5px}.fuelux .step-content .step-pane{display:none}.fuelux .step-content .active{display:block}.fuelux .step-content .active .btn-group .active{display:inline-block}
\ No newline at end of file
--- /dev/null
+.fuelux [class^="icon-"],
+.fuelux [class*=" icon-"] {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ margin-top: 1px;
+ *margin-right: .3em;
+ line-height: 14px;
+ vertical-align: text-top;
+ background-position: 14px 14px;
+ background-repeat: no-repeat;
+}
+
+.fuelux .icon-white,
+.fuelux .nav-pills > .active > a > [class^="icon-"],
+.fuelux .nav-pills > .active > a > [class*=" icon-"],
+.fuelux .nav-list > .active > a > [class^="icon-"],
+.fuelux .nav-list > .active > a > [class*=" icon-"],
+.fuelux .navbar-inverse .nav > .active > a > [class^="icon-"],
+.fuelux .navbar-inverse .nav > .active > a > [class*=" icon-"],
+.fuelux .dropdown-menu > li > a:hover > [class^="icon-"],
+.fuelux .dropdown-menu > li > a:focus > [class^="icon-"],
+.fuelux .dropdown-menu > li > a:hover > [class*=" icon-"],
+.fuelux .dropdown-menu > li > a:focus > [class*=" icon-"],
+.fuelux .dropdown-menu > .active > a > [class^="icon-"],
+.fuelux .dropdown-menu > .active > a > [class*=" icon-"],
+.fuelux .dropdown-submenu:hover > a > [class^="icon-"],
+.fuelux .dropdown-submenu:focus > a > [class^="icon-"],
+.fuelux .dropdown-submenu:hover > a > [class*=" icon-"],
+.fuelux .dropdown-submenu:focus > a > [class*=" icon-"] {
+}
+
+.fuelux .icon-search {
+ background:url('../img/search.png') 0 0 no-repeat;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+}
+.fuelux .icon-remove {
+ background:url('../img/searchremove.png') 0 0 no-repeat;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+}
+
+.fuelux .icon-chevron-up {
+ background:url('../img/sort_up.png') 0 0 no-repeat;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+}
+
+.fuelux .icon-chevron-down {
+ background:url('../img/sort_down.png') 0 0 no-repeat;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+}
+
+.fuelux .icon-chevron-left {
+ background:url('../img/nextpageleft.png') 0 0 no-repeat;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+}
+
+.fuelux .icon-chevron-right {
+ background:url('../img/nextpageright.png') 0 0 no-repeat;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+}
+
+.fuelux .icon-chevron-right {
+ background:url('../img/nextpageright.png') 0 0 no-repeat;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+}
+
--- /dev/null
+/*
+ * Fuel UX Data components - static data source
+ * https://github.com/ExactTarget/fuelux-data
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['underscore'], factory);
+ } else {
+ root.StaticDataSource = factory();
+ }
+}(this, function () {
+
+ var StaticDataSource = function (options) {
+ this._formatter = options.formatter;
+ this._columns = options.columns;
+ this._delay = options.delay || 0;
+ this._data = options.data;
+ };
+
+ StaticDataSource.prototype = {
+
+ columns: function () {
+ return this._columns;
+ },
+
+ data: function (options, callback) {
+ var self = this;
+
+ setTimeout(function () {
+ var data = $.extend(true, [], self._data);
+
+ // SEARCHING
+ if (options.search) {
+ data = _.filter(data, function (item) {
+ var match = false;
+
+ _.each(item, function (prop) {
+ if (_.isString(prop) || _.isFinite(prop)) {
+ if (prop.toString().toLowerCase().indexOf(options.search.toLowerCase()) !== -1) match = true;
+ }
+ });
+
+ return match;
+ });
+ }
+
+ // FILTERING
+ if (options.filter) {
+ data = _.filter(data, function (item) {
+ switch(options.filter.value) {
+ case 'lt5m':
+ if(item.population < 5000000) return true;
+ break;
+ case 'gte5m':
+ if(item.population >= 5000000) return true;
+ break;
+ default:
+ return true;
+ break;
+ }
+ });
+ }
+
+ var count = data.length;
+
+ // SORTING
+ if (options.sortProperty) {
+ data = _.sortBy(data, options.sortProperty);
+ if (options.sortDirection === 'desc') data.reverse();
+ }
+
+ // PAGING
+ var startIndex = options.pageIndex * options.pageSize;
+ var endIndex = startIndex + options.pageSize;
+ var end = (endIndex > count) ? count : endIndex;
+ var pages = Math.ceil(count / options.pageSize);
+ var page = options.pageIndex + 1;
+ var start = startIndex + 1;
+
+ data = data.slice(startIndex, endIndex);
+
+ if (self._formatter) self._formatter(data);
+
+ callback({ data: data, start: start, end: end, count: count, pages: pages, page: page });
+
+ }, this._delay)
+ }
+ };
+
+ return StaticDataSource;
+}));
--- /dev/null
+Copyright (C) 2012, ExactTarget, Inc.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in the
+Software without restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====================================================================
+
+The above license does not apply to the following bundled components:
+
+Â¥ jQuery located at lib/jquery.js
+Â¥ Bootstrap Apache 2.0 Located under lib/bootstrap
+Â¥ RequireJS located at lib/require.js
+Â¥ QUnit located under lib/qunit
+Â¥ Grunt located under node-modules/grunt
+Â¥ Grunt-contrib located under node-modules/grunt-contrib
+Â¥ Grunt-recess located under node-modules/grunt-recess
+
+Licensing information regarding the above packages can be found in the THIRD-PARTY file.
\ No newline at end of file
--- /dev/null
+/*! Fuel UX - v2.3.1 - 2013-08-02
+* https://github.com/ExactTarget/fuelux
+* Copyright (c) 2013 ExactTarget; Licensed MIT */
+(function(){(function(a){var b;define("bootstrap/bootstrap-transition",["jquery"],function(){return function(){!function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery)}.call(a),b})})(this),function(a){var b;define("bootstrap/bootstrap-affix",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.options=a.extend({},a.fn.affix.defaults,c),this.$window=a(window).on("scroll.affix.data-api",a.proxy(this.checkPosition,this)).on("click.affix.data-api",a.proxy(function(){setTimeout(a.proxy(this.checkPosition,this),1)},this)),this.$element=a(b),this.checkPosition()};b.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var b=a(document).height(),c=this.$window.scrollTop(),d=this.$element.offset(),e=this.options.offset,f=e.bottom,g=e.top,h="affix affix-top affix-bottom",i;typeof e!="object"&&(f=g=e),typeof g=="function"&&(g=e.top()),typeof f=="function"&&(f=e.bottom()),i=this.unpin!=null&&c+this.unpin<=d.top?!1:f!=null&&d.top+this.$element.height()>=b-f?"bottom":g!=null&&c<=g?"top":!1;if(this.affixed===i)return;this.affixed=i,this.unpin=i=="bottom"?d.top-c:null,this.$element.removeClass(h).addClass("affix"+(i?"-"+i:""))};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("affix"),f=typeof c=="object"&&c;e||d.data("affix",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.defaults={offset:0},a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-alert",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.alert.data-api",b,c.prototype.close)}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-button",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a=a+"Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-carousel",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(b){var c=this.getActiveIndex(),d=this;if(b>this.$items.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){d.to(b)}):c==b?this.pause().cycle():this.slide(b>c?"next":"prev",a(this.$items[b]))},pause:function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this,j;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h](),j=a.Event("slide",{relatedTarget:e[0],direction:g});if(e.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")}));if(a.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(j);if(j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})}else{this.$element.trigger(j);if(j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=a.extend({},a.fn.carousel.defaults,typeof c=="object"&&c),g=typeof c=="string"?c:f.slide;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),c.data()),g;e.carousel(f),(g=c.attr("data-slide-to"))&&e.data("carousel").pause().to(g).cycle(),b.preventDefault()})}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-collapse",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b,c,d,e;if(this.transitioning||this.$element.hasClass("in"))return;b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find("> .accordion-group > .in");if(d&&d.length){e=d.data("collapse");if(e&&e.transitioning)return;d.collapse("hide"),e||d.data("collapse",null)}this.$element[b](0),this.transition("addClass",a.Event("show"),"shown"),a.support.transition&&this.$element[b](this.$element[0][c])},hide:function(){var b;if(this.transitioning||!this.$element.hasClass("in"))return;b=this.dimension(),this.reset(this.$element[b]()),this.transition("removeClass",a.Event("hide"),"hidden"),this.$element[b](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a!==null?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c.type=="show"&&e.reset(),e.transitioning=0,e.$element.trigger(d)};this.$element.trigger(c);if(c.isDefaultPrevented())return;this.transitioning=1,this.$element[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=a.extend({},a.fn.collapse.defaults,d.data(),typeof c=="object"&&c);e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();c[a(e).hasClass("in")?"addClass":"removeClass"]("collapsed"),a(e).collapse(f)})}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-dropdown",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){function d(){a(".dropdown-backdrop").remove(),a(b).each(function(){e(a(this)).removeClass("open")})}function e(b){var c=b.attr("data-target"),d;c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,"")),d=c&&a(c);if(!d||!d.length)d=b.parent();return d}var b="[data-toggle=dropdown]",c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),f,g;if(c.is(".disabled, :disabled"))return;return f=e(c),g=f.hasClass("open"),d(),g||("ontouchstart"in document.documentElement&&a('<div class="dropdown-backdrop"/>').insertBefore(a(this)).on("click",d),f.toggleClass("open")),c.focus(),!1},keydown:function(c){var d,f,g,h,i,j;if(!/(38|40|27)/.test(c.keyCode))return;d=a(this),c.preventDefault(),c.stopPropagation();if(d.is(".disabled, :disabled"))return;h=e(d),i=h.hasClass("open");if(!i||i&&c.keyCode==27)return c.which==27&&h.find(b).focus(),d.click();f=a("[role=menu] li:not(.divider):visible a",h);if(!f.length)return;j=f.index(f.filter(":focus")),c.keyCode==38&&j>0&&j--,c.keyCode==40&&j<f.length-1&&j++,~j||(j=0),f.eq(j).focus()}};var f=a.fn.dropdown;a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=f,this},a(document).on("click.dropdown.data-api",d).on("click.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.dropdown.data-api",b,c.prototype.toggle).on("keydown.dropdown.data-api",b+", [role=menu]",c.prototype.keydown)}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-modal",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.focus().trigger("shown")}):b.$element.focus().trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?a.proxy(this.$element[0].focus,this.$element[0]):a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!b)return;e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b):b()):b&&b()}};var c=a.fn.modal;a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());b.preventDefault(),e.modal(f).one("hide",function(){c.focus()})})}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-tooltip",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f,g,h,i;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,g=this.options.trigger.split(" ");for(i=g.length;i--;)h=g[i],h=="click"?this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this)):h!="manual"&&(e=h=="hover"?"mouseenter":"focus",f=h=="hover"?"mouseleave":"blur",this.$element.on(e+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f+"."+this.type,this.options.selector,a.proxy(this.leave,this)));this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,this.$element.data(),b),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a.fn[this.type].defaults,d={},e;this._options&&a.each(this._options,function(a,b){c[a]!=b&&(d[a]=b)}),e=a(b.currentTarget)[this.type](d).data(this.type);if(!e.options.delay||!e.options.delay.show)return e.show();clearTimeout(this.timeout),e.hoverState="in",this.timeout=setTimeout(function(){e.hoverState=="in"&&e.show()},e.options.delay.show)},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!c.options.delay||!c.options.delay.hide)return c.hide();c.hoverState="out",this.timeout=setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide)},show:function(){var b,c,d,e,f,g,h=a.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(h);if(h.isDefaultPrevented())return;b=this.tip(),this.setContent(),this.options.animation&&b.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,b[0],this.$element[0]):this.options.placement,b.detach().css({top:0,left:0,display:"block"}),this.options.container?b.appendTo(this.options.container):b.insertAfter(this.$element),c=this.getPosition(),d=b[0].offsetWidth,e=b[0].offsetHeight;switch(f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}this.applyPlacement(g,f),this.$element.trigger("shown")}},applyPlacement:function(a,b){var c=this.tip(),d=c[0].offsetWidth,e=c[0].offsetHeight,f,g,h,i;c.offset(a).addClass(b).addClass("in"),f=c[0].offsetWidth,g=c[0].offsetHeight,b=="top"&&g!=e&&(a.top=a.top+e-g,i=!0),b=="bottom"||b=="top"?(h=0,a.left<0&&(h=a.left*-2,a.left=0,c.offset(a),f=c[0].offsetWidth,g=c[0].offsetHeight),this.replaceArrow(h-d+f,f,"left")):this.replaceArrow(g-e,g,"top"),i&&c.offset(a)},replaceArrow:function(a,b,c){this.arrow().css(c,a?50*(1-a/b)+"%":"")},setContent:function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},hide:function(){function e(){var b=setTimeout(function(){c.off(a.support.transition.end).detach()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.detach()})}var b=this,c=this.tip(),d=a.Event("hide");this.$element.trigger(d);if(d.isDefaultPrevented())return;return c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?e():c.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var b=this.$element[0];return a.extend({},typeof b.getBoundingClientRect=="function"?b.getBoundingClientRect():{width:b.offsetWidth,height:b.offsetHeight},this.$element.offset())},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(b){var c=b?a(b.currentTarget)[this.type](this._options).data(this.type):this;c.tip().hasClass("in")?c.hide():c.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var c=a.fn.tooltip;a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-popover",["bootstrap/bootstrap-transition","bootstrap/bootstrap-tooltip"],function(){return function(){!function(a){var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=(typeof c.content=="function"?c.content.call(b[0]):c.content)||b.attr("data-content"),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-scrollspy",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll-spy.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body"),this.refresh(),this.process()}b.prototype={constructor:b,refresh:function(){var b=this,c;this.offsets=a([]),this.targets=a([]),c=this.$body.find(this.selector).map(function(){var c=a(this),d=c.data("target")||c.attr("href"),e=/^#\w/.test(d)&&a(d);return e&&e.length&&[[e.position().top+(!a.isWindow(b.$scrollElement.get(0))&&b.$scrollElement.scrollTop()),d]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,c=b-this.$scrollElement.height(),d=this.offsets,e=this.targets,f=this.activeTarget,g;if(a>=c)return f!=(g=e.last()[0])&&this.activate(g);for(g=d.length;g--;)f!=e[g]&&a>=d[g]&&(!d[g+1]||a<=d[g+1])&&this.activate(e[g])},activate:function(b){var c,d;this.activeTarget=b,a(this.selector).parent(".active").removeClass("active"),d=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',c=a(d).parent("li").addClass("active"),c.parent(".dropdown-menu").length&&(c=c.closest("li.dropdown").addClass("active")),c.trigger("activate")}};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-tab",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f,g;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active:last a")[0],g=a.Event("show",{relatedTarget:e}),b.trigger(g);if(g.isDefaultPrevented())return;f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery)}.call(a),b})}(this),function(a){var b;define("bootstrap/bootstrap-typeahead",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=a(this.options.menu),this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(a)).change(),this.hide()},updater:function(a){return a},show:function(){var b=a.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:b.top+b.height,left:b.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(c=a.isFunction(this.source)?this.source(this.query,a.proxy(this.process,this)):this.source,c?this.process(c):this)},process:function(b){var c=this;return b=a.grep(b,function(a){return c.matcher(a)}),b=this.sorter(b),b.length?this.render(b.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){var b=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return a.replace(new RegExp("("+b+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",a.proxy(this.keydown,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this)).on("mouseleave","li",a.proxy(this.mouseleave,this))},eventSupported:function(a){var b=a in this.$element;return b||(this.$element.setAttribute(a,"return;"),b=typeof this.$element[a]=="function"),b},move:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},keydown:function(b){this.suppressKeyPressRepeat=~a.inArray(b.keyCode,[40,38,9,13,27]),this.move(b)},keypress:function(a){if(this.suppressKeyPressRepeat)return;this.move(a)},keyup:function(a){switch(a.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},focus:function(a){this.focused=!0},blur:function(a){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(a){a.stopPropagation(),a.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(b){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")},mouseleave:function(a){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var c=a.fn.typeahead;a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},a.fn.typeahead.Constructor=b,a.fn.typeahead.noConflict=function(){return a.fn.typeahead=c,this},a(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;c.typeahead(c.data())})}(window.jQuery)}.call(a),b})}(this),define("fuelux/checkbox",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.checkbox.defaults,c),this.$label=this.$element.parent(),this.$icon=this.$label.find("i"),this.$chk=this.$label.find("input[type=checkbox]"),this.setState(this.$chk),this.$chk.on("change",b.proxy(this.itemchecked,this))};c.prototype={constructor:c,setState:function(a){var b=a.is(":checked"),c=a.is(":disabled");this.$icon.removeClass("checked").removeClass("disabled"),b===!0&&this.$icon.addClass("checked"),c===!0&&this.$icon.addClass("disabled")},enable:function(){this.$chk.attr("disabled",!1),this.$icon.removeClass("disabled")},disable:function(){this.$chk.attr("disabled",!0),this.$icon.addClass("disabled")},toggle:function(){this.$chk.click()},itemchecked:function(a){var c=b(a.target);this.setState(c)}},b.fn.checkbox=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("checkbox"),h=typeof a=="object"&&a;g||f.data("checkbox",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.checkbox.defaults={},b.fn.checkbox.Constructor=c,b(function(){b(window).on("load",function(){b(".checkbox-custom > input[type=checkbox]").each(function(){var a=b(this);if(a.data("checkbox"))return;a.checkbox(a.data())})})})}),define("fuelux/util",["require","jquery"],function(a){function c(a,c){return(a.textContent||a.innerText||b(a).text()||"").toLowerCase()===(c||"").toLowerCase()}var b=a("jquery");b.expr[":"].fuelTextExactCI=b.expr.createPseudo?b.expr.createPseudo(function(a){return function(b){return c(b,a)}}):function(a,b,d){return c(a,d[3])}}),define("fuelux/combobox",["require","jquery","./util"],function(a){var b=a("jquery");a("./util");var c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.combobox.defaults,c),this.$element.on("click","a",b.proxy(this.itemclicked,this)),this.$element.on("change","input",b.proxy(this.inputchanged,this)),this.$input=this.$element.find("input"),this.$button=this.$element.find(".btn"),this.setDefaultSelection()};c.prototype={constructor:c,selectedItem:function(){var a=this.$selectedItem,c={};if(a){var d=this.$selectedItem.text();c=b.extend({text:d},this.$selectedItem.data())}else c={text:this.$input.val()};return c},selectByText:function(a){var b="li:fuelTextExactCI("+a+")";this.selectBySelector(b)},selectByValue:function(a){var b='li[data-value="'+a+'"]';this.selectBySelector(b)},selectByIndex:function(a){var b="li:eq("+a+")";this.selectBySelector(b)},selectBySelector:function(a){var b=this.$element.find(a);typeof b[0]!="undefined"?(this.$selectedItem=b,this.$input.val(this.$selectedItem.text())):this.$selectedItem=null},setDefaultSelection:function(){var a="li[data-selected=true]:first",b=this.$element.find(a);b.length>0&&(this.selectBySelector(a),b.removeData("selected"),b.removeAttr("data-selected"))},enable:function(){this.$input.removeAttr("disabled"),this.$button.removeClass("disabled")},disable:function(){this.$input.attr("disabled",!0),this.$button.addClass("disabled")},itemclicked:function(a){this.$selectedItem=b(a.target).parent(),this.$input.val(this.$selectedItem.text()).trigger("change",{synthetic:!0});var c=this.selectedItem();this.$element.trigger("changed",c),a.preventDefault()},inputchanged:function(a,c){if(c&&c.synthetic)return;var d=b(a.target).val();this.selectByText(d);var e=this.selectedItem();e.text.length===0&&(e={text:d}),this.$element.trigger("changed",e)}},b.fn.combobox=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("combobox"),h=typeof a=="object"&&a;g||f.data("combobox",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.combobox.defaults={},b.fn.combobox.Constructor=c,b(function(){b(window).on("load",function(){b(".combobox").each(function(){var a=b(this);if(a.data("combobox"))return;a.combobox(a.data())})}),b("body").on("mousedown.combobox.data-api",".combobox",function(a){var c=b(this);if(c.data("combobox"))return;c.combobox(c.data())})})}),define("fuelux/datagrid",["require","jquery"],function(a){var b=a("jquery"),c=22,d=function(a,c){this.$element=b(a),this.$thead=this.$element.find("thead"),this.$tfoot=this.$element.find("tfoot"),this.$footer=this.$element.find("tfoot th"),this.$footerchildren=this.$footer.children().show().css("visibility","hidden"),this.$topheader=this.$element.find("thead th"),this.$searchcontrol=this.$element.find(".datagrid-search"),this.$filtercontrol=this.$element.find(".filter"),this.$pagesize=this.$element.find(".grid-pagesize"),this.$pageinput=this.$element.find(".grid-pager input"),this.$pagedropdown=this.$element.find(".grid-pager .dropdown-menu"),this.$prevpagebtn=this.$element.find(".grid-prevpage"),this.$nextpagebtn=this.$element.find(".grid-nextpage"),this.$pageslabel=this.$element.find(".grid-pages"),this.$countlabel=this.$element.find(".grid-count"),this.$startlabel=this.$element.find(".grid-start"),this.$endlabel=this.$element.find(".grid-end"),this.$tbody=b("<tbody>").insertAfter(this.$thead),this.$colheader=b("<tr>").appendTo(this.$thead),this.options=b.extend(!0,{},b.fn.datagrid.defaults,c),this.$pagesize.hasClass("select")?this.options.dataOptions.pageSize=parseInt(this.$pagesize.select("selectedItem").value,10):this.options.dataOptions.pageSize=parseInt(this.$pagesize.val(),10),this.$searchcontrol.length<=0&&(this.$searchcontrol=this.$element.find(".search")),this.columns=this.options.dataSource.columns(),this.$nextpagebtn.on("click",b.proxy(this.next,this)),this.$prevpagebtn.on("click",b.proxy(this.previous,this)),this.$searchcontrol.on("searched cleared",b.proxy(this.searchChanged,this)),this.$filtercontrol.on("changed",b.proxy(this.filterChanged,this)),this.$colheader.on("click","th",b.proxy(this.headerClicked,this)),this.$pagesize.hasClass("select")?this.$pagesize.on("changed",b.proxy(this.pagesizeChanged,this)):this.$pagesize.on("change",b.proxy(this.pagesizeChanged,this)),this.$pageinput.on("change",b.proxy(this.pageChanged,this)),this.renderColumns(),this.options.stretchHeight&&this.initStretchHeight(),this.renderData()};d.prototype={constructor:d,renderColumns:function(){var a=this;this.$footer.attr("colspan",this.columns.length),this.$topheader.attr("colspan",this.columns.length);var c="";b.each(this.columns,function(a,b){c+='<th data-property="'+b.property+'"',b.sortable&&(c+=' class="sortable"'),c+=">"+b.label+"</th>"}),a.$colheader.append(c)},updateColumns:function(a,b){this._updateColumns(this.$colheader,a,b),this.$sizingHeader&&this._updateColumns(this.$sizingHeader,this.$sizingHeader.find("th").eq(a.index()),b)},_updateColumns:function(a,c,d){var e=d==="asc"?"icon-chevron-up":"icon-chevron-down";a.find("i.datagrid-sort").remove(),a.find("th").removeClass("sorted"),b("<i>").addClass(e+" datagrid-sort").appendTo(c),c.addClass("sorted")},updatePageDropdown:function(a){var b="";for(var c=1;c<=a.pages;c++)b+="<li><a>"+c+"</a></li>";this.$pagedropdown.html(b)},updatePageButtons:function(a){a.page===1?this.$prevpagebtn.attr("disabled","disabled"):this.$prevpagebtn.removeAttr("disabled"),a.page===a.pages?this.$nextpagebtn.attr("disabled","disabled"):this.$nextpagebtn.removeAttr("disabled")},renderData:function(){var a=this;this.$tbody.html(this.placeholderRowHTML(this.options.loadingHTML)),this.options.dataSource.data(this.options.dataOptions,function(c){var d=c.count===1?a.options.itemText:a.options.itemsText,e="";a.$footerchildren.css("visibility",function(){return c.count>0?"visible":"hidden"}),a.$pageinput.val(c.page),a.$pageslabel.text(c.pages),a.$countlabel.text(c.count+" "+d),a.$startlabel.text(c.start),a.$endlabel.text(c.end),a.updatePageDropdown(c),a.updatePageButtons(c),b.each(c.data,function(c,d){e+="<tr>",b.each(a.columns,function(a,b){e+="<td>"+d[b.property]+"</td>"}),e+="</tr>"}),e||(e=a.placeholderRowHTML("0 "+a.options.itemsText)),a.$tbody.html(e),a.stretchHeight(),a.$element.trigger("loaded")})},placeholderRowHTML:function(a){return'<tr><td style="text-align:center;padding:20px;border-bottom:none;" colspan="'+this.columns.length+'">'+a+"</td></tr>"},headerClicked:function(a){var c=b(a.target);if(!c.hasClass("sortable"))return;var d=this.options.dataOptions.sortDirection,e=this.options.dataOptions.sortProperty,f=c.data("property");e===f?this.options.dataOptions.sortDirection=d==="asc"?"desc":"asc":(this.options.dataOptions.sortDirection="asc",this.options.dataOptions.sortProperty=f),this.options.dataOptions.pageIndex=0,this.updateColumns(c,this.options.dataOptions.sortDirection),this.renderData()},pagesizeChanged:function(a,c){c?this.options.dataOptions.pageSize=parseInt(c.value,10):this.options.dataOptions.pageSize=parseInt(b(a.target).val(),10),this.options.dataOptions.pageIndex=0,this.renderData()},pageChanged:function(a){var c=parseInt(b(a.target).val(),10);c=isNaN(c)?1:c;var d=this.$pageslabel.text();this.options.dataOptions.pageIndex=c>d?d-1:c-1,this.renderData()},searchChanged:function(a,b){this.options.dataOptions.search=b,this.options.dataOptions.pageIndex=0,this.renderData()},filterChanged:function(a,b){this.options.dataOptions.filter=b,this.options.dataOptions.pageIndex=0,this.renderData()},previous:function(){this.$nextpagebtn.attr("disabled","disabled"),this.$prevpagebtn.attr("disabled","disabled"),this.options.dataOptions.pageIndex--,this.renderData()},next:function(){this.$nextpagebtn.attr("disabled","disabled"),this.$prevpagebtn.attr("disabled","disabled"),this.options.dataOptions.pageIndex++,this.renderData()},reload:function(){this.options.dataOptions.pageIndex=0,this.renderData()},initStretchHeight:function(){this.$gridContainer=this.$element.parent(),this.$element.wrap('<div class="datagrid-stretch-wrapper">'),this.$stretchWrapper=this.$element.parent(),this.$headerTable=b("<table>").attr("class",this.$element.attr("class")),this.$footerTable=this.$headerTable.clone(),this.$headerTable.prependTo(this.$gridContainer).addClass("datagrid-stretch-header"),this.$thead.detach().appendTo(this.$headerTable),this.$sizingHeader=this.$thead.clone(),this.$sizingHeader.find("tr:first").remove(),this.$footerTable.appendTo(this.$gridContainer).addClass("datagrid-stretch-footer"),this.$tfoot.detach().appendTo(this.$footerTable)},stretchHeight:function(){if(!this.$gridContainer)return;this.setColumnWidths();var a=this.$gridContainer.height(),b=this.$headerTable.outerHeight(),c=this.$footerTable.outerHeight(),d=b+c;this.$stretchWrapper.height(a-d)},setColumnWidths:function(){function e(e,f){if(e===d-1)return;var g=b(f),h=a.eq(e),i=h.width();h.hasClass("sorted")&&g.prop("tagName")==="TD"&&(i=i+c),g.width(i)}if(!this.$sizingHeader)return;this.$element.prepend(this.$sizingHeader);var a=this.$sizingHeader.find("th"),d=a.length;this.$colheader.find("th").each(e),this.$tbody.find("tr:first > td").each(e),this.$sizingHeader.detach()}},b.fn.datagrid=function(a){return this.each(function(){var c=b(this),e=c.data("datagrid"),f=typeof a=="object"&&a;e||c.data("datagrid",e=new d(this,f)),typeof a=="string"&&e[a]()})},b.fn.datagrid.defaults={dataOptions:{pageIndex:0,pageSize:10},loadingHTML:'<div class="progress progress-striped active" style="width:50%;margin:auto;"><div class="bar" style="width:100%;"></div></div>',itemsText:"items",itemText:"item"},b.fn.datagrid.Constructor=d}),define("fuelux/pillbox",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.pillbox.defaults,c),this.$element.on("click","li",b.proxy(this.itemclicked,this))};c.prototype={constructor:c,items:function(){return this.$element.find("li").map(function(){var a=b(this);return b.extend({text:a.text()},a.data())}).get()},itemclicked:function(a){b(a.currentTarget).remove(),a.preventDefault()}},b.fn.pillbox=function(a){var d,e=this.each(function(){var e=b(this),f=e.data("pillbox"),g=typeof a=="object"&&a;f||e.data("pillbox",f=new c(this,g)),typeof a=="string"&&(d=f[a]())});return d===undefined?e:d},b.fn.pillbox.defaults={},b.fn.pillbox.Constructor=c,b(function(){b("body").on("mousedown.pillbox.data-api",".pillbox",function(a){var c=b(this);if(c.data("pillbox"))return;c.pillbox(c.data())})})}),define("fuelux/radio",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.radio.defaults,c),this.$label=this.$element.parent(),this.$icon=this.$label.find("i"),this.$radio=this.$label.find("input[type=radio]"),this.groupName=this.$radio.attr("name"),this.setState(this.$radio),this.$radio.on("change",b.proxy(this.itemchecked,this))};c.prototype={constructor:c,setState:function(a,b){var c=a.is(":checked"),d=a.is(":disabled");c===!0&&this.$icon.addClass("checked"),d===!0&&this.$icon.addClass("disabled")},resetGroup:function(){b("input[name="+this.groupName+"]").next().removeClass("checked")},enable:function(){this.$radio.attr("disabled",!1),this.$icon.removeClass("disabled")},disable:function(){this.$radio.attr("disabled",!0),this.$icon.addClass("disabled")},itemchecked:function(a){var c=b(a.target);this.resetGroup(),this.setState(c)}},b.fn.radio=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("radio"),h=typeof a=="object"&&a;g||f.data("radio",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.radio.defaults={},b.fn.radio.Constructor=c,b(function(){b(window).on("load",function(){b(".radio-custom > input[type=radio]").each(function(){var a=b(this);if(a.data("radio"))return;a.radio(a.data())})})})}),define("fuelux/search",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.search.defaults,c),this.$button=this.$element.find("button").on("click",b.proxy(this.buttonclicked,this)),this.$input=this.$element.find("input").on("keydown",b.proxy(this.keypress,this)).on("keyup",b.proxy(this.keypressed,this)),this.$icon=this.$element.find("i"),this.activeSearch=""};c.prototype={constructor:c,search:function(a){this.$icon.attr("class","icon-remove"),this.activeSearch=a,this.$element.trigger("searched",a)},clear:function(){this.$icon.attr("class","icon-search"),this.activeSearch="",this.$input.val(""),this.$element.trigger("cleared")},action:function(){var a=this.$input.val(),b=a===""||a===this.activeSearch;this.activeSearch&&b?this.clear():a&&this.search(a)},buttonclicked:function(a){a.preventDefault();if(b(a.currentTarget).is(".disabled, :disabled"))return;this.action()},keypress:function(a){a.which===13&&a.preventDefault()},keypressed:function(a){var b,c;a.which===13?(a.preventDefault(),this.action()):(b=this.$input.val(),c=b&&b===this.activeSearch,this.$icon.attr("class",c?"icon-remove":"icon-search"))},disable:function(){this.$input.attr("disabled","disabled"),this.$button.addClass("disabled")},enable:function(){this.$input.removeAttr("disabled"),this.$button.removeClass("disabled")}},b.fn.search=function(a){return this.each(function(){var d=b(this),e=d.data("search"),f=typeof a=="object"&&a;e||d.data("search",e=new c(this,f)),typeof a=="string"&&e[a]()})},b.fn.search.defaults={},b.fn.search.Constructor=c,b(function(){b("body").on("mousedown.search.data-api",".search",function(){var a=b(this);if(a.data("search"))return;a.search(a.data())})})}),define("fuelux/spinner",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.spinner.defaults,c),this.$input=this.$element.find(".spinner-input"),this.$element.on("keyup",this.$input,b.proxy(this.change,this)),this.options.hold?(this.$element.on("mousedown",".spinner-up",b.proxy(function(){this.startSpin(!0)},this)),this.$element.on("mouseup",".spinner-up, .spinner-down",b.proxy(this.stopSpin,this)),this.$element.on("mouseout",".spinner-up, .spinner-down",b.proxy(this.stopSpin,this)),this.$element.on("mousedown",".spinner-down",b.proxy(function(){this.startSpin(!1)},this))):(this.$element.on("click",".spinner-up",b.proxy(function(){this.step(!0)},this)),this.$element.on("click",".spinner-down",b.proxy(function(){this.step(!1)},this))),this.switches={count:1,enabled:!0},this.options.speed==="medium"?this.switches.speed=300:this.options.speed==="fast"?this.switches.speed=100:this.switches.speed=500,this.lastValue=null,this.render(),this.options.disabled&&this.disable()};c.prototype={constructor:c,render:function(){this.$input.val(this.options.value),this.$input.attr("maxlength",(this.options.max+"").split("").length)},change:function(){var a=this.$input.val();a/1?this.options.value=a/1:(a=a.replace(/[^0-9]/g,""),this.$input.val(a),this.options.value=a/1),this.triggerChangedEvent()},stopSpin:function(){clearTimeout(this.switches.timeout),this.switches.count=1,this.triggerChangedEvent()},triggerChangedEvent:function(){var a=this.value();if(a===this.lastValue)return;this.lastValue=a,this.$element.trigger("changed",a),this.$element.trigger("change")},startSpin:function(a){if(!this.options.disabled){var c=this.switches.count;c===1?(this.step(a),c=1):c<3?c=1.5:c<8?c=2.5:c=4,this.switches.timeout=setTimeout(b.proxy(function(){this.iterator(a)},this),this.switches.speed/c),this.switches.count++}},iterator:function(a){this.step(a),this.startSpin(a)},step:function(a){var b=this.options.value,c=a?this.options.max:this.options.min;if(a?b<c:b>c){var d=b+(a?1:-1)*this.options.step;(a?d>c:d<c)?this.value(c):this.value(d)}},value:function(a){return!isNaN(parseFloat(a))&&isFinite(a)?(a=parseFloat(a),this.options.value=a,this.$input.val(a),this):this.options.value},disable:function(){this.options.disabled=!0,this.$input.attr("disabled",""),this.$element.find("button").addClass("disabled")},enable:function(){this.options.disabled=!1,this.$input.removeAttr("disabled"),this.$element.find("button").removeClass("disabled")}},b.fn.spinner=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("spinner"),h=typeof a=="object"&&a;g||f.data("spinner",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.spinner.defaults={value:1,min:1,max:999,step:1,hold:!0,speed:"medium",disabled:!1},b.fn.spinner.Constructor=c,b(function(){b("body").on("mousedown.spinner.data-api",".spinner",function(a){var c=b(this);if(c.data("spinner"))return;c.spinner(c.data())})})}),define("fuelux/select",["require","jquery","./util"],function(a){var b=a("jquery");a("./util");var c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.select.defaults,c),this.$element.on("click","a",b.proxy(this.itemclicked,this)),this.$button=this.$element.find(".btn"),this.$label=this.$element.find(".dropdown-label"),this.setDefaultSelection(),c.resize==="auto"&&this.resize()};c.prototype={constructor:c,itemclicked:function(a){this.$selectedItem=b(a.target).parent(),this.$label.text(this.$selectedItem.text());var c=this.selectedItem();this.$element.trigger("changed",c),a.preventDefault()},resize:function(){var a=b("#selectTextSize")[0];a||b("<div/>").attr({id:"selectTextSize"}).appendTo("body");var c=0,d=0;this.$element.find("a").each(function(){var a=b(this),e=a.text(),f=b("#selectTextSize");f.text(e),d=f.outerWidth(),d>c&&(c=d)}),this.$label.width(c)},selectedItem:function(){var a=this.$selectedItem.text();return b.extend({text:a},this.$selectedItem.data())},selectByText:function(a){var b="li a:fuelTextExactCI("+a+")";this.selectBySelector(b)},selectByValue:function(a){var b='li[data-value="'+a+'"]';this.selectBySelector(b)},selectByIndex:function(a){var b="li:eq("+a+")";this.selectBySelector(b)},selectBySelector:function(a){var b=this.$element.find(a);this.$selectedItem=b,this.$label.text(this.$selectedItem.text())},setDefaultSelection:function(){var a="li[data-selected=true]:first",b=this.$element.find(a);b.length===0?this.selectByIndex(0):(this.selectBySelector(a),b.removeData("selected"),b.removeAttr("data-selected"))},enable:function(){this.$button.removeClass("disabled")},disable:function(){this.$button.addClass("disabled")}},b.fn.select=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("select"),h=typeof a=="object"&&a;g||f.data("select",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.select.defaults={},b.fn.select.Constructor=c,b(function(){b(window).on("load",function(){b(".select").each(function(){var a=b(this);if(a.data("select"))return;a.select(a.data())})}),b("body").on("mousedown.select.data-api",".select",function(a){var c=b(this);if(c.data("select"))return;c.select(c.data())})})}),define("fuelux/tree",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.tree.defaults,c),this.$element.on("click",".tree-item",b.proxy(function(a){this.selectItem(a.currentTarget)},this)),this.$element.on("click",".tree-folder-header",b.proxy(function(a){this.selectFolder(a.currentTarget)},this)),this.render()};c.prototype={constructor:c,render:function(){this.populate(this.$element)},populate:function(a){var c=this,d=a.parent().find(".tree-loader:eq(0)");d.show(),this.options.dataSource.data(a.data(),function(e){d.hide(),b.each(e.data,function(b,d){var e;d.type==="folder"?(e=c.$element.find(".tree-folder:eq(0)").clone().show(),e.find(".tree-folder-name").html(d.name),e.find(".tree-loader").html(c.options.loadingHTML),e.find(".tree-folder-header").data(d)):d.type==="item"&&(e=c.$element.find(".tree-item:eq(0)").clone().show(),e.find(".tree-item-name").html(d.name),e.data(d)),a.hasClass("tree-folder-header")?a.parent().find(".tree-folder-content:eq(0)").append(e):a.append(e)}),c.$element.trigger("loaded")})},selectItem:function(a){var c=b(a),d=this.$element.find(".tree-selected"),e=[];this.options.multiSelect?b.each(d,function(a,d){var f=b(d);f[0]!==c[0]&&e.push(b(d).data())}):d[0]!==c[0]&&(d.removeClass("tree-selected").find("i").removeClass("icon-ok").addClass("tree-dot"),e.push(c.data())),c.hasClass("tree-selected")?(c.removeClass("tree-selected"),c.find("i").removeClass("icon-ok").addClass("tree-dot")):(c.addClass("tree-selected"),c.find("i").removeClass("tree-dot").addClass("icon-ok"),this.options.multiSelect&&e.push(c.data())),e.length&&this.$element.trigger("selected",{info:e})},selectFolder:function(a){var c=b(a),d=c.parent();c.find(".icon-folder-close").length?(d.find(".tree-folder-content").children().length?d.find(".tree-folder-content:eq(0)").show():this.populate(c),d.find(".icon-folder-close:eq(0)").removeClass("icon-folder-close").addClass("icon-folder-open"),this.$element.trigger("opened",c.data())):(this.options.cacheItems?d.find(".tree-folder-content:eq(0)").hide():d.find(".tree-folder-content:eq(0)").empty(),d.find(".icon-folder-open:eq(0)").removeClass("icon-folder-open").addClass("icon-folder-close"),this.$element.trigger("closed",c.data()))},selectedItems:function(){var a=this.$element.find(".tree-selected"),c=[];return b.each(a,function(a,d){c.push(b(d).data())}),c}},b.fn.tree=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("tree"),h=typeof a=="object"&&a;g||f.data("tree",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.tree.defaults={multiSelect:!1,loadingHTML:"<div>Loading...</div>",cacheItems:!0},b.fn.tree.Constructor=c}),define("fuelux/wizard",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){var d;this.$element=b(a),this.options=b.extend({},b.fn.wizard.defaults,c),this.currentStep=1,this.numSteps=this.$element.find("li").length,this.$prevBtn=this.$element.find("button.btn-prev"),this.$nextBtn=this.$element.find("button.btn-next"),d=this.$nextBtn.children().detach(),this.nextText=b.trim(this.$nextBtn.text()),this.$nextBtn.append(d),this.$prevBtn.on("click",b.proxy(this.previous,this)),this.$nextBtn.on("click",b.proxy(this.next,this)),this.$element.on("click","li.complete",b.proxy(this.stepclicked,this))};c.prototype={constructor:c,setState:function(){var a=this.currentStep>1,c=this.currentStep===1,d=this.currentStep===this.numSteps;this.$prevBtn.attr("disabled",c===!0||a===!1);var e=this.$nextBtn.data();if(e&&e.last){this.lastText=e.last;if(typeof this.lastText!="undefined"){var f=d!==!0?this.nextText:this.lastText,g=this.$nextBtn.children().detach();this.$nextBtn.text(f).append(g)}}var h=this.$element.find("li");h.removeClass("active").removeClass("complete"),h.find("span.badge").removeClass("badge-info").removeClass("badge-success");var i="li:lt("+(this.currentStep-1)+")",j=this.$element.find(i);j.addClass("complete"),j.find("span.badge").addClass("badge-success");var k="li:eq("+(this.currentStep-1)+")",l=this.$element.find(k);l.addClass("active"),l.find("span.badge").addClass("badge-info");var m=l.data().target;b(".step-pane").removeClass("active"),b(m).addClass("active"),this.$element.trigger("changed")},stepclicked:function(a){var c=b(a.currentTarget),d=b(".steps li").index(c),e=b.Event("stepclick");this.$element.trigger(e,{step:d+1});if(e.isDefaultPrevented())return;this.currentStep=d+1,this.setState()},previous:function(){var a=this.currentStep>1;if(a){var c=b.Event("change");this.$element.trigger(c,{step:this.currentStep,direction:"previous"});if(c.isDefaultPrevented())return;this.currentStep-=1,this.setState()}},next:function(){var a=this.currentStep+1<=this.numSteps,c=this.currentStep===this.numSteps;if(a){var d=b.Event("change");this.$element.trigger(d,{step:this.currentStep,direction:"next"});if(d.isDefaultPrevented())return;this.currentStep+=1,this.setState()}else c&&this.$element.trigger("finished")},selectedItem:function(a){return{step:this.currentStep}}},b.fn.wizard=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("wizard"),h=typeof a=="object"&&a;g||f.data("wizard",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.wizard.defaults={},b.fn.wizard.Constructor=c,b(function(){b("body").on("mousedown.wizard.data-api",".wizard",function(){var a=b(this);if(a.data("wizard"))return;a.wizard(a.data())})})}),define("fuelux/all",["require","jquery","bootstrap/bootstrap-affix","bootstrap/bootstrap-alert","bootstrap/bootstrap-button","bootstrap/bootstrap-carousel","bootstrap/bootstrap-collapse","bootstrap/bootstrap-dropdown","bootstrap/bootstrap-modal","bootstrap/bootstrap-popover","bootstrap/bootstrap-scrollspy","bootstrap/bootstrap-tab","bootstrap/bootstrap-tooltip","bootstrap/bootstrap-transition","bootstrap/bootstrap-typeahead","fuelux/checkbox","fuelux/combobox","fuelux/datagrid","fuelux/pillbox","fuelux/radio","fuelux/search","fuelux/spinner","fuelux/select","fuelux/tree","fuelux/wizard"],function(a){a("jquery"),a("bootstrap/bootstrap-affix"),a("bootstrap/bootstrap-alert"),a("bootstrap/bootstrap-button"),a("bootstrap/bootstrap-carousel"),a("bootstrap/bootstrap-collapse"),a("bootstrap/bootstrap-dropdown"),a("bootstrap/bootstrap-modal"),a("bootstrap/bootstrap-popover"),a("bootstrap/bootstrap-scrollspy"),a("bootstrap/bootstrap-tab"),a("bootstrap/bootstrap-tooltip"),a("bootstrap/bootstrap-transition"),a("bootstrap/bootstrap-typeahead"),a("fuelux/checkbox"),a("fuelux/combobox"),a("fuelux/datagrid"),a("fuelux/pillbox"),a("fuelux/radio"),a("fuelux/search"),a("fuelux/spinner"),a("fuelux/select"),a("fuelux/tree"),a("fuelux/wizard")})})();
\ No newline at end of file
--- /dev/null
+/*
+ * Fuel UX Checkbox
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery'],function (require) {
+
+ var $ = require('jquery');
+
+
+ // CHECKBOX CONSTRUCTOR AND PROTOTYPE
+
+ var Checkbox = function (element, options) {
+
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.checkbox.defaults, options);
+
+ // cache elements
+ this.$label = this.$element.parent();
+ this.$icon = this.$label.find('i');
+ this.$chk = this.$label.find('input[type=checkbox]');
+
+ // set default state
+ this.setState(this.$chk);
+
+ // handle events
+ this.$chk.on('change', $.proxy(this.itemchecked, this));
+ };
+
+ Checkbox.prototype = {
+
+ constructor: Checkbox,
+
+ setState: function ($chk) {
+ var checked = $chk.is(':checked');
+ var disabled = $chk.is(':disabled');
+
+ // reset classes
+ this.$icon.removeClass('checked').removeClass('disabled');
+
+ // set state of checkbox
+ if (checked === true) {
+ this.$icon.addClass('checked');
+ }
+ if (disabled === true) {
+ this.$icon.addClass('disabled');
+ }
+ },
+
+ enable: function () {
+ this.$chk.attr('disabled', false);
+ this.$icon.removeClass('disabled');
+ },
+
+ disable: function () {
+ this.$chk.attr('disabled', true);
+ this.$icon.addClass('disabled');
+ },
+
+ toggle: function () {
+ this.$chk.click();
+ },
+
+ itemchecked: function (e) {
+ var chk = $(e.target);
+ this.setState(chk);
+ }
+ };
+
+
+ // CHECKBOX PLUGIN DEFINITION
+
+ $.fn.checkbox = function (option, value) {
+ var methodReturn;
+
+ var $set = this.each(function () {
+ var $this = $(this);
+ var data = $this.data('checkbox');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('checkbox', (data = new Checkbox(this, options)));
+ if (typeof option === 'string') methodReturn = data[option](value);
+ });
+
+ return (methodReturn === undefined) ? $set : methodReturn;
+ };
+
+ $.fn.checkbox.defaults = {};
+
+ $.fn.checkbox.Constructor = Checkbox;
+
+
+ // CHECKBOX DATA-API
+
+ $(function () {
+ $(window).on('load', function () {
+ //$('i.checkbox').each(function () {
+ $('.checkbox-custom > input[type=checkbox]').each(function () {
+ var $this = $(this);
+ if ($this.data('checkbox')) return;
+ $this.checkbox($this.data());
+ });
+ });
+ });
+
+});
--- /dev/null
+/*
+ * Fuel UX Combobox
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery','./util'],function (require) {
+
+ var $ = require('jquery');
+ require('./util');
+
+ // COMBOBOX CONSTRUCTOR AND PROTOTYPE
+
+ var Combobox = function (element, options) {
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.combobox.defaults, options);
+ this.$element.on('click', 'a', $.proxy(this.itemclicked, this));
+ this.$element.on('change', 'input', $.proxy(this.inputchanged, this));
+ this.$input = this.$element.find('input');
+ this.$button = this.$element.find('.btn');
+
+ // set default selection
+ this.setDefaultSelection();
+ };
+
+ Combobox.prototype = {
+
+ constructor: Combobox,
+
+ selectedItem: function () {
+ var item = this.$selectedItem;
+ var data = {};
+
+ if (item) {
+ var txt = this.$selectedItem.text();
+ data = $.extend({ text: txt }, this.$selectedItem.data());
+ }
+ else {
+ data = { text: this.$input.val()};
+ }
+
+ return data;
+ },
+
+ selectByText: function (text) {
+ var selector = 'li:fuelTextExactCI(' + text + ')';
+ this.selectBySelector(selector);
+ },
+
+ selectByValue: function (value) {
+ var selector = 'li[data-value="' + value + '"]';
+ this.selectBySelector(selector);
+ },
+
+ selectByIndex: function (index) {
+ // zero-based index
+ var selector = 'li:eq(' + index + ')';
+ this.selectBySelector(selector);
+ },
+
+ selectBySelector: function (selector) {
+ var $item = this.$element.find(selector);
+
+ if (typeof $item[0] !== 'undefined') {
+ this.$selectedItem = $item;
+ this.$input.val(this.$selectedItem.text());
+ }
+ else {
+ this.$selectedItem = null;
+ }
+ },
+
+ setDefaultSelection: function () {
+ var selector = 'li[data-selected=true]:first';
+ var item = this.$element.find(selector);
+
+ if (item.length > 0) {
+ // select by data-attribute
+ this.selectBySelector(selector);
+ item.removeData('selected');
+ item.removeAttr('data-selected');
+ }
+ },
+
+ enable: function () {
+ this.$input.removeAttr('disabled');
+ this.$button.removeClass('disabled');
+ },
+
+ disable: function () {
+ this.$input.attr('disabled', true);
+ this.$button.addClass('disabled');
+ },
+
+ itemclicked: function (e) {
+ this.$selectedItem = $(e.target).parent();
+
+ // set input text and trigger input change event marked as synthetic
+ this.$input.val(this.$selectedItem.text()).trigger('change', { synthetic: true });
+
+ // pass object including text and any data-attributes
+ // to onchange event
+ var data = this.selectedItem();
+
+ // trigger changed event
+ this.$element.trigger('changed', data);
+
+ e.preventDefault();
+ },
+
+ inputchanged: function (e, extra) {
+
+ // skip processing for internally-generated synthetic event
+ // to avoid double processing
+ if (extra && extra.synthetic) return;
+
+ var val = $(e.target).val();
+ this.selectByText(val);
+
+ // find match based on input
+ // if no match, pass the input value
+ var data = this.selectedItem();
+ if (data.text.length === 0) {
+ data = { text: val };
+ }
+
+ // trigger changed event
+ this.$element.trigger('changed', data);
+
+ }
+
+ };
+
+
+ // COMBOBOX PLUGIN DEFINITION
+
+ $.fn.combobox = function (option, value) {
+ var methodReturn;
+
+ var $set = this.each(function () {
+ var $this = $(this);
+ var data = $this.data('combobox');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('combobox', (data = new Combobox(this, options)));
+ if (typeof option === 'string') methodReturn = data[option](value);
+ });
+
+ return (methodReturn === undefined) ? $set : methodReturn;
+ };
+
+ $.fn.combobox.defaults = {};
+
+ $.fn.combobox.Constructor = Combobox;
+
+
+ // COMBOBOX DATA-API
+
+ $(function () {
+
+ $(window).on('load', function () {
+ $('.combobox').each(function () {
+ var $this = $(this);
+ if ($this.data('combobox')) return;
+ $this.combobox($this.data());
+ });
+ });
+
+ $('body').on('mousedown.combobox.data-api', '.combobox', function (e) {
+ var $this = $(this);
+ if ($this.data('combobox')) return;
+ $this.combobox($this.data());
+ });
+ });
+
+});
--- /dev/null
+/*
+ * Fuel UX Datagrid
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery'],function(require) {
+
+ var $ = require('jquery');
+
+ // Relates to thead .sorted styles in datagrid.less
+ var SORTED_HEADER_OFFSET = 22;
+
+
+ // DATAGRID CONSTRUCTOR AND PROTOTYPE
+
+ var Datagrid = function (element, options) {
+ this.$element = $(element);
+ this.$thead = this.$element.find('thead');
+ this.$tfoot = this.$element.find('tfoot');
+ this.$footer = this.$element.find('tfoot th');
+ this.$footerchildren = this.$footer.children().show().css('visibility', 'hidden');
+ this.$topheader = this.$element.find('thead th');
+ this.$searchcontrol = this.$element.find('.datagrid-search');
+ this.$filtercontrol = this.$element.find('.filter');
+ this.$pagesize = this.$element.find('.grid-pagesize');
+ this.$pageinput = this.$element.find('.grid-pager input');
+ this.$pagedropdown = this.$element.find('.grid-pager .dropdown-menu');
+ this.$prevpagebtn = this.$element.find('.grid-prevpage');
+ this.$nextpagebtn = this.$element.find('.grid-nextpage');
+ this.$pageslabel = this.$element.find('.grid-pages');
+ this.$countlabel = this.$element.find('.grid-count');
+ this.$startlabel = this.$element.find('.grid-start');
+ this.$endlabel = this.$element.find('.grid-end');
+
+ this.$tbody = $('<tbody>').insertAfter(this.$thead);
+ this.$colheader = $('<tr>').appendTo(this.$thead);
+
+ this.options = $.extend(true, {}, $.fn.datagrid.defaults, options);
+
+ // Shim until v3 -- account for FuelUX select or native select for page size:
+ if (this.$pagesize.hasClass('select')) {
+ this.options.dataOptions.pageSize = parseInt(this.$pagesize.select('selectedItem').value, 10);
+ } else {
+ this.options.dataOptions.pageSize = parseInt(this.$pagesize.val(), 10);
+ }
+
+ // Shim until v3 -- account for older search class:
+ if (this.$searchcontrol.length <= 0) {
+ this.$searchcontrol = this.$element.find('.search');
+ }
+
+ this.columns = this.options.dataSource.columns();
+
+ this.$nextpagebtn.on('click', $.proxy(this.next, this));
+ this.$prevpagebtn.on('click', $.proxy(this.previous, this));
+ this.$searchcontrol.on('searched cleared', $.proxy(this.searchChanged, this));
+ this.$filtercontrol.on('changed', $.proxy(this.filterChanged, this));
+ this.$colheader.on('click', 'th', $.proxy(this.headerClicked, this));
+
+ if(this.$pagesize.hasClass('select')) {
+ this.$pagesize.on('changed', $.proxy(this.pagesizeChanged, this));
+ } else {
+ this.$pagesize.on('change', $.proxy(this.pagesizeChanged, this));
+ }
+
+ this.$pageinput.on('change', $.proxy(this.pageChanged, this));
+
+ this.renderColumns();
+
+ if (this.options.stretchHeight) this.initStretchHeight();
+
+ this.renderData();
+ };
+
+ Datagrid.prototype = {
+
+ constructor: Datagrid,
+
+ renderColumns: function () {
+ var self = this;
+
+ this.$footer.attr('colspan', this.columns.length);
+ this.$topheader.attr('colspan', this.columns.length);
+
+ var colHTML = '';
+
+ $.each(this.columns, function (index, column) {
+ colHTML += '<th data-property="' + column.property + '"';
+ if (column.sortable) colHTML += ' class="sortable"';
+ colHTML += '>' + column.label + '</th>';
+ });
+
+ self.$colheader.append(colHTML);
+ },
+
+ updateColumns: function ($target, direction) {
+ this._updateColumns(this.$colheader, $target, direction);
+
+ if (this.$sizingHeader) {
+ this._updateColumns(this.$sizingHeader, this.$sizingHeader.find('th').eq($target.index()), direction);
+ }
+ },
+
+ _updateColumns: function ($header, $target, direction) {
+ var className = (direction === 'asc') ? 'icon-chevron-up' : 'icon-chevron-down';
+ $header.find('i.datagrid-sort').remove();
+ $header.find('th').removeClass('sorted');
+ $('<i>').addClass(className + ' datagrid-sort').appendTo($target);
+ $target.addClass('sorted');
+ },
+
+ updatePageDropdown: function (data) {
+ var pageHTML = '';
+
+ for (var i = 1; i <= data.pages; i++) {
+ pageHTML += '<li><a>' + i + '</a></li>';
+ }
+
+ this.$pagedropdown.html(pageHTML);
+ },
+
+ updatePageButtons: function (data) {
+ if (data.page === 1) {
+ this.$prevpagebtn.attr('disabled', 'disabled');
+ } else {
+ this.$prevpagebtn.removeAttr('disabled');
+ }
+
+ if (data.page === data.pages) {
+ this.$nextpagebtn.attr('disabled', 'disabled');
+ } else {
+ this.$nextpagebtn.removeAttr('disabled');
+ }
+ },
+
+ renderData: function () {
+ var self = this;
+
+ this.$tbody.html(this.placeholderRowHTML(this.options.loadingHTML));
+
+ this.options.dataSource.data(this.options.dataOptions, function (data) {
+ var itemdesc = (data.count === 1) ? self.options.itemText : self.options.itemsText;
+ var rowHTML = '';
+
+ self.$footerchildren.css('visibility', function () {
+ return (data.count > 0) ? 'visible' : 'hidden';
+ });
+
+ self.$pageinput.val(data.page);
+ self.$pageslabel.text(data.pages);
+ self.$countlabel.text(data.count + ' ' + itemdesc);
+ self.$startlabel.text(data.start);
+ self.$endlabel.text(data.end);
+
+ self.updatePageDropdown(data);
+ self.updatePageButtons(data);
+
+ $.each(data.data, function (index, row) {
+ rowHTML += '<tr>';
+ $.each(self.columns, function (index, column) {
+ rowHTML += '<td>' + row[column.property] + '</td>';
+ });
+ rowHTML += '</tr>';
+ });
+
+ if (!rowHTML) rowHTML = self.placeholderRowHTML('0 ' + self.options.itemsText);
+
+ self.$tbody.html(rowHTML);
+ self.stretchHeight();
+
+ self.$element.trigger('loaded');
+ });
+
+ },
+
+ placeholderRowHTML: function (content) {
+ return '<tr><td style="text-align:center;padding:20px;border-bottom:none;" colspan="' +
+ this.columns.length + '">' + content + '</td></tr>';
+ },
+
+ headerClicked: function (e) {
+ var $target = $(e.target);
+ if (!$target.hasClass('sortable')) return;
+
+ var direction = this.options.dataOptions.sortDirection;
+ var sort = this.options.dataOptions.sortProperty;
+ var property = $target.data('property');
+
+ if (sort === property) {
+ this.options.dataOptions.sortDirection = (direction === 'asc') ? 'desc' : 'asc';
+ } else {
+ this.options.dataOptions.sortDirection = 'asc';
+ this.options.dataOptions.sortProperty = property;
+ }
+
+ this.options.dataOptions.pageIndex = 0;
+ this.updateColumns($target, this.options.dataOptions.sortDirection);
+ this.renderData();
+ },
+
+ pagesizeChanged: function (e, pageSize) {
+ if(pageSize) {
+ this.options.dataOptions.pageSize = parseInt(pageSize.value, 10);
+ } else {
+ this.options.dataOptions.pageSize = parseInt($(e.target).val(), 10);
+ }
+
+ this.options.dataOptions.pageIndex = 0;
+ this.renderData();
+ },
+
+ pageChanged: function (e) {
+ var pageRequested = parseInt($(e.target).val(), 10);
+ pageRequested = (isNaN(pageRequested)) ? 1 : pageRequested;
+ var maxPages = this.$pageslabel.text();
+
+ this.options.dataOptions.pageIndex =
+ (pageRequested > maxPages) ? maxPages - 1 : pageRequested - 1;
+
+ this.renderData();
+ },
+
+ searchChanged: function (e, search) {
+ this.options.dataOptions.search = search;
+ this.options.dataOptions.pageIndex = 0;
+ this.renderData();
+ },
+
+ filterChanged: function (e, filter) {
+ this.options.dataOptions.filter = filter;
+ this.options.dataOptions.pageIndex = 0;
+ this.renderData();
+ },
+
+ previous: function () {
+ this.$nextpagebtn.attr('disabled', 'disabled');
+ this.$prevpagebtn.attr('disabled', 'disabled');
+ this.options.dataOptions.pageIndex--;
+ this.renderData();
+ },
+
+ next: function () {
+ this.$nextpagebtn.attr('disabled', 'disabled');
+ this.$prevpagebtn.attr('disabled', 'disabled');
+ this.options.dataOptions.pageIndex++;
+ this.renderData();
+ },
+
+ reload: function () {
+ this.options.dataOptions.pageIndex = 0;
+ this.renderData();
+ },
+
+ initStretchHeight: function () {
+ this.$gridContainer = this.$element.parent();
+
+ this.$element.wrap('<div class="datagrid-stretch-wrapper">');
+ this.$stretchWrapper = this.$element.parent();
+
+ this.$headerTable = $('<table>').attr('class', this.$element.attr('class'));
+ this.$footerTable = this.$headerTable.clone();
+
+ this.$headerTable.prependTo(this.$gridContainer).addClass('datagrid-stretch-header');
+ this.$thead.detach().appendTo(this.$headerTable);
+
+ this.$sizingHeader = this.$thead.clone();
+ this.$sizingHeader.find('tr:first').remove();
+
+ this.$footerTable.appendTo(this.$gridContainer).addClass('datagrid-stretch-footer');
+ this.$tfoot.detach().appendTo(this.$footerTable);
+ },
+
+ stretchHeight: function () {
+ if (!this.$gridContainer) return;
+
+ this.setColumnWidths();
+
+ var targetHeight = this.$gridContainer.height();
+ var headerHeight = this.$headerTable.outerHeight();
+ var footerHeight = this.$footerTable.outerHeight();
+ var overhead = headerHeight + footerHeight;
+
+ this.$stretchWrapper.height(targetHeight - overhead);
+ },
+
+ setColumnWidths: function () {
+ if (!this.$sizingHeader) return;
+
+ this.$element.prepend(this.$sizingHeader);
+
+ var $sizingCells = this.$sizingHeader.find('th');
+ var columnCount = $sizingCells.length;
+
+ function matchSizingCellWidth(i, el) {
+ if (i === columnCount - 1) return;
+
+ var $el = $(el);
+ var $sourceCell = $sizingCells.eq(i);
+ var width = $sourceCell.width();
+
+ // TD needs extra width to match sorted column header
+ if ($sourceCell.hasClass('sorted') && $el.prop('tagName') === 'TD') width = width + SORTED_HEADER_OFFSET;
+
+ $el.width(width);
+ }
+
+ this.$colheader.find('th').each(matchSizingCellWidth);
+ this.$tbody.find('tr:first > td').each(matchSizingCellWidth);
+
+ this.$sizingHeader.detach();
+ }
+ };
+
+
+ // DATAGRID PLUGIN DEFINITION
+
+ $.fn.datagrid = function (option) {
+ return this.each(function () {
+ var $this = $(this);
+ var data = $this.data('datagrid');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('datagrid', (data = new Datagrid(this, options)));
+ if (typeof option === 'string') data[option]();
+ });
+ };
+
+ $.fn.datagrid.defaults = {
+ dataOptions: { pageIndex: 0, pageSize: 10 },
+ loadingHTML: '<div class="progress progress-striped active" style="width:50%;margin:auto;"><div class="bar" style="width:100%;"></div></div>',
+ itemsText: 'items',
+ itemText: 'item'
+ };
+
+ $.fn.datagrid.Constructor = Datagrid;
+
+});
--- /dev/null
+/*! Fuel UX - v2.3.1 - 2013-08-02
+* https://github.com/ExactTarget/fuelux
+* Copyright (c) 2013 ExactTarget; Licensed MIT */
+(function(){var a,b,c;(function(d){function l(a,b){var c,d,e,f,g,h,j,k,l,m,n=b&&b.split("/"),o=i.map,p=o&&o["*"]||{};if(a&&a.charAt(0)==="."&&b){n=n.slice(0,n.length-1),a=n.concat(a.split("/"));for(k=0;k<a.length;k+=1){m=a[k];if(m===".")a.splice(k,1),k-=1;else if(m==="..")if(k!==1||a[2]!==".."&&a[0]!=="..")k>0&&(a.splice(k-1,2),k-=2);else break}a=a.join("/")}if((n||p)&&o){c=a.split("/");for(k=c.length;k>0;k-=1){d=c.slice(0,k).join("/");if(n)for(l=n.length;l>0;l-=1){e=o[n.slice(0,l).join("/")];if(e){e=e[d];if(e){f=e,g=k;break}}}if(f)break;!h&&p&&p[d]&&(h=p[d],j=k)}!f&&h&&(f=h,g=j),f&&(c.splice(0,g,f),a=c.join("/"))}return a}function m(a,b){return function(){return f.apply(d,k.call(arguments,0).concat([a,b]))}}function n(a){return function(b){return l(b,a)}}function o(a){return function(b){g[a]=b}}function p(a){if(h.hasOwnProperty(a)){var b=h[a];delete h[a],j[a]=!0,e.apply(d,b)}if(!g.hasOwnProperty(a))throw new Error("No "+a);return g[a]}function q(a,b){var c,d,e=a.indexOf("!");return e!==-1?(c=l(a.slice(0,e),b),a=a.slice(e+1),d=p(c),d&&d.normalize?a=d.normalize(a,n(b)):a=l(a,b)):a=l(a,b),{f:c?c+"!"+a:a,n:a,p:d}}function r(a){return function(){return i&&i.config&&i.config[a]||{}}}var e,f,g={},h={},i={},j={},k=[].slice;e=function(a,b,c,e){var f,i,k,l,n,s=[],t;e=e||a;if(typeof c=="function"){b=!b.length&&c.length?["require","exports","module"]:b;for(n=0;n<b.length;n+=1){l=q(b[n],e),i=l.f;if(i==="require")s[n]=m(a);else if(i==="exports")s[n]=g[a]={},t=!0;else if(i==="module")f=s[n]={id:a,uri:"",exports:g[a],config:r(a)};else if(g.hasOwnProperty(i)||h.hasOwnProperty(i))s[n]=p(i);else if(l.p)l.p.load(l.n,m(e,!0),o(i),{}),s[n]=g[i];else if(!j[i])throw new Error(a+" missing "+i)}k=c.apply(g[a],s);if(a)if(f&&f.exports!==d&&f.exports!==g[a])g[a]=f.exports;else if(k!==d||!t)g[a]=k}else a&&(g[a]=c)},a=b=f=function(a,b,c,g,h){return typeof a=="string"?p(q(a,b).f):(a.splice||(i=a,b.splice?(a=b,b=c,c=null):a=d),b=b||function(){},typeof c=="function"&&(c=g,g=h),g?e(d,a,b,c):setTimeout(function(){e(d,a,b,c)},15),f)},f.config=function(a){return i=a,f},c=function(a,b,c){b.splice||(c=b,b=[]),h[a]=[a,b,c]},c.amd={jQuery:!0}})(),c("almond",function(){}),function(a){var b;c("bootstrap/bootstrap-transition",["jquery"],function(){return function(){!function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-affix",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.options=a.extend({},a.fn.affix.defaults,c),this.$window=a(window).on("scroll.affix.data-api",a.proxy(this.checkPosition,this)).on("click.affix.data-api",a.proxy(function(){setTimeout(a.proxy(this.checkPosition,this),1)},this)),this.$element=a(b),this.checkPosition()};b.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var b=a(document).height(),c=this.$window.scrollTop(),d=this.$element.offset(),e=this.options.offset,f=e.bottom,g=e.top,h="affix affix-top affix-bottom",i;typeof e!="object"&&(f=g=e),typeof g=="function"&&(g=e.top()),typeof f=="function"&&(f=e.bottom()),i=this.unpin!=null&&c+this.unpin<=d.top?!1:f!=null&&d.top+this.$element.height()>=b-f?"bottom":g!=null&&c<=g?"top":!1;if(this.affixed===i)return;this.affixed=i,this.unpin=i=="bottom"?d.top-c:null,this.$element.removeClass(h).addClass("affix"+(i?"-"+i:""))};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("affix"),f=typeof c=="object"&&c;e||d.data("affix",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.defaults={offset:0},a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-alert",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.alert.data-api",b,c.prototype.close)}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-button",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a=a+"Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-carousel",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(b){var c=this.getActiveIndex(),d=this;if(b>this.$items.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){d.to(b)}):c==b?this.pause().cycle():this.slide(b>c?"next":"prev",a(this.$items[b]))},pause:function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this,j;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h](),j=a.Event("slide",{relatedTarget:e[0],direction:g});if(e.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")}));if(a.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(j);if(j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})}else{this.$element.trigger(j);if(j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=a.extend({},a.fn.carousel.defaults,typeof c=="object"&&c),g=typeof c=="string"?c:f.slide;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),c.data()),g;e.carousel(f),(g=c.attr("data-slide-to"))&&e.data("carousel").pause().to(g).cycle(),b.preventDefault()})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-collapse",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b,c,d,e;if(this.transitioning||this.$element.hasClass("in"))return;b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find("> .accordion-group > .in");if(d&&d.length){e=d.data("collapse");if(e&&e.transitioning)return;d.collapse("hide"),e||d.data("collapse",null)}this.$element[b](0),this.transition("addClass",a.Event("show"),"shown"),a.support.transition&&this.$element[b](this.$element[0][c])},hide:function(){var b;if(this.transitioning||!this.$element.hasClass("in"))return;b=this.dimension(),this.reset(this.$element[b]()),this.transition("removeClass",a.Event("hide"),"hidden"),this.$element[b](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a!==null?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c.type=="show"&&e.reset(),e.transitioning=0,e.$element.trigger(d)};this.$element.trigger(c);if(c.isDefaultPrevented())return;this.transitioning=1,this.$element[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=a.extend({},a.fn.collapse.defaults,d.data(),typeof c=="object"&&c);e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();c[a(e).hasClass("in")?"addClass":"removeClass"]("collapsed"),a(e).collapse(f)})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-dropdown",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){function d(){a(".dropdown-backdrop").remove(),a(b).each(function(){e(a(this)).removeClass("open")})}function e(b){var c=b.attr("data-target"),d;c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,"")),d=c&&a(c);if(!d||!d.length)d=b.parent();return d}var b="[data-toggle=dropdown]",c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),f,g;if(c.is(".disabled, :disabled"))return;return f=e(c),g=f.hasClass("open"),d(),g||("ontouchstart"in document.documentElement&&a('<div class="dropdown-backdrop"/>').insertBefore(a(this)).on("click",d),f.toggleClass("open")),c.focus(),!1},keydown:function(c){var d,f,g,h,i,j;if(!/(38|40|27)/.test(c.keyCode))return;d=a(this),c.preventDefault(),c.stopPropagation();if(d.is(".disabled, :disabled"))return;h=e(d),i=h.hasClass("open");if(!i||i&&c.keyCode==27)return c.which==27&&h.find(b).focus(),d.click();f=a("[role=menu] li:not(.divider):visible a",h);if(!f.length)return;j=f.index(f.filter(":focus")),c.keyCode==38&&j>0&&j--,c.keyCode==40&&j<f.length-1&&j++,~j||(j=0),f.eq(j).focus()}};var f=a.fn.dropdown;a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=f,this},a(document).on("click.dropdown.data-api",d).on("click.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.dropdown.data-api",b,c.prototype.toggle).on("keydown.dropdown.data-api",b+", [role=menu]",c.prototype.keydown)}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-modal",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.focus().trigger("shown")}):b.$element.focus().trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?a.proxy(this.$element[0].focus,this.$element[0]):a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!b)return;e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b):b()):b&&b()}};var c=a.fn.modal;a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());b.preventDefault(),e.modal(f).one("hide",function(){c.focus()})})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-tooltip",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f,g,h,i;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,g=this.options.trigger.split(" ");for(i=g.length;i--;)h=g[i],h=="click"?this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this)):h!="manual"&&(e=h=="hover"?"mouseenter":"focus",f=h=="hover"?"mouseleave":"blur",this.$element.on(e+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f+"."+this.type,this.options.selector,a.proxy(this.leave,this)));this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,this.$element.data(),b),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a.fn[this.type].defaults,d={},e;this._options&&a.each(this._options,function(a,b){c[a]!=b&&(d[a]=b)}),e=a(b.currentTarget)[this.type](d).data(this.type);if(!e.options.delay||!e.options.delay.show)return e.show();clearTimeout(this.timeout),e.hoverState="in",this.timeout=setTimeout(function(){e.hoverState=="in"&&e.show()},e.options.delay.show)},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!c.options.delay||!c.options.delay.hide)return c.hide();c.hoverState="out",this.timeout=setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide)},show:function(){var b,c,d,e,f,g,h=a.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(h);if(h.isDefaultPrevented())return;b=this.tip(),this.setContent(),this.options.animation&&b.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,b[0],this.$element[0]):this.options.placement,b.detach().css({top:0,left:0,display:"block"}),this.options.container?b.appendTo(this.options.container):b.insertAfter(this.$element),c=this.getPosition(),d=b[0].offsetWidth,e=b[0].offsetHeight;switch(f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}this.applyPlacement(g,f),this.$element.trigger("shown")}},applyPlacement:function(a,b){var c=this.tip(),d=c[0].offsetWidth,e=c[0].offsetHeight,f,g,h,i;c.offset(a).addClass(b).addClass("in"),f=c[0].offsetWidth,g=c[0].offsetHeight,b=="top"&&g!=e&&(a.top=a.top+e-g,i=!0),b=="bottom"||b=="top"?(h=0,a.left<0&&(h=a.left*-2,a.left=0,c.offset(a),f=c[0].offsetWidth,g=c[0].offsetHeight),this.replaceArrow(h-d+f,f,"left")):this.replaceArrow(g-e,g,"top"),i&&c.offset(a)},replaceArrow:function(a,b,c){this.arrow().css(c,a?50*(1-a/b)+"%":"")},setContent:function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},hide:function(){function e(){var b=setTimeout(function(){c.off(a.support.transition.end).detach()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.detach()})}var b=this,c=this.tip(),d=a.Event("hide");this.$element.trigger(d);if(d.isDefaultPrevented())return;return c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?e():c.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var b=this.$element[0];return a.extend({},typeof b.getBoundingClientRect=="function"?b.getBoundingClientRect():{width:b.offsetWidth,height:b.offsetHeight},this.$element.offset())},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(b){var c=b?a(b.currentTarget)[this.type](this._options).data(this.type):this;c.tip().hasClass("in")?c.hide():c.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var c=a.fn.tooltip;a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-popover",["bootstrap/bootstrap-transition","bootstrap/bootstrap-tooltip"],function(){return function(){!function(a){var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=(typeof c.content=="function"?c.content.call(b[0]):c.content)||b.attr("data-content"),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-scrollspy",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll-spy.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body"),this.refresh(),this.process()}b.prototype={constructor:b,refresh:function(){var b=this,c;this.offsets=a([]),this.targets=a([]),c=this.$body.find(this.selector).map(function(){var c=a(this),d=c.data("target")||c.attr("href"),e=/^#\w/.test(d)&&a(d);return e&&e.length&&[[e.position().top+(!a.isWindow(b.$scrollElement.get(0))&&b.$scrollElement.scrollTop()),d]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,c=b-this.$scrollElement.height(),d=this.offsets,e=this.targets,f=this.activeTarget,g;if(a>=c)return f!=(g=e.last()[0])&&this.activate(g);for(g=d.length;g--;)f!=e[g]&&a>=d[g]&&(!d[g+1]||a<=d[g+1])&&this.activate(e[g])},activate:function(b){var c,d;this.activeTarget=b,a(this.selector).parent(".active").removeClass("active"),d=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',c=a(d).parent("li").addClass("active"),c.parent(".dropdown-menu").length&&(c=c.closest("li.dropdown").addClass("active")),c.trigger("activate")}};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-tab",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f,g;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active:last a")[0],g=a.Event("show",{relatedTarget:e}),b.trigger(g);if(g.isDefaultPrevented())return;f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery)}.call(a),b})}(this),function(a){var b;c("bootstrap/bootstrap-typeahead",["bootstrap/bootstrap-transition"],function(){return function(){!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=a(this.options.menu),this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(a)).change(),this.hide()},updater:function(a){return a},show:function(){var b=a.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:b.top+b.height,left:b.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(c=a.isFunction(this.source)?this.source(this.query,a.proxy(this.process,this)):this.source,c?this.process(c):this)},process:function(b){var c=this;return b=a.grep(b,function(a){return c.matcher(a)}),b=this.sorter(b),b.length?this.render(b.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){var b=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return a.replace(new RegExp("("+b+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",a.proxy(this.keydown,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this)).on("mouseleave","li",a.proxy(this.mouseleave,this))},eventSupported:function(a){var b=a in this.$element;return b||(this.$element.setAttribute(a,"return;"),b=typeof this.$element[a]=="function"),b},move:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},keydown:function(b){this.suppressKeyPressRepeat=~a.inArray(b.keyCode,[40,38,9,13,27]),this.move(b)},keypress:function(a){if(this.suppressKeyPressRepeat)return;this.move(a)},keyup:function(a){switch(a.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},focus:function(a){this.focused=!0},blur:function(a){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(a){a.stopPropagation(),a.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(b){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")},mouseleave:function(a){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var c=a.fn.typeahead;a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},a.fn.typeahead.Constructor=b,a.fn.typeahead.noConflict=function(){return a.fn.typeahead=c,this},a(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;c.typeahead(c.data())})}(window.jQuery)}.call(a),b})}(this),c("fuelux/checkbox",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.checkbox.defaults,c),this.$label=this.$element.parent(),this.$icon=this.$label.find("i"),this.$chk=this.$label.find("input[type=checkbox]"),this.setState(this.$chk),this.$chk.on("change",b.proxy(this.itemchecked,this))};c.prototype={constructor:c,setState:function(a){var b=a.is(":checked"),c=a.is(":disabled");this.$icon.removeClass("checked").removeClass("disabled"),b===!0&&this.$icon.addClass("checked"),c===!0&&this.$icon.addClass("disabled")},enable:function(){this.$chk.attr("disabled",!1),this.$icon.removeClass("disabled")},disable:function(){this.$chk.attr("disabled",!0),this.$icon.addClass("disabled")},toggle:function(){this.$chk.click()},itemchecked:function(a){var c=b(a.target);this.setState(c)}},b.fn.checkbox=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("checkbox"),h=typeof a=="object"&&a;g||f.data("checkbox",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.checkbox.defaults={},b.fn.checkbox.Constructor=c,b(function(){b(window).on("load",function(){b(".checkbox-custom > input[type=checkbox]").each(function(){var a=b(this);if(a.data("checkbox"))return;a.checkbox(a.data())})})})}),c("fuelux/util",["require","jquery"],function(a){function c(a,c){return(a.textContent||a.innerText||b(a).text()||"").toLowerCase()===(c||"").toLowerCase()}var b=a("jquery");b.expr[":"].fuelTextExactCI=b.expr.createPseudo?b.expr.createPseudo(function(a){return function(b){return c(b,a)}}):function(a,b,d){return c(a,d[3])}}),c("fuelux/combobox",["require","jquery","./util"],function(a){var b=a("jquery");a("./util");var c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.combobox.defaults,c),this.$element.on("click","a",b.proxy(this.itemclicked,this)),this.$element.on("change","input",b.proxy(this.inputchanged,this)),this.$input=this.$element.find("input"),this.$button=this.$element.find(".btn"),this.setDefaultSelection()};c.prototype={constructor:c,selectedItem:function(){var a=this.$selectedItem,c={};if(a){var d=this.$selectedItem.text();c=b.extend({text:d},this.$selectedItem.data())}else c={text:this.$input.val()};return c},selectByText:function(a){var b="li:fuelTextExactCI("+a+")";this.selectBySelector(b)},selectByValue:function(a){var b='li[data-value="'+a+'"]';this.selectBySelector(b)},selectByIndex:function(a){var b="li:eq("+a+")";this.selectBySelector(b)},selectBySelector:function(a){var b=this.$element.find(a);typeof b[0]!="undefined"?(this.$selectedItem=b,this.$input.val(this.$selectedItem.text())):this.$selectedItem=null},setDefaultSelection:function(){var a="li[data-selected=true]:first",b=this.$element.find(a);b.length>0&&(this.selectBySelector(a),b.removeData("selected"),b.removeAttr("data-selected"))},enable:function(){this.$input.removeAttr("disabled"),this.$button.removeClass("disabled")},disable:function(){this.$input.attr("disabled",!0),this.$button.addClass("disabled")},itemclicked:function(a){this.$selectedItem=b(a.target).parent(),this.$input.val(this.$selectedItem.text()).trigger("change",{synthetic:!0});var c=this.selectedItem();this.$element.trigger("changed",c),a.preventDefault()},inputchanged:function(a,c){if(c&&c.synthetic)return;var d=b(a.target).val();this.selectByText(d);var e=this.selectedItem();e.text.length===0&&(e={text:d}),this.$element.trigger("changed",e)}},b.fn.combobox=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("combobox"),h=typeof a=="object"&&a;g||f.data("combobox",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.combobox.defaults={},b.fn.combobox.Constructor=c,b(function(){b(window).on("load",function(){b(".combobox").each(function(){var a=b(this);if(a.data("combobox"))return;a.combobox(a.data())})}),b("body").on("mousedown.combobox.data-api",".combobox",function(a){var c=b(this);if(c.data("combobox"))return;c.combobox(c.data())})})}),c("fuelux/datagrid",["require","jquery"],function(a){var b=a("jquery"),c=22,d=function(a,c){this.$element=b(a),this.$thead=this.$element.find("thead"),this.$tfoot=this.$element.find("tfoot"),this.$footer=this.$element.find("tfoot th"),this.$footerchildren=this.$footer.children().show().css("visibility","hidden"),this.$topheader=this.$element.find("thead th"),this.$searchcontrol=this.$element.find(".datagrid-search"),this.$filtercontrol=this.$element.find(".filter"),this.$pagesize=this.$element.find(".grid-pagesize"),this.$pageinput=this.$element.find(".grid-pager input"),this.$pagedropdown=this.$element.find(".grid-pager .dropdown-menu"),this.$prevpagebtn=this.$element.find(".grid-prevpage"),this.$nextpagebtn=this.$element.find(".grid-nextpage"),this.$pageslabel=this.$element.find(".grid-pages"),this.$countlabel=this.$element.find(".grid-count"),this.$startlabel=this.$element.find(".grid-start"),this.$endlabel=this.$element.find(".grid-end"),this.$tbody=b("<tbody>").insertAfter(this.$thead),this.$colheader=b("<tr>").appendTo(this.$thead),this.options=b.extend(!0,{},b.fn.datagrid.defaults,c),this.$pagesize.hasClass("select")?this.options.dataOptions.pageSize=parseInt(this.$pagesize.select("selectedItem").value,10):this.options.dataOptions.pageSize=parseInt(this.$pagesize.val(),10),this.$searchcontrol.length<=0&&(this.$searchcontrol=this.$element.find(".search")),this.columns=this.options.dataSource.columns(),this.$nextpagebtn.on("click",b.proxy(this.next,this)),this.$prevpagebtn.on("click",b.proxy(this.previous,this)),this.$searchcontrol.on("searched cleared",b.proxy(this.searchChanged,this)),this.$filtercontrol.on("changed",b.proxy(this.filterChanged,this)),this.$colheader.on("click","th",b.proxy(this.headerClicked,this)),this.$pagesize.hasClass("select")?this.$pagesize.on("changed",b.proxy(this.pagesizeChanged,this)):this.$pagesize.on("change",b.proxy(this.pagesizeChanged,this)),this.$pageinput.on("change",b.proxy(this.pageChanged,this)),this.renderColumns(),this.options.stretchHeight&&this.initStretchHeight(),this.renderData()};d.prototype={constructor:d,renderColumns:function(){var a=this;this.$footer.attr("colspan",this.columns.length),this.$topheader.attr("colspan",this.columns.length);var c="";b.each(this.columns,function(a,b){c+='<th data-property="'+b.property+'"',b.sortable&&(c+=' class="sortable"'),c+=">"+b.label+"</th>"}),a.$colheader.append(c)},updateColumns:function(a,b){this._updateColumns(this.$colheader,a,b),this.$sizingHeader&&this._updateColumns(this.$sizingHeader,this.$sizingHeader.find("th").eq(a.index()),b)},_updateColumns:function(a,c,d){var e=d==="asc"?"icon-chevron-up":"icon-chevron-down";a.find("i.datagrid-sort").remove(),a.find("th").removeClass("sorted"),b("<i>").addClass(e+" datagrid-sort").appendTo(c),c.addClass("sorted")},updatePageDropdown:function(a){var b="";for(var c=1;c<=a.pages;c++)b+="<li><a>"+c+"</a></li>";this.$pagedropdown.html(b)},updatePageButtons:function(a){a.page===1?this.$prevpagebtn.attr("disabled","disabled"):this.$prevpagebtn.removeAttr("disabled"),a.page===a.pages?this.$nextpagebtn.attr("disabled","disabled"):this.$nextpagebtn.removeAttr("disabled")},renderData:function(){var a=this;this.$tbody.html(this.placeholderRowHTML(this.options.loadingHTML)),this.options.dataSource.data(this.options.dataOptions,function(c){var d=c.count===1?a.options.itemText:a.options.itemsText,e="";a.$footerchildren.css("visibility",function(){return c.count>0?"visible":"hidden"}),a.$pageinput.val(c.page),a.$pageslabel.text(c.pages),a.$countlabel.text(c.count+" "+d),a.$startlabel.text(c.start),a.$endlabel.text(c.end),a.updatePageDropdown(c),a.updatePageButtons(c),b.each(c.data,function(c,d){e+="<tr>",b.each(a.columns,function(a,b){e+="<td>"+d[b.property]+"</td>"}),e+="</tr>"}),e||(e=a.placeholderRowHTML("0 "+a.options.itemsText)),a.$tbody.html(e),a.stretchHeight(),a.$element.trigger("loaded")})},placeholderRowHTML:function(a){return'<tr><td style="text-align:center;padding:20px;border-bottom:none;" colspan="'+this.columns.length+'">'+a+"</td></tr>"},headerClicked:function(a){var c=b(a.target);if(!c.hasClass("sortable"))return;var d=this.options.dataOptions.sortDirection,e=this.options.dataOptions.sortProperty,f=c.data("property");e===f?this.options.dataOptions.sortDirection=d==="asc"?"desc":"asc":(this.options.dataOptions.sortDirection="asc",this.options.dataOptions.sortProperty=f),this.options.dataOptions.pageIndex=0,this.updateColumns(c,this.options.dataOptions.sortDirection),this.renderData()},pagesizeChanged:function(a,c){c?this.options.dataOptions.pageSize=parseInt(c.value,10):this.options.dataOptions.pageSize=parseInt(b(a.target).val(),10),this.options.dataOptions.pageIndex=0,this.renderData()},pageChanged:function(a){var c=parseInt(b(a.target).val(),10);c=isNaN(c)?1:c;var d=this.$pageslabel.text();this.options.dataOptions.pageIndex=c>d?d-1:c-1,this.renderData()},searchChanged:function(a,b){this.options.dataOptions.search=b,this.options.dataOptions.pageIndex=0,this.renderData()},filterChanged:function(a,b){this.options.dataOptions.filter=b,this.options.dataOptions.pageIndex=0,this.renderData()},previous:function(){this.$nextpagebtn.attr("disabled","disabled"),this.$prevpagebtn.attr("disabled","disabled"),this.options.dataOptions.pageIndex--,this.renderData()},next:function(){this.$nextpagebtn.attr("disabled","disabled"),this.$prevpagebtn.attr("disabled","disabled"),this.options.dataOptions.pageIndex++,this.renderData()},reload:function(){this.options.dataOptions.pageIndex=0,this.renderData()},initStretchHeight:function(){this.$gridContainer=this.$element.parent(),this.$element.wrap('<div class="datagrid-stretch-wrapper">'),this.$stretchWrapper=this.$element.parent(),this.$headerTable=b("<table>").attr("class",this.$element.attr("class")),this.$footerTable=this.$headerTable.clone(),this.$headerTable.prependTo(this.$gridContainer).addClass("datagrid-stretch-header"),this.$thead.detach().appendTo(this.$headerTable),this.$sizingHeader=this.$thead.clone(),this.$sizingHeader.find("tr:first").remove(),this.$footerTable.appendTo(this.$gridContainer).addClass("datagrid-stretch-footer"),this.$tfoot.detach().appendTo(this.$footerTable)},stretchHeight:function(){if(!this.$gridContainer)return;this.setColumnWidths();var a=this.$gridContainer.height(),b=this.$headerTable.outerHeight(),c=this.$footerTable.outerHeight(),d=b+c;this.$stretchWrapper.height(a-d)},setColumnWidths:function(){function e(e,f){if(e===d-1)return;var g=b(f),h=a.eq(e),i=h.width();h.hasClass("sorted")&&g.prop("tagName")==="TD"&&(i=i+c),g.width(i)}if(!this.$sizingHeader)return;this.$element.prepend(this.$sizingHeader);var a=this.$sizingHeader.find("th"),d=a.length;this.$colheader.find("th").each(e),this.$tbody.find("tr:first > td").each(e),this.$sizingHeader.detach()}},b.fn.datagrid=function(a){return this.each(function(){var c=b(this),e=c.data("datagrid"),f=typeof a=="object"&&a;e||c.data("datagrid",e=new d(this,f)),typeof a=="string"&&e[a]()})},b.fn.datagrid.defaults={dataOptions:{pageIndex:0,pageSize:10},loadingHTML:'<div class="progress progress-striped active" style="width:50%;margin:auto;"><div class="bar" style="width:100%;"></div></div>',itemsText:"items",itemText:"item"},b.fn.datagrid.Constructor=d}),c("fuelux/pillbox",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.pillbox.defaults,c),this.$element.on("click","li",b.proxy(this.itemclicked,this))};c.prototype={constructor:c,items:function(){return this.$element.find("li").map(function(){var a=b(this);return b.extend({text:a.text()},a.data())}).get()},itemclicked:function(a){b(a.currentTarget).remove(),a.preventDefault()}},b.fn.pillbox=function(a){var d,e=this.each(function(){var e=b(this),f=e.data("pillbox"),g=typeof a=="object"&&a;f||e.data("pillbox",f=new c(this,g)),typeof a=="string"&&(d=f[a]())});return d===undefined?e:d},b.fn.pillbox.defaults={},b.fn.pillbox.Constructor=c,b(function(){b("body").on("mousedown.pillbox.data-api",".pillbox",function(a){var c=b(this);if(c.data("pillbox"))return;c.pillbox(c.data())})})}),c("fuelux/radio",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.radio.defaults,c),this.$label=this.$element.parent(),this.$icon=this.$label.find("i"),this.$radio=this.$label.find("input[type=radio]"),this.groupName=this.$radio.attr("name"),this.setState(this.$radio),this.$radio.on("change",b.proxy(this.itemchecked,this))};c.prototype={constructor:c,setState:function(a,b){var c=a.is(":checked"),d=a.is(":disabled");c===!0&&this.$icon.addClass("checked"),d===!0&&this.$icon.addClass("disabled")},resetGroup:function(){b("input[name="+this.groupName+"]").next().removeClass("checked")},enable:function(){this.$radio.attr("disabled",!1),this.$icon.removeClass("disabled")},disable:function(){this.$radio.attr("disabled",!0),this.$icon.addClass("disabled")},itemchecked:function(a){var c=b(a.target);this.resetGroup(),this.setState(c)}},b.fn.radio=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("radio"),h=typeof a=="object"&&a;g||f.data("radio",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.radio.defaults={},b.fn.radio.Constructor=c,b(function(){b(window).on("load",function(){b(".radio-custom > input[type=radio]").each(function(){var a=b(this);if(a.data("radio"))return;a.radio(a.data())})})})}),c("fuelux/search",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.search.defaults,c),this.$button=this.$element.find("button").on("click",b.proxy(this.buttonclicked,this)),this.$input=this.$element.find("input").on("keydown",b.proxy(this.keypress,this)).on("keyup",b.proxy(this.keypressed,this)),this.$icon=this.$element.find("i"),this.activeSearch=""};c.prototype={constructor:c,search:function(a){this.$icon.attr("class","icon-remove"),this.activeSearch=a,this.$element.trigger("searched",a)},clear:function(){this.$icon.attr("class","icon-search"),this.activeSearch="",this.$input.val(""),this.$element.trigger("cleared")},action:function(){var a=this.$input.val(),b=a===""||a===this.activeSearch;this.activeSearch&&b?this.clear():a&&this.search(a)},buttonclicked:function(a){a.preventDefault();if(b(a.currentTarget).is(".disabled, :disabled"))return;this.action()},keypress:function(a){a.which===13&&a.preventDefault()},keypressed:function(a){var b,c;a.which===13?(a.preventDefault(),this.action()):(b=this.$input.val(),c=b&&b===this.activeSearch,this.$icon.attr("class",c?"icon-remove":"icon-search"))},disable:function(){this.$input.attr("disabled","disabled"),this.$button.addClass("disabled")},enable:function(){this.$input.removeAttr("disabled"),this.$button.removeClass("disabled")}},b.fn.search=function(a){return this.each(function(){var d=b(this),e=d.data("search"),f=typeof a=="object"&&a;e||d.data("search",e=new c(this,f)),typeof a=="string"&&e[a]()})},b.fn.search.defaults={},b.fn.search.Constructor=c,b(function(){b("body").on("mousedown.search.data-api",".search",function(){var a=b(this);if(a.data("search"))return;a.search(a.data())})})}),c("fuelux/spinner",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.spinner.defaults,c),this.$input=this.$element.find(".spinner-input"),this.$element.on("keyup",this.$input,b.proxy(this.change,this)),this.options.hold?(this.$element.on("mousedown",".spinner-up",b.proxy(function(){this.startSpin(!0)},this)),this.$element.on("mouseup",".spinner-up, .spinner-down",b.proxy(this.stopSpin,this)),this.$element.on("mouseout",".spinner-up, .spinner-down",b.proxy(this.stopSpin,this)),this.$element.on("mousedown",".spinner-down",b.proxy(function(){this.startSpin(!1)},this))):(this.$element.on("click",".spinner-up",b.proxy(function(){this.step(!0)},this)),this.$element.on("click",".spinner-down",b.proxy(function(){this.step(!1)},this))),this.switches={count:1,enabled:!0},this.options.speed==="medium"?this.switches.speed=300:this.options.speed==="fast"?this.switches.speed=100:this.switches.speed=500,this.lastValue=null,this.render(),this.options.disabled&&this.disable()};c.prototype={constructor:c,render:function(){this.$input.val(this.options.value),this.$input.attr("maxlength",(this.options.max+"").split("").length)},change:function(){var a=this.$input.val();a/1?this.options.value=a/1:(a=a.replace(/[^0-9]/g,""),this.$input.val(a),this.options.value=a/1),this.triggerChangedEvent()},stopSpin:function(){clearTimeout(this.switches.timeout),this.switches.count=1,this.triggerChangedEvent()},triggerChangedEvent:function(){var a=this.value();if(a===this.lastValue)return;this.lastValue=a,this.$element.trigger("changed",a),this.$element.trigger("change")},startSpin:function(a){if(!this.options.disabled){var c=this.switches.count;c===1?(this.step(a),c=1):c<3?c=1.5:c<8?c=2.5:c=4,this.switches.timeout=setTimeout(b.proxy(function(){this.iterator(a)},this),this.switches.speed/c),this.switches.count++}},iterator:function(a){this.step(a),this.startSpin(a)},step:function(a){var b=this.options.value,c=a?this.options.max:this.options.min;if(a?b<c:b>c){var d=b+(a?1:-1)*this.options.step;(a?d>c:d<c)?this.value(c):this.value(d)}},value:function(a){return!isNaN(parseFloat(a))&&isFinite(a)?(a=parseFloat(a),this.options.value=a,this.$input.val(a),this):this.options.value},disable:function(){this.options.disabled=!0,this.$input.attr("disabled",""),this.$element.find("button").addClass("disabled")},enable:function(){this.options.disabled=!1,this.$input.removeAttr("disabled"),this.$element.find("button").removeClass("disabled")}},b.fn.spinner=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("spinner"),h=typeof a=="object"&&a;g||f.data("spinner",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.spinner.defaults={value:1,min:1,max:999,step:1,hold:!0,speed:"medium",disabled:!1},b.fn.spinner.Constructor=c,b(function(){b("body").on("mousedown.spinner.data-api",".spinner",function(a){var c=b(this);if(c.data("spinner"))return;c.spinner(c.data())})})}),c("fuelux/select",["require","jquery","./util"],function(a){var b=a("jquery");a("./util");var c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.select.defaults,c),this.$element.on("click","a",b.proxy(this.itemclicked,this)),this.$button=this.$element.find(".btn"),this.$label=this.$element.find(".dropdown-label"),this.setDefaultSelection(),c.resize==="auto"&&this.resize()};c.prototype={constructor:c,itemclicked:function(a){this.$selectedItem=b(a.target).parent(),this.$label.text(this.$selectedItem.text());var c=this.selectedItem();this.$element.trigger("changed",c),a.preventDefault()},resize:function(){var a=b("#selectTextSize")[0];a||b("<div/>").attr({id:"selectTextSize"}).appendTo("body");var c=0,d=0;this.$element.find("a").each(function(){var a=b(this),e=a.text(),f=b("#selectTextSize");f.text(e),d=f.outerWidth(),d>c&&(c=d)}),this.$label.width(c)},selectedItem:function(){var a=this.$selectedItem.text();return b.extend({text:a},this.$selectedItem.data())},selectByText:function(a){var b="li a:fuelTextExactCI("+a+")";this.selectBySelector(b)},selectByValue:function(a){var b='li[data-value="'+a+'"]';this.selectBySelector(b)},selectByIndex:function(a){var b="li:eq("+a+")";this.selectBySelector(b)},selectBySelector:function(a){var b=this.$element.find(a);this.$selectedItem=b,this.$label.text(this.$selectedItem.text())},setDefaultSelection:function(){var a="li[data-selected=true]:first",b=this.$element.find(a);b.length===0?this.selectByIndex(0):(this.selectBySelector(a),b.removeData("selected"),b.removeAttr("data-selected"))},enable:function(){this.$button.removeClass("disabled")},disable:function(){this.$button.addClass("disabled")}},b.fn.select=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("select"),h=typeof a=="object"&&a;g||f.data("select",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.select.defaults={},b.fn.select.Constructor=c,b(function(){b(window).on("load",function(){b(".select").each(function(){var a=b(this);if(a.data("select"))return;a.select(a.data())})}),b("body").on("mousedown.select.data-api",".select",function(a){var c=b(this);if(c.data("select"))return;c.select(c.data())})})}),c("fuelux/tree",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){this.$element=b(a),this.options=b.extend({},b.fn.tree.defaults,c),this.$element.on("click",".tree-item",b.proxy(function(a){this.selectItem(a.currentTarget)},this)),this.$element.on("click",".tree-folder-header",b.proxy(function(a){this.selectFolder(a.currentTarget)},this)),this.render()};c.prototype={constructor:c,render:function(){this.populate(this.$element)},populate:function(a){var c=this,d=a.parent().find(".tree-loader:eq(0)");d.show(),this.options.dataSource.data(a.data(),function(e){d.hide(),b.each(e.data,function(b,d){var e;d.type==="folder"?(e=c.$element.find(".tree-folder:eq(0)").clone().show(),e.find(".tree-folder-name").html(d.name),e.find(".tree-loader").html(c.options.loadingHTML),e.find(".tree-folder-header").data(d)):d.type==="item"&&(e=c.$element.find(".tree-item:eq(0)").clone().show(),e.find(".tree-item-name").html(d.name),e.data(d)),a.hasClass("tree-folder-header")?a.parent().find(".tree-folder-content:eq(0)").append(e):a.append(e)}),c.$element.trigger("loaded")})},selectItem:function(a){var c=b(a),d=this.$element.find(".tree-selected"),e=[];this.options.multiSelect?b.each(d,function(a,d){var f=b(d);f[0]!==c[0]&&e.push(b(d).data())}):d[0]!==c[0]&&(d.removeClass("tree-selected").find("i").removeClass("icon-ok").addClass("tree-dot"),e.push(c.data())),c.hasClass("tree-selected")?(c.removeClass("tree-selected"),c.find("i").removeClass("icon-ok").addClass("tree-dot")):(c.addClass("tree-selected"),c.find("i").removeClass("tree-dot").addClass("icon-ok"),this.options.multiSelect&&e.push(c.data())),e.length&&this.$element.trigger("selected",{info:e})},selectFolder:function(a){var c=b(a),d=c.parent();c.find(".icon-folder-close").length?(d.find(".tree-folder-content").children().length?d.find(".tree-folder-content:eq(0)").show():this.populate(c),d.find(".icon-folder-close:eq(0)").removeClass("icon-folder-close").addClass("icon-folder-open"),this.$element.trigger("opened",c.data())):(this.options.cacheItems?d.find(".tree-folder-content:eq(0)").hide():d.find(".tree-folder-content:eq(0)").empty(),d.find(".icon-folder-open:eq(0)").removeClass("icon-folder-open").addClass("icon-folder-close"),this.$element.trigger("closed",c.data()))},selectedItems:function(){var a=this.$element.find(".tree-selected"),c=[];return b.each(a,function(a,d){c.push(b(d).data())}),c}},b.fn.tree=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("tree"),h=typeof a=="object"&&a;g||f.data("tree",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.tree.defaults={multiSelect:!1,loadingHTML:"<div>Loading...</div>",cacheItems:!0},b.fn.tree.Constructor=c}),c("fuelux/wizard",["require","jquery"],function(a){var b=a("jquery"),c=function(a,c){var d;this.$element=b(a),this.options=b.extend({},b.fn.wizard.defaults,c),this.currentStep=1,this.numSteps=this.$element.find("li").length,this.$prevBtn=this.$element.find("button.btn-prev"),this.$nextBtn=this.$element.find("button.btn-next"),d=this.$nextBtn.children().detach(),this.nextText=b.trim(this.$nextBtn.text()),this.$nextBtn.append(d),this.$prevBtn.on("click",b.proxy(this.previous,this)),this.$nextBtn.on("click",b.proxy(this.next,this)),this.$element.on("click","li.complete",b.proxy(this.stepclicked,this))};c.prototype={constructor:c,setState:function(){var a=this.currentStep>1,c=this.currentStep===1,d=this.currentStep===this.numSteps;this.$prevBtn.attr("disabled",c===!0||a===!1);var e=this.$nextBtn.data();if(e&&e.last){this.lastText=e.last;if(typeof this.lastText!="undefined"){var f=d!==!0?this.nextText:this.lastText,g=this.$nextBtn.children().detach();this.$nextBtn.text(f).append(g)}}var h=this.$element.find("li");h.removeClass("active").removeClass("complete"),h.find("span.badge").removeClass("badge-info").removeClass("badge-success");var i="li:lt("+(this.currentStep-1)+")",j=this.$element.find(i);j.addClass("complete"),j.find("span.badge").addClass("badge-success");var k="li:eq("+(this.currentStep-1)+")",l=this.$element.find(k);l.addClass("active"),l.find("span.badge").addClass("badge-info");var m=l.data().target;b(".step-pane").removeClass("active"),b(m).addClass("active"),this.$element.trigger("changed")},stepclicked:function(a){var c=b(a.currentTarget),d=b(".steps li").index(c),e=b.Event("stepclick");this.$element.trigger(e,{step:d+1});if(e.isDefaultPrevented())return;this.currentStep=d+1,this.setState()},previous:function(){var a=this.currentStep>1;if(a){var c=b.Event("change");this.$element.trigger(c,{step:this.currentStep,direction:"previous"});if(c.isDefaultPrevented())return;this.currentStep-=1,this.setState()}},next:function(){var a=this.currentStep+1<=this.numSteps,c=this.currentStep===this.numSteps;if(a){var d=b.Event("change");this.$element.trigger(d,{step:this.currentStep,direction:"next"});if(d.isDefaultPrevented())return;this.currentStep+=1,this.setState()}else c&&this.$element.trigger("finished")},selectedItem:function(a){return{step:this.currentStep}}},b.fn.wizard=function(a,d){var e,f=this.each(function(){var f=b(this),g=f.data("wizard"),h=typeof a=="object"&&a;g||f.data("wizard",g=new c(this,h)),typeof a=="string"&&(e=g[a](d))});return e===undefined?f:e},b.fn.wizard.defaults={},b.fn.wizard.Constructor=c,b(function(){b("body").on("mousedown.wizard.data-api",".wizard",function(){var a=b(this);if(a.data("wizard"))return;a.wizard(a.data())})})}),c("fuelux/all",["require","jquery","bootstrap/bootstrap-affix","bootstrap/bootstrap-alert","bootstrap/bootstrap-button","bootstrap/bootstrap-carousel","bootstrap/bootstrap-collapse","bootstrap/bootstrap-dropdown","bootstrap/bootstrap-modal","bootstrap/bootstrap-popover","bootstrap/bootstrap-scrollspy","bootstrap/bootstrap-tab","bootstrap/bootstrap-tooltip","bootstrap/bootstrap-transition","bootstrap/bootstrap-typeahead","fuelux/checkbox","fuelux/combobox","fuelux/datagrid","fuelux/pillbox","fuelux/radio","fuelux/search","fuelux/spinner","fuelux/select","fuelux/tree","fuelux/wizard"],function(a){a("jquery"),a("bootstrap/bootstrap-affix"),a("bootstrap/bootstrap-alert"),a("bootstrap/bootstrap-button"),a("bootstrap/bootstrap-carousel"),a("bootstrap/bootstrap-collapse"),a("bootstrap/bootstrap-dropdown"),a("bootstrap/bootstrap-modal"),a("bootstrap/bootstrap-popover"),a("bootstrap/bootstrap-scrollspy"),a("bootstrap/bootstrap-tab"),a("bootstrap/bootstrap-tooltip"),a("bootstrap/bootstrap-transition"),a("bootstrap/bootstrap-typeahead"),a("fuelux/checkbox"),a("fuelux/combobox"),a("fuelux/datagrid"),a("fuelux/pillbox"),a("fuelux/radio"),a("fuelux/search"),a("fuelux/spinner"),a("fuelux/select"),a("fuelux/tree"),a("fuelux/wizard")}),c("jquery",[],function(){return jQuery}),c("fuelux/loader",["fuelux/all"],function(){}),b("fuelux/loader")})();
\ No newline at end of file
--- /dev/null
+/*
+ * Fuel UX Pillbox
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery'],function(require) {
+
+ var $ = require('jquery');
+
+
+ // PILLBOX CONSTRUCTOR AND PROTOTYPE
+
+ var Pillbox = function (element, options) {
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.pillbox.defaults, options);
+ this.$element.on('click', 'li', $.proxy(this.itemclicked, this));
+ };
+
+ Pillbox.prototype = {
+ constructor: Pillbox,
+
+ items: function() {
+ return this.$element.find('li').map(function() {
+ var $this = $(this);
+ return $.extend({ text: $this.text() }, $this.data());
+ }).get();
+ },
+
+ itemclicked: function (e) {
+ $(e.currentTarget).remove();
+ e.preventDefault();
+ }
+ };
+
+
+ // PILLBOX PLUGIN DEFINITION
+
+ $.fn.pillbox = function (option) {
+ var methodReturn;
+
+ var $set = this.each(function () {
+ var $this = $(this);
+ var data = $this.data('pillbox');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('pillbox', (data = new Pillbox(this, options)));
+ if (typeof option === 'string') methodReturn = data[option]();
+ });
+
+ return (methodReturn === undefined) ? $set : methodReturn;
+ };
+
+ $.fn.pillbox.defaults = {};
+
+ $.fn.pillbox.Constructor = Pillbox;
+
+
+ // PILLBOX DATA-API
+
+ $(function () {
+ $('body').on('mousedown.pillbox.data-api', '.pillbox', function (e) {
+ var $this = $(this);
+ if ($this.data('pillbox')) return;
+ $this.pillbox($this.data());
+ });
+ });
+
+});
+
--- /dev/null
+/*
+ * Fuel UX Radio
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery'],function (require) {
+
+ var $ = require('jquery');
+
+
+ // RADIO CONSTRUCTOR AND PROTOTYPE
+
+ var Radio = function (element, options) {
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.radio.defaults, options);
+
+ // cache elements
+ this.$label = this.$element.parent();
+ this.$icon = this.$label.find('i');
+ this.$radio = this.$label.find('input[type=radio]');
+ this.groupName = this.$radio.attr('name');
+
+ // set default state
+ this.setState(this.$radio);
+
+ // handle events
+ this.$radio.on('change', $.proxy(this.itemchecked, this));
+ };
+
+ Radio.prototype = {
+
+ constructor: Radio,
+
+ setState: function ($radio, resetGroupState) {
+ var checked = $radio.is(':checked');
+ var disabled = $radio.is(':disabled');
+
+ // set state of radio
+ if (checked === true) {
+ this.$icon.addClass('checked');
+ }
+ if (disabled === true) {
+ this.$icon.addClass('disabled');
+ }
+ },
+
+ resetGroup: function () {
+ // reset all radio buttons in group
+ $('input[name=' + this.groupName + ']').next().removeClass('checked');
+ },
+
+ enable: function () {
+ this.$radio.attr('disabled', false);
+ this.$icon.removeClass('disabled');
+ },
+
+ disable: function () {
+ this.$radio.attr('disabled', true);
+ this.$icon.addClass('disabled');
+ },
+
+ itemchecked: function (e) {
+ var radio = $(e.target);
+
+ this.resetGroup();
+ this.setState(radio);
+ }
+ };
+
+
+ // RADIO PLUGIN DEFINITION
+
+ $.fn.radio = function (option, value) {
+ var methodReturn;
+
+ var $set = this.each(function () {
+ var $this = $(this);
+ var data = $this.data('radio');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('radio', (data = new Radio(this, options)));
+ if (typeof option === 'string') methodReturn = data[option](value);
+ });
+
+ return (methodReturn === undefined) ? $set : methodReturn;
+ };
+
+ $.fn.radio.defaults = {};
+
+ $.fn.radio.Constructor = Radio;
+
+
+ // RADIO DATA-API
+
+ $(function () {
+ $(window).on('load', function () {
+ //$('i.radio').each(function () {
+ $('.radio-custom > input[type=radio]').each(function () {
+ var $this = $(this);
+ if ($this.data('radio')) return;
+ $this.radio($this.data());
+ });
+ });
+ });
+
+});
--- /dev/null
+/*
+ * Fuel UX Search
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery'],function(require) {
+
+ var $ = require('jquery');
+
+
+ // SEARCH CONSTRUCTOR AND PROTOTYPE
+
+ var Search = function (element, options) {
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.search.defaults, options);
+
+ this.$button = this.$element.find('button')
+ .on('click', $.proxy(this.buttonclicked, this));
+
+ this.$input = this.$element.find('input')
+ .on('keydown', $.proxy(this.keypress, this))
+ .on('keyup', $.proxy(this.keypressed, this));
+
+ this.$icon = this.$element.find('i');
+ this.activeSearch = '';
+ };
+
+ Search.prototype = {
+
+ constructor: Search,
+
+ search: function (searchText) {
+ this.$icon.attr('class', 'icon-remove');
+ this.activeSearch = searchText;
+ this.$element.trigger('searched', searchText);
+ },
+
+ clear: function () {
+ this.$icon.attr('class', 'icon-search');
+ this.activeSearch = '';
+ this.$input.val('');
+ this.$element.trigger('cleared');
+ },
+
+ action: function () {
+ var val = this.$input.val();
+ var inputEmptyOrUnchanged = val === '' || val === this.activeSearch;
+
+ if (this.activeSearch && inputEmptyOrUnchanged) {
+ this.clear();
+ } else if (val) {
+ this.search(val);
+ }
+ },
+
+ buttonclicked: function (e) {
+ e.preventDefault();
+ if ($(e.currentTarget).is('.disabled, :disabled')) return;
+ this.action();
+ },
+
+ keypress: function (e) {
+ if (e.which === 13) {
+ e.preventDefault();
+ }
+ },
+
+ keypressed: function (e) {
+ var val, inputPresentAndUnchanged;
+
+ if (e.which === 13) {
+ e.preventDefault();
+ this.action();
+ } else {
+ val = this.$input.val();
+ inputPresentAndUnchanged = val && (val === this.activeSearch);
+ this.$icon.attr('class', inputPresentAndUnchanged ? 'icon-remove' : 'icon-search');
+ }
+ },
+
+ disable: function () {
+ this.$input.attr('disabled', 'disabled');
+ this.$button.addClass('disabled');
+ },
+
+ enable: function () {
+ this.$input.removeAttr('disabled');
+ this.$button.removeClass('disabled');
+ }
+
+ };
+
+
+ // SEARCH PLUGIN DEFINITION
+
+ $.fn.search = function (option) {
+ return this.each(function () {
+ var $this = $(this);
+ var data = $this.data('search');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('search', (data = new Search(this, options)));
+ if (typeof option === 'string') data[option]();
+ });
+ };
+
+ $.fn.search.defaults = {};
+
+ $.fn.search.Constructor = Search;
+
+
+ // SEARCH DATA-API
+
+ $(function () {
+ $('body').on('mousedown.search.data-api', '.search', function () {
+ var $this = $(this);
+ if ($this.data('search')) return;
+ $this.search($this.data());
+ });
+ });
+
+});
--- /dev/null
+/*
+ * Fuel UX Select
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery','./util'],function(require) {
+
+ var $ = require('jquery');
+ require('./util');
+
+ // SELECT CONSTRUCTOR AND PROTOTYPE
+
+ var Select = function (element, options) {
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.select.defaults, options);
+ this.$element.on('click', 'a', $.proxy(this.itemclicked, this));
+ this.$button = this.$element.find('.btn');
+ this.$label = this.$element.find('.dropdown-label');
+ this.setDefaultSelection();
+
+ if (options.resize === 'auto') {
+ this.resize();
+ }
+ };
+
+ Select.prototype = {
+
+ constructor: Select,
+
+ itemclicked: function (e) {
+ this.$selectedItem = $(e.target).parent();
+ this.$label.text(this.$selectedItem.text());
+
+ // pass object including text and any data-attributes
+ // to onchange event
+ var data = this.selectedItem();
+
+ // trigger changed event
+ this.$element.trigger('changed', data);
+
+ e.preventDefault();
+ },
+
+ resize: function() {
+ var el = $('#selectTextSize')[0];
+
+ // create element if it doesn't exist
+ // used to calculate the length of the longest string
+ if(!el) {
+ $('<div/>').attr({id:'selectTextSize'}).appendTo('body');
+ }
+
+ var width = 0;
+ var newWidth = 0;
+
+ // iterate through each item to find longest string
+ this.$element.find('a').each(function () {
+ var $this = $(this);
+ var txt = $this.text();
+ var $txtSize = $('#selectTextSize');
+ $txtSize.text(txt);
+ newWidth = $txtSize.outerWidth();
+ if(newWidth > width) {
+ width = newWidth;
+ }
+ });
+
+ this.$label.width(width);
+ },
+
+ selectedItem: function() {
+ var txt = this.$selectedItem.text();
+ return $.extend({ text: txt }, this.$selectedItem.data());
+ },
+
+ selectByText: function(text) {
+ var selector = 'li a:fuelTextExactCI(' + text + ')';
+ this.selectBySelector(selector);
+ },
+
+ selectByValue: function(value) {
+ var selector = 'li[data-value="' + value + '"]';
+ this.selectBySelector(selector);
+ },
+
+ selectByIndex: function(index) {
+ // zero-based index
+ var selector = 'li:eq(' + index + ')';
+ this.selectBySelector(selector);
+ },
+
+ selectBySelector: function(selector) {
+ var item = this.$element.find(selector);
+
+ this.$selectedItem = item;
+ this.$label.text(this.$selectedItem.text());
+ },
+
+ setDefaultSelection: function() {
+ var selector = 'li[data-selected=true]:first';
+ var item = this.$element.find(selector);
+ if(item.length === 0) {
+ // select first item
+ this.selectByIndex(0);
+ }
+ else {
+ // select by data-attribute
+ this.selectBySelector(selector);
+ item.removeData('selected');
+ item.removeAttr('data-selected');
+ }
+ },
+
+ enable: function() {
+ this.$button.removeClass('disabled');
+ },
+
+ disable: function() {
+ this.$button.addClass('disabled');
+ }
+
+ };
+
+
+ // SELECT PLUGIN DEFINITION
+
+ $.fn.select = function (option,value) {
+ var methodReturn;
+
+ var $set = this.each(function () {
+ var $this = $(this);
+ var data = $this.data('select');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('select', (data = new Select(this, options)));
+ if (typeof option === 'string') methodReturn = data[option](value);
+ });
+
+ return (methodReturn === undefined) ? $set : methodReturn;
+ };
+
+ $.fn.select.defaults = {};
+
+ $.fn.select.Constructor = Select;
+
+
+ // SELECT DATA-API
+
+ $(function () {
+
+ $(window).on('load', function () {
+ $('.select').each(function () {
+ var $this = $(this);
+ if ($this.data('select')) return;
+ $this.select($this.data());
+ });
+ });
+
+ $('body').on('mousedown.select.data-api', '.select', function (e) {
+ var $this = $(this);
+ if ($this.data('select')) return;
+ $this.select($this.data());
+ });
+ });
+
+});
--- /dev/null
+/*\r
+ * Fuel UX Spinner\r
+ * https://github.com/ExactTarget/fuelux\r
+ *\r
+ * Copyright (c) 2012 ExactTarget\r
+ * Licensed under the MIT license.\r
+ */\r
+\r
+define(['require','jquery'],function(require) {\r
+\r
+ var $ = require('jquery');\r
+\r
+\r
+ // SPINNER CONSTRUCTOR AND PROTOTYPE\r
+\r
+ var Spinner = function (element, options) {\r
+ this.$element = $(element);\r
+ this.options = $.extend({}, $.fn.spinner.defaults, options);\r
+ this.$input = this.$element.find('.spinner-input');\r
+ this.$element.on('keyup', this.$input, $.proxy(this.change, this));\r
+\r
+ if (this.options.hold) {\r
+ this.$element.on('mousedown', '.spinner-up', $.proxy(function() { this.startSpin(true); } , this));\r
+ this.$element.on('mouseup', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));\r
+ this.$element.on('mouseout', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));\r
+ this.$element.on('mousedown', '.spinner-down', $.proxy(function() {this.startSpin(false);} , this));\r
+ } else {\r
+ this.$element.on('click', '.spinner-up', $.proxy(function() { this.step(true); } , this));\r
+ this.$element.on('click', '.spinner-down', $.proxy(function() { this.step(false); }, this));\r
+ }\r
+\r
+ this.switches = {\r
+ count: 1,\r
+ enabled: true\r
+ };\r
+\r
+ if (this.options.speed === 'medium') {\r
+ this.switches.speed = 300;\r
+ } else if (this.options.speed === 'fast') {\r
+ this.switches.speed = 100;\r
+ } else {\r
+ this.switches.speed = 500;\r
+ }\r
+\r
+ this.lastValue = null;\r
+\r
+ this.render();\r
+\r
+ if (this.options.disabled) {\r
+ this.disable();\r
+ }\r
+ };\r
+\r
+ Spinner.prototype = {\r
+ constructor: Spinner,\r
+\r
+ render: function () {\r
+ this.$input.val(this.options.value);\r
+ this.$input.attr('maxlength',(this.options.max + '').split('').length);\r
+ },\r
+\r
+ change: function () {\r
+ var newVal = this.$input.val();\r
+\r
+ if(newVal/1){\r
+ this.options.value = newVal/1;\r
+ }else{\r
+ newVal = newVal.replace(/[^0-9]/g,'');\r
+ this.$input.val(newVal);\r
+ this.options.value = newVal/1;\r
+ }\r
+\r
+ this.triggerChangedEvent();\r
+ },\r
+\r
+ stopSpin: function () {\r
+ clearTimeout(this.switches.timeout);\r
+ this.switches.count = 1;\r
+ this.triggerChangedEvent();\r
+ },\r
+\r
+ triggerChangedEvent: function () {\r
+ var currentValue = this.value();\r
+ if (currentValue === this.lastValue) return;\r
+\r
+ this.lastValue = currentValue;\r
+\r
+ // Primary changed event\r
+ this.$element.trigger('changed', currentValue);\r
+\r
+ // Undocumented, kept for backward compatibility\r
+ this.$element.trigger('change');\r
+ },\r
+\r
+ startSpin: function (type) {\r
+\r
+ if (!this.options.disabled) {\r
+ var divisor = this.switches.count;\r
+\r
+ if (divisor === 1) {\r
+ this.step(type);\r
+ divisor = 1;\r
+ } else if (divisor < 3){\r
+ divisor = 1.5;\r
+ } else if (divisor < 8){\r
+ divisor = 2.5;\r
+ } else {\r
+ divisor = 4;\r
+ }\r
+\r
+ this.switches.timeout = setTimeout($.proxy(function() {this.iterator(type);} ,this),this.switches.speed/divisor);\r
+ this.switches.count++;\r
+ }\r
+ },\r
+\r
+ iterator: function (type) {\r
+ this.step(type);\r
+ this.startSpin(type);\r
+ },\r
+\r
+ step: function (dir) {\r
+ var curValue = this.options.value;\r
+ var limValue = dir ? this.options.max : this.options.min;\r
+\r
+ if ((dir ? curValue < limValue : curValue > limValue)) {\r
+ var newVal = curValue + (dir ? 1 : -1) * this.options.step;\r
+\r
+ if (dir ? newVal > limValue : newVal < limValue) {\r
+ this.value(limValue);\r
+ } else {\r
+ this.value(newVal);\r
+ }\r
+ }\r
+ },\r
+\r
+ value: function (value) {\r
+ if (!isNaN(parseFloat(value)) && isFinite(value)) {\r
+ value = parseFloat(value);\r
+ this.options.value = value;\r
+ this.$input.val(value);\r
+ return this;\r
+ } else {\r
+ return this.options.value;\r
+ }\r
+ },\r
+\r
+ disable: function () {\r
+ this.options.disabled = true;\r
+ this.$input.attr('disabled','');\r
+ this.$element.find('button').addClass('disabled');\r
+ },\r
+\r
+ enable: function () {\r
+ this.options.disabled = false;\r
+ this.$input.removeAttr("disabled");\r
+ this.$element.find('button').removeClass('disabled');\r
+ }\r
+ };\r
+\r
+\r
+ // SPINNER PLUGIN DEFINITION\r
+\r
+ $.fn.spinner = function (option,value) {\r
+ var methodReturn;\r
+\r
+ var $set = this.each(function () {\r
+ var $this = $(this);\r
+ var data = $this.data('spinner');\r
+ var options = typeof option === 'object' && option;\r
+\r
+ if (!data) $this.data('spinner', (data = new Spinner(this, options)));\r
+ if (typeof option === 'string') methodReturn = data[option](value);\r
+ });\r
+\r
+ return (methodReturn === undefined) ? $set : methodReturn;\r
+ };\r
+\r
+ $.fn.spinner.defaults = {\r
+ value: 1,\r
+ min: 1,\r
+ max: 999,\r
+ step: 1,\r
+ hold: true,\r
+ speed: 'medium',\r
+ disabled: false\r
+ };\r
+\r
+ $.fn.spinner.Constructor = Spinner;\r
+\r
+\r
+ // SPINNER DATA-API\r
+\r
+ $(function () {\r
+ $('body').on('mousedown.spinner.data-api', '.spinner', function (e) {\r
+ var $this = $(this);\r
+ if ($this.data('spinner')) return;\r
+ $this.spinner($this.data());\r
+ });\r
+ });\r
+\r
+});\r
--- /dev/null
+/*\r
+ * Fuel UX Tree\r
+ * https://github.com/ExactTarget/fuelux\r
+ *\r
+ * Copyright (c) 2012 ExactTarget\r
+ * Licensed under the MIT license.\r
+ */\r
+\r
+define(['require','jquery'],function(require) {\r
+\r
+ var $ = require('jquery');\r
+\r
+\r
+ // TREE CONSTRUCTOR AND PROTOTYPE\r
+\r
+ var Tree = function (element, options) {\r
+ this.$element = $(element);\r
+ this.options = $.extend({}, $.fn.tree.defaults, options);\r
+\r
+ this.$element.on('click', '.tree-item', $.proxy( function(ev) { this.selectItem(ev.currentTarget); } ,this));\r
+ this.$element.on('click', '.tree-folder-header', $.proxy( function(ev) { this.selectFolder(ev.currentTarget); }, this));\r
+\r
+ this.render();\r
+ };\r
+\r
+ Tree.prototype = {\r
+ constructor: Tree,\r
+\r
+ render: function () {\r
+ this.populate(this.$element);\r
+ },\r
+\r
+ populate: function ($el) {\r
+ var self = this;\r
+ var loader = $el.parent().find('.tree-loader:eq(0)');\r
+\r
+ loader.show();\r
+ this.options.dataSource.data($el.data(), function (items) {\r
+ loader.hide();\r
+\r
+ $.each( items.data, function(index, value) {\r
+ var $entity;\r
+\r
+ if(value.type === "folder") {\r
+ $entity = self.$element.find('.tree-folder:eq(0)').clone().show();\r
+ $entity.find('.tree-folder-name').html(value.name);\r
+ $entity.find('.tree-loader').html(self.options.loadingHTML);\r
+ $entity.find('.tree-folder-header').data(value);\r
+ } else if (value.type === "item") {\r
+ $entity = self.$element.find('.tree-item:eq(0)').clone().show();\r
+ $entity.find('.tree-item-name').html(value.name);\r
+ $entity.data(value);\r
+ }\r
+\r
+ if($el.hasClass('tree-folder-header')) {\r
+ $el.parent().find('.tree-folder-content:eq(0)').append($entity);\r
+ } else {\r
+ $el.append($entity);\r
+ }\r
+ });\r
+\r
+ self.$element.trigger('loaded');\r
+ });\r
+ },\r
+\r
+ selectItem: function (el) {\r
+ var $el = $(el);\r
+ var $all = this.$element.find('.tree-selected');\r
+ var data = [];\r
+\r
+ if (this.options.multiSelect) {\r
+ $.each($all, function(index, value) {\r
+ var $val = $(value);\r
+ if($val[0] !== $el[0]) {\r
+ data.push( $(value).data() );\r
+ }\r
+ });\r
+ } else if ($all[0] !== $el[0]) {\r
+ $all.removeClass('tree-selected')\r
+ .find('i').removeClass('icon-ok').addClass('tree-dot');\r
+ data.push($el.data());\r
+ }\r
+\r
+ if($el.hasClass('tree-selected')) {\r
+ $el.removeClass('tree-selected');\r
+ $el.find('i').removeClass('icon-ok').addClass('tree-dot');\r
+ } else {\r
+ $el.addClass ('tree-selected');\r
+ $el.find('i').removeClass('tree-dot').addClass('icon-ok');\r
+ if (this.options.multiSelect) {\r
+ data.push( $el.data() );\r
+ }\r
+ }\r
+\r
+ if(data.length) {\r
+ this.$element.trigger('selected', {info: data});\r
+ }\r
+\r
+ },\r
+\r
+ selectFolder: function (el) {\r
+ var $el = $(el);\r
+ var $par = $el.parent();\r
+\r
+ if($el.find('.icon-folder-close').length) {\r
+ if ($par.find('.tree-folder-content').children().length) {\r
+ $par.find('.tree-folder-content:eq(0)').show();\r
+ } else {\r
+ this.populate( $el );\r
+ }\r
+\r
+ $par.find('.icon-folder-close:eq(0)')\r
+ .removeClass('icon-folder-close')\r
+ .addClass('icon-folder-open');\r
+\r
+ this.$element.trigger('opened', $el.data());\r
+ } else {\r
+ if(this.options.cacheItems) {\r
+ $par.find('.tree-folder-content:eq(0)').hide();\r
+ } else {\r
+ $par.find('.tree-folder-content:eq(0)').empty();\r
+ }\r
+\r
+ $par.find('.icon-folder-open:eq(0)')\r
+ .removeClass('icon-folder-open')\r
+ .addClass('icon-folder-close');\r
+\r
+ this.$element.trigger('closed', $el.data());\r
+ }\r
+ },\r
+\r
+ selectedItems: function () {\r
+ var $sel = this.$element.find('.tree-selected');\r
+ var data = [];\r
+\r
+ $.each($sel, function (index, value) {\r
+ data.push($(value).data());\r
+ });\r
+ return data;\r
+ }\r
+ };\r
+\r
+\r
+ // TREE PLUGIN DEFINITION\r
+\r
+ $.fn.tree = function (option, value) {\r
+ var methodReturn;\r
+\r
+ var $set = this.each(function () {\r
+ var $this = $(this);\r
+ var data = $this.data('tree');\r
+ var options = typeof option === 'object' && option;\r
+\r
+ if (!data) $this.data('tree', (data = new Tree(this, options)));\r
+ if (typeof option === 'string') methodReturn = data[option](value);\r
+ });\r
+\r
+ return (methodReturn === undefined) ? $set : methodReturn;\r
+ };\r
+\r
+ $.fn.tree.defaults = {\r
+ multiSelect: false,\r
+ loadingHTML: '<div>Loading...</div>',\r
+ cacheItems: true\r
+ };\r
+\r
+ $.fn.tree.Constructor = Tree;\r
+\r
+});\r
--- /dev/null
+/*
+ * Fuel UX Utilities
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery'],function (require) {
+
+ var $ = require('jquery');
+
+ // custom case-insensitive match expression
+ function fuelTextExactCI(elem, text) {
+ return (elem.textContent || elem.innerText || $(elem).text() || '').toLowerCase() === (text || '').toLowerCase();
+ }
+
+ $.expr[':'].fuelTextExactCI = $.expr.createPseudo ?
+ $.expr.createPseudo(function (text) {
+ return function (elem) {
+ return fuelTextExactCI(elem, text);
+ };
+ }) :
+ function (elem, i, match) {
+ return fuelTextExactCI(elem, match[3]);
+ };
+
+});
\ No newline at end of file
--- /dev/null
+/*
+ * Fuel UX Wizard
+ * https://github.com/ExactTarget/fuelux
+ *
+ * Copyright (c) 2012 ExactTarget
+ * Licensed under the MIT license.
+ */
+
+define(['require','jquery'],function (require) {
+
+ var $ = require('jquery');
+
+
+ // WIZARD CONSTRUCTOR AND PROTOTYPE
+
+ var Wizard = function (element, options) {
+ var kids;
+
+ this.$element = $(element);
+ this.options = $.extend({}, $.fn.wizard.defaults, options);
+ this.currentStep = 1;
+ this.numSteps = this.$element.find('li').length;
+ this.$prevBtn = this.$element.find('button.btn-prev');
+ this.$nextBtn = this.$element.find('button.btn-next');
+
+ kids = this.$nextBtn.children().detach();
+ this.nextText = $.trim(this.$nextBtn.text());
+ this.$nextBtn.append(kids);
+
+ // handle events
+ this.$prevBtn.on('click', $.proxy(this.previous, this));
+ this.$nextBtn.on('click', $.proxy(this.next, this));
+ this.$element.on('click', 'li.complete', $.proxy(this.stepclicked, this));
+ };
+
+ Wizard.prototype = {
+
+ constructor: Wizard,
+
+ setState: function () {
+ var canMovePrev = (this.currentStep > 1);
+ var firstStep = (this.currentStep === 1);
+ var lastStep = (this.currentStep === this.numSteps);
+
+ // disable buttons based on current step
+ this.$prevBtn.attr('disabled', (firstStep === true || canMovePrev === false));
+
+ // change button text of last step, if specified
+ var data = this.$nextBtn.data();
+ if (data && data.last) {
+ this.lastText = data.last;
+ if (typeof this.lastText !== 'undefined') {
+ // replace text
+ var text = (lastStep !== true) ? this.nextText : this.lastText;
+ var kids = this.$nextBtn.children().detach();
+ this.$nextBtn.text(text).append(kids);
+ }
+ }
+
+ // reset classes for all steps
+ var $steps = this.$element.find('li');
+ $steps.removeClass('active').removeClass('complete');
+ $steps.find('span.badge').removeClass('badge-info').removeClass('badge-success');
+
+ // set class for all previous steps
+ var prevSelector = 'li:lt(' + (this.currentStep - 1) + ')';
+ var $prevSteps = this.$element.find(prevSelector);
+ $prevSteps.addClass('complete');
+ $prevSteps.find('span.badge').addClass('badge-success');
+
+ // set class for current step
+ var currentSelector = 'li:eq(' + (this.currentStep - 1) + ')';
+ var $currentStep = this.$element.find(currentSelector);
+ $currentStep.addClass('active');
+ $currentStep.find('span.badge').addClass('badge-info');
+
+ // set display of target element
+ var target = $currentStep.data().target;
+ $('.step-pane').removeClass('active');
+ $(target).addClass('active');
+
+ this.$element.trigger('changed');
+ },
+
+ stepclicked: function (e) {
+ var li = $(e.currentTarget);
+
+ var index = $('.steps li').index(li);
+
+ var evt = $.Event('stepclick');
+ this.$element.trigger(evt, {step: index + 1});
+ if (evt.isDefaultPrevented()) return;
+
+ this.currentStep = (index + 1);
+ this.setState();
+ },
+
+ previous: function () {
+ var canMovePrev = (this.currentStep > 1);
+ if (canMovePrev) {
+ var e = $.Event('change');
+ this.$element.trigger(e, {step: this.currentStep, direction: 'previous'});
+ if (e.isDefaultPrevented()) return;
+
+ this.currentStep -= 1;
+ this.setState();
+ }
+ },
+
+ next: function () {
+ var canMoveNext = (this.currentStep + 1 <= this.numSteps);
+ var lastStep = (this.currentStep === this.numSteps);
+
+ if (canMoveNext) {
+ var e = $.Event('change');
+ this.$element.trigger(e, {step: this.currentStep, direction: 'next'});
+
+ if (e.isDefaultPrevented()) return;
+
+ this.currentStep += 1;
+ this.setState();
+ }
+ else if (lastStep) {
+ this.$element.trigger('finished');
+ }
+ },
+
+ selectedItem: function (val) {
+ return {
+ step: this.currentStep
+ };
+ }
+ };
+
+
+ // WIZARD PLUGIN DEFINITION
+
+ $.fn.wizard = function (option, value) {
+ var methodReturn;
+
+ var $set = this.each(function () {
+ var $this = $(this);
+ var data = $this.data('wizard');
+ var options = typeof option === 'object' && option;
+
+ if (!data) $this.data('wizard', (data = new Wizard(this, options)));
+ if (typeof option === 'string') methodReturn = data[option](value);
+ });
+
+ return (methodReturn === undefined) ? $set : methodReturn;
+ };
+
+ $.fn.wizard.defaults = {};
+
+ $.fn.wizard.Constructor = Wizard;
+
+
+ // WIZARD DATA-API
+
+ $(function () {
+ $('body').on('mousedown.wizard.data-api', '.wizard', function () {
+ var $this = $(this);
+ if ($this.data('wizard')) return;
+ $this.wizard($this.data());
+ });
+ });
+
+});
return $buttonGroup;
}
},
+ datagrid: {
+ /*
+ * The init function returns HTML markup for the datagrid per the options provided. Each consumer
+ * of the datagrid must first call init and then provide the datasource for the grid.
+ * id: this is the id of the table
+ * options: {
+ * searchable: true/false,
+ * pagination: turned off for now,
+ * flexibleRowsPerPage: turned off
+ * }
+ * classes : String containing bootstrap related classes. For ex: "table-striped table-condensed"
+ * The classes "table", "table-bordered" and "datagrid" will be added by default
+ */
+ init: function(id, options, classes) {
+ var $fuelGridContainerDiv = $(document.createElement("div"));
+ $fuelGridContainerDiv.addClass("fuelux");
+ $table = $(document.createElement("table"));
+ $table.attr("id", id);
+ $table.addClass("table table-bordered datagrid");
+ $table.addClass(classes);
+ // create datagrid header
+ $thead = $(document.createElement("thead"));
+ $headertr = $(document.createElement("tr"));
+ $headerth = $(document.createElement("th"));
+ // create datagrid footer
+ $tfoot = $(document.createElement("tfoot"));
+ $footertr = $(document.createElement("tr"));
+ $footerth = $(document.createElement("th"));
+ if(options.searchable == true) {
+ $headerth.append(one.lib.dashlet.datagrid._searchable());
+ }
+ if(options.flexibleRowsPerPage == true) {
+ $footerth.append(one.lib.dashlet.datagrid._rowsPerPage(options.popout));
+ }
+ if(options.pagination == true) {
+ $footerth.append(one.lib.dashlet.datagrid._pagination());
+ }
+ $headertr.append($headerth);
+ $thead.append($headertr);
+ $footertr.append($footerth);
+ $tfoot.append($footertr);
+ $table.append($thead).append($tfoot);
+ $fuelGridContainerDiv.append($table);
+ return $fuelGridContainerDiv;
+ },
+ _searchable: function() {
+ var searchHTML = "<div class='datagrid-header-left'><div class='input-append search datagrid-search'> <input type='text' class='input-medium' placeholder='Search'><button type='button' class='btn'><i class='icon-search'></i></button></div></div>";
+ return searchHTML;
+ },
+ _pagination: function() {
+ var html = '<div class="datagrid-footer-right" style="display:none;"><div class="grid-pager"><button type="button" class="btn grid-prevpage"><i class="icon-chevron-left"></i></button><span>Page</span> <div style="display:inline-block;"><input type="text" name="pagenumber" style="width:25px;margin-bottom:-10px;vertical-align:middle;margin-right:5px;"></div><span>of <span class="grid-pages"></span></span><button type="button" class="btn grid-nextpage"><i class="icon-chevron-right"></i></button></div></div>';
+ return html;
+ },
+ _rowsPerPage: function(popout) {
+ if(popout) {
+ var html = '<div class="datagrid-footer-left" style="display:none;"><div class="grid-controls"><span><span class="grid-start"></span>-<span class="grid-end"></span> of <span class="grid-count"></span></span><div class="select grid-pagesize" data-resize="auto" style="visibility:hidden;"><button type="button" data-toggle="dropdown" class="btn dropdown-toggle"><span class="dropdown-label"></span><span class="caret"></span></button><ul class="dropdown-menu"><li data-value="10" data-selected="true"><a href="#">5</a></li><li data-value="10"><a href="#">10</a></li><li data-value="20"><a href="#">20</a></li><li data-value="50"><a href="#">50</a></li><li data-value="100"><a href="#">100</a></li></ul></div><span style="display:none;">Per Page</span></div></div>';
+ } else {
+ var html = '<div class="datagrid-footer-left" style="display:none;"><div class="grid-controls"><span><span class="grid-start"></span>-<span class="grid-end"></span> of <span class="grid-count"></span></span><div class="select grid-pagesize" data-resize="auto" style="visibility:hidden;"><button type="button" data-toggle="dropdown" class="btn dropdown-toggle"><span class="dropdown-label"></span><span class="caret"></span></button><ul class="dropdown-menu"><li data-value="5" data-selected="true"><a href="#">5</a></li><li data-value="10"><a href="#">10</a></li><li data-value="20"><a href="#">20</a></li><li data-value="50"><a href="#">50</a></li><li data-value="100"><a href="#">100</a></li></ul></div><span style="display:none;">Per Page</span></div></div>';
+ }
+ return html;
+ }
+ },
table : {
table : function(classes, id) {
var $table = $(document.createElement('table'));
--- /dev/null
+// Underscore.js 1.3.3
+// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore is freely distributable under the MIT license.
+// Portions of Underscore are inspired or borrowed from Prototype,
+// Oliver Steele's Functional, and John Resig's Micro-Templating.
+// For all details and documentation:
+// http://documentcloud.github.com/underscore
+(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
+c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
+g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
+c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
+a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
+c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
+a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
+function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
+(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
+j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
+0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
+e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
+i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
+1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
+i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
+g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
+return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
+c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
+function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
+b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
+b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
+function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
+u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
+b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
+this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
\ No newline at end of file
List<NodeConnectorStatistics> statistics = statisticsManager
.getNodeConnectorStatistics(node);
for (NodeConnectorStatistics stats : statistics) {
- cells.add(this.convertPortsStatistics(stats));
+ cells.add(this.convertPortsStatistics(stats, containerName));
}
}
}
}
private Map<String, String> convertPortsStatistics(
- NodeConnectorStatistics ncStats) {
+ NodeConnectorStatistics ncStats, String containerName) {
Map<String, String> row = new HashMap<String, String>();
+ ISwitchManager switchManager = (ISwitchManager) ServiceHelper
+ .getInstance(ISwitchManager.class, containerName, this);
+ NodeConnector nodeConnector = ncStats.getNodeConnector();
+ Description description = (Description) switchManager.getNodeProp(nodeConnector.getNode(), Description.propertyName);
+ String desc = (description == null) ? "" : description.getValue();
+ String nodeName = desc.equalsIgnoreCase("none") ? nodeConnector.getNode().getNodeIDString() : desc;
+ String nodeConnectorDisplayName = nodeConnector.getType() + "|" + nodeConnector.getID() + "@" + nodeName;
row.put("nodeConnector",
- String.valueOf(ncStats.getNodeConnector().toString()));
+ String.valueOf(nodeConnectorDisplayName));
+
row.put("rxPkts", String.valueOf(ncStats.getReceivePacketCount()));
row.put("txPkts", String.valueOf(ncStats.getTransmitPacketCount()));
row.put("rxBytes", String.valueOf(ncStats.getReceiveByteCount()));
-
/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
name : 'Existing Nodes'
},
uptime: {
- id: 'uptime',
- name: 'Uptime'
+ id: 'uptime',
+ name: 'Uptime'
},
flowsOrPorts: {
- id: "flowsOrPorts",
- name: "Statistics"
+ id: "flowsOrPorts",
+ name: "Statistics"
}
};
/**Troubleshoot modules*/
one.f.troubleshooting = {
- rootUrl: "/controller/web/troubleshoot",
- rightBottomDashlet: {
- get: function() {
- var $rightBottomDashlet = $("#right-bottom").find(".dashlet");
- return $rightBottomDashlet;
- },
- setDashletHeader: function(label) {
- $("#right-bottom li a")[0].innerHTML = label;
- }
- },
- createTable: function(columnNames, body) {
- var tableAttributes = ["table-striped", "table-bordered", "table-condensed"];
- var $table = one.lib.dashlet.table.table(tableAttributes);
- var tableHeaders = columnNames;
- var $thead = one.lib.dashlet.table.header(tableHeaders);
- var $tbody = one.lib.dashlet.table.body(body, tableHeaders);
- $table.append($thead)
- .append($tbody);
- return $table;
- }
+ rootUrl: "/controller/web/troubleshoot",
+ rightBottomDashlet: {
+ get: function() {
+ var $rightBottomDashlet = $("#right-bottom").find(".dashlet");
+ return $rightBottomDashlet;
+ },
+ setDashletHeader: function(label) {
+ $("#right-bottom li a")[0].innerHTML = label;
+ }
+ },
+ createTable: function(columnNames, body) {
+ var tableAttributes = ["table-striped", "table-bordered", "table-condensed"];
+ var $table = one.lib.dashlet.table.table(tableAttributes);
+ var tableHeaders = columnNames;
+ var $thead = one.lib.dashlet.table.header(tableHeaders);
+ var $tbody = one.lib.dashlet.table.body(body, tableHeaders);
+ $table.append($thead)
+ .append($tbody);
+ return $table;
+ }
};
one.f.troubleshooting.existingNodes = {
- id: {
- popout: "one_f_troubleshooting_existingNodes_id_popout",
- modal: "one_f_troubleshooting_existingNodes_id_modal"
- },
- // TODO: Make these values configurable.
- autoRefreshInterval: {
- flows: 10000,
- ports: 10000,
- refreshRateInterval: 5000
- },
- load: {
- main: function($dashlet) {
- one.lib.dashlet.empty($dashlet);
- $dashlet.append(one.lib.dashlet.header(one.f.dashlet.existingNodes.name));
-
- // TODO(l): Add a generic auto expand function to one.lib and replace custom height setting.
- //$('#left-top').height('100%');
- one.f.troubleshooting.existingNodes.ajax(one.f.troubleshooting.rootUrl + "/existingNodes" , function(content) {
- var body = one.f.troubleshooting.existingNodes.data.existingNodes(content);
- var $table = one.f.troubleshooting.createTable(content.columnNames, body);
- $dashlet.append($table);
- });
- },
- flows: function(nodeId) {
- try {
- clearTimeout(one.f.troubleshooting.existingNodes.registry.refreshTimer);
- $.getJSON(one.main.constants.address.prefix + "/troubleshoot/flowStats?nodeId=" + nodeId, function(content) {
- var body = one.f.troubleshooting.existingNodes.data.flows(content);
- var $table = one.f.troubleshooting.createTable(content.columnNames, body);
- $rightBottomDashlet = one.f.troubleshooting.rightBottomDashlet.get();
- one.f.troubleshooting.rightBottomDashlet.setDashletHeader("Flows");
- one.lib.dashlet.empty($rightBottomDashlet);
- $rightBottomDashlet.append(one.lib.dashlet.header("Flow Details"));
- $rightBottomDashlet.append($table);
- var numberOfFlows = content.nodeData.length;
- var refreshRate = one.f.troubleshooting.existingNodes.autoRefreshInterval.flows;
- if (numberOfFlows > 0) {
- refreshRate += Math.floor(numberOfFlows / 500) *
- one.f.troubleshooting.existingNodes.autoRefreshInterval.refreshRateInterval;
- }
- one.f.troubleshooting.existingNodes.registry.refreshTimer = setTimeout(
- one.f.troubleshooting.existingNodes.load.flows,
- refreshRate, nodeId);
- });
- } catch(e) {}
- },
- ports: function(nodeId) {
- try {
- clearTimeout(one.f.troubleshooting.existingNodes.registry.refreshTimer);
- $.getJSON(one.main.constants.address.prefix + "/troubleshoot/portStats?nodeId=" + nodeId, function(content) {
- var body = one.f.troubleshooting.existingNodes.data.ports(content);
- var $table = one.f.troubleshooting.createTable(content.columnNames, body);
- $rightBottomDashlet = one.f.troubleshooting.rightBottomDashlet.get();
- one.f.troubleshooting.rightBottomDashlet.setDashletHeader("Ports");
- one.lib.dashlet.empty($rightBottomDashlet);
- $rightBottomDashlet.append(one.lib.dashlet.header("Port Details"));
- $rightBottomDashlet.append($table);
- var numberOfPorts = content.nodeData.length;
- var refreshRate = one.f.troubleshooting.existingNodes.autoRefreshInterval.ports;
- if (numberOfPorts > 0) {
- refreshRate += Math.floor(numberOfPorts / 500) *
- one.f.troubleshooting.existingNodes.autoRefreshInterval.refreshRateInterval;
- }
- one.f.troubleshooting.existingNodes.registry.refreshTimer = setTimeout(
- one.f.troubleshooting.existingNodes.load.ports,
- refreshRate, nodeId);
- });
- } catch(e) {}
- }
- },
- ajax : function(url, callback) {
- $.getJSON(url, function(data) {
- callback(data);
- });
- },
- registry: {},
- modal : {
- },
- data : {
- existingNodes : function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- var entry = [];
- entry.push(value["nodeName"]);
- entry.push(value["nodeId"]);
- var nodeIdvalue = value["nodeId"];
- entry.push("<a href=\"javascript:one.f.troubleshooting.existingNodes.load.flows('" + value["nodeId"] + "');\">Flows</a>" +
- " <a href=\"javascript:one.f.troubleshooting.existingNodes.load.ports('" + value["nodeId"] + "');\">Ports</a>");
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- },
- ports: function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- var entry = [];
- entry.push(value["nodeConnector"]);
- entry.push(value["rxPkts"]);
- entry.push(value["txPkts"]);
- entry.push(value["rxBytes"]);
- entry.push(value["txBytes"]);
- entry.push(value["rxDrops"]);
- entry.push(value["txDrops"]);
- entry.push(value["rxErrors"]);
- entry.push(value["txErrors"]);
- entry.push(value["rxFrameErrors"]);
- entry.push(value["rxOverRunErrors"]);
- entry.push(value["rxCRCErrors"]);
- entry.push(value["collisions"]);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- },
- flows: function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- var entry = [];
- entry.push(value["nodeName"]);
- entry.push(value["inPort"]);
- entry.push(value["dlSrc"]);
- entry.push(value["dlDst"]);
- entry.push(value["dlType"]);
- entry.push(value["dlVlan"]);
- entry.push(value["nwSrc"]);
- entry.push(value["nwDst"]);
- entry.push(value["nwProto"]);
- entry.push(value["tpSrc"]);
- entry.push(value["tpDst"]);
- entry.push(value["actions"]);
- entry.push(value["byteCount"]);
- entry.push(value["packetCount"]);
- entry.push(value["durationSeconds"]);
- entry.push(value["idleTimeout"]);
- entry.push(value["outPorts"]);
- entry.push(value["outVlanId"]);
- entry.push(value["priority"]);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- }
- }
+ id: {
+ popout: "one_f_troubleshooting_existingNodes_id_popout",
+ modal: "one_f_troubleshooting_existingNodes_id_modal",
+ existingNodesDataGrid: "one_f_troubleshooting_existingNodes_id_datagrid",
+ portsDataGrid: "one_f_troubleshooting_existingNodes_id_portsDataGrid",
+ flowsDataGrid: "one_f_troubleshooting_existingNodes_id_flowsDataGrid"
+ },
+ // TODO: Make these values configurable.
+ autoRefreshInterval: {
+ flows: 10000,
+ ports: 10000,
+ refreshRateInterval: 5000
+ },
+ load: {
+ main: function($dashlet) {
+ one.lib.dashlet.empty($dashlet);
+ $dashlet.append(one.lib.dashlet.header(one.f.dashlet.existingNodes.name));
+ // TODO(l): Add a generic auto expand function to one.lib and replace custom height setting.
+ //$('#left-top').height('100%');
+ one.f.troubleshooting.existingNodes.ajax(one.f.troubleshooting.rootUrl + "/existingNodes" , function(content) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.troubleshooting.existingNodes.id.existingNodesDataGrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.troubleshooting.existingNodes.data.existingNodesGrid(content);
+ $("#" + one.f.troubleshooting.existingNodes.id.existingNodesDataGrid).datagrid({dataSource: dataSource});
+
+ });
+ },
+ flows: function(nodeId) {
+ try {
+ clearTimeout(one.f.troubleshooting.existingNodes.registry.refreshTimer);
+ $.getJSON(one.main.constants.address.prefix + "/troubleshoot/flowStats?nodeId=" + nodeId, function(content) {
+ $rightBottomDashlet = one.f.troubleshooting.rightBottomDashlet.get();
+ one.f.troubleshooting.rightBottomDashlet.setDashletHeader("Flows");
+ one.lib.dashlet.empty($rightBottomDashlet);
+ $rightBottomDashlet.append(one.lib.dashlet.header("Flow Details"));
+
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.troubleshooting.existingNodes.id.flowsDataGrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $rightBottomDashlet.append($gridHTML);
+ var dataSource = one.f.troubleshooting.existingNodes.data.flowsGrid(content);
+ $("#" + one.f.troubleshooting.existingNodes.id.flowsDataGrid).datagrid({dataSource: dataSource});
+
+ var numberOfFlows = content.nodeData.length;
+ var refreshRate = one.f.troubleshooting.existingNodes.autoRefreshInterval.flows;
+ if (numberOfFlows > 0) {
+ refreshRate += Math.floor(numberOfFlows / 500) *
+ one.f.troubleshooting.existingNodes.autoRefreshInterval.refreshRateInterval;
+ }
+ one.f.troubleshooting.existingNodes.registry.refreshTimer = setTimeout(
+ one.f.troubleshooting.existingNodes.load.flows,
+ refreshRate, nodeId);
+ });
+ } catch(e) {}
+ },
+ ports: function(nodeId) {
+ try {
+ clearTimeout(one.f.troubleshooting.existingNodes.registry.refreshTimer);
+ $.getJSON(one.main.constants.address.prefix + "/troubleshoot/portStats?nodeId=" + nodeId, function(content) {
+ $rightBottomDashlet = one.f.troubleshooting.rightBottomDashlet.get();
+ one.f.troubleshooting.rightBottomDashlet.setDashletHeader("Ports");
+ one.lib.dashlet.empty($rightBottomDashlet);
+ $rightBottomDashlet.append(one.lib.dashlet.header("Port Details"));
+
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.troubleshooting.existingNodes.id.portsDataGrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $rightBottomDashlet.append($gridHTML);
+ var dataSource = one.f.troubleshooting.existingNodes.data.portsGrid(content);
+ $("#" + one.f.troubleshooting.existingNodes.id.portsDataGrid).datagrid({dataSource: dataSource});
+
+ var numberOfPorts = content.nodeData.length;
+ var refreshRate = one.f.troubleshooting.existingNodes.autoRefreshInterval.ports;
+ if (numberOfPorts > 0) {
+ refreshRate += Math.floor(numberOfPorts / 500) *
+ one.f.troubleshooting.existingNodes.autoRefreshInterval.refreshRateInterval;
+ }
+ one.f.troubleshooting.existingNodes.registry.refreshTimer = setTimeout(
+ one.f.troubleshooting.existingNodes.load.ports,
+ refreshRate, nodeId);
+ });
+ } catch(e) {}
+ }
+ },
+ ajax : function(url, callback) {
+ $.getJSON(url, function(data) {
+ callback(data);
+ });
+ },
+ registry: {},
+ modal : {
+ },
+ data : {
+ existingNodesGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Name',
+ sortable: true
+ },
+ {
+ property: 'nodeId',
+ label: 'Static Route',
+ sortable: true
+ },
+ {
+ property: 'statistics',
+ label: 'Statistics',
+ sortable: true
+ }
+ ],
+ data: data.nodeData,
+ formatter: function(items) {
+ $.each(items, function(index, item) {
+ item["statistics"] = "<a href=\"javascript:one.f.troubleshooting.existingNodes.load.flows('" + item["nodeId"] + "');\">Flows</a>" +
+ " <a href=\"javascript:one.f.troubleshooting.existingNodes.load.ports('" + item["nodeId"] + "');\">Ports</a>";
+ });
+
+ },
+ delay: 0
+ });
+ return source;
+ },
+ portsGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeConnector',
+ label: 'Node Connector',
+ sortable: true
+ },
+ {
+ property: 'rxPkts',
+ label: 'Rx Pkts',
+ sortable: true
+ },
+ {
+ property: 'txPkts',
+ label: 'Tx Pkts',
+ sortable: true
+ },
+ {
+ property: 'rxBytes',
+ label: 'Rx Bytes',
+ sortable: true
+ },
+ {
+ property: 'txBytes',
+ label: 'Tx Bytes',
+ sortable: true
+ },
+ {
+ property: 'rxDrops',
+ label: 'Rx Drops',
+ sortable: true
+ },
+ {
+ property: 'txDrops',
+ label: 'Tx Drops',
+ sortable: true
+ },
+ {
+ property: 'rxErrors',
+ label: 'Rx Errs',
+ sortable: true
+ },
+ {
+ property: 'txErrors',
+ label: 'Tx Errs',
+ sortable: true
+ },
+ {
+ property: 'rxFrameErrors',
+ label: 'Rx Frame Errs',
+ sortable: true
+ },
+ {
+ property: 'rxOverRunErrors',
+ label: 'Rx OverRun Errs',
+ sortable: true
+ },
+ {
+ property: 'rxCRCErrors',
+ label: 'Rx CRC Errs',
+ sortable: true
+ },
+ {
+ property: 'collisions',
+ label: 'Collisions',
+ sortable: true
+ }
+ ],
+ data: data.nodeData,
+ delay: 200
+ });
+ return source;
+ },
+ ports: function(data) {
+ var result = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ var entry = [];
+ entry.push(value["nodeConnector"]);
+ entry.push(value["rxPkts"]);
+ entry.push(value["txPkts"]);
+ entry.push(value["rxBytes"]);
+ entry.push(value["txBytes"]);
+ entry.push(value["rxDrops"]);
+ entry.push(value["txDrops"]);
+ entry.push(value["rxErrors"]);
+ entry.push(value["txErrors"]);
+ entry.push(value["rxFrameErrors"]);
+ entry.push(value["rxOverRunErrors"]);
+ entry.push(value["rxCRCErrors"]);
+ entry.push(value["collisions"]);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ },
+ flowsGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Node',
+ sortable: true
+ },
+ {
+ property: 'inPort',
+ label: 'In Port',
+ sortable: true
+ },
+ {
+ property: 'dlSrc',
+ label: 'DL Src',
+ sortable: true
+ },
+ {
+ property: 'dlDst',
+ label: 'DL Dst',
+ sortable: true
+ },
+ {
+ property: 'dlType',
+ label: 'DL Type',
+ sortable: true
+ },
+ {
+ property: 'dlVlan',
+ label: 'DL Vlan',
+ sortable: true
+ },
+ {
+ property: 'nwSrc',
+ label: 'NW Src',
+ sortable: true
+ },
+ {
+ property: 'nwDst',
+ label: 'NW Dst',
+ sortable: true
+ },
+ {
+ property: 'nwProto',
+ label: 'NW Proto',
+ sortable: true
+ },
+ {
+ property: 'tpSrc',
+ label: 'TP Src',
+ sortable: true
+ },
+ {
+ property: 'tpDst',
+ label: 'TP Dst',
+ sortable: true
+ },
+ {
+ property: 'actions',
+ label: 'Actions',
+ sortable: true
+ },
+ {
+ property: 'byteCount',
+ label: 'Byte Count',
+ sortable: true
+ },
+ {
+ property: 'packetCount',
+ label: 'Packet Count',
+ sortable: true
+ },
+ {
+ property: 'durationSeconds',
+ label: 'Duration Seconds',
+ sortable: true
+ },
+ {
+ property: 'idleTimeout',
+ label: 'Idle Timeout',
+ sortable: true
+ },
+ {
+ property: 'outPorts',
+ label: 'Out Ports',
+ sortable: true
+ },
+ {
+ property: 'outVlanId',
+ label: 'Out VlanId',
+ sortable: true
+ },
+ {
+ property: 'priority',
+ label: 'Priority',
+ sortable: true
+ }
+ ],
+ data: data.nodeData,
+ delay: 0
+ });
+ return source;
+ },
+ flows: function(data) {
+ var result = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ var entry = [];
+ entry.push(value["nodeName"]);
+ entry.push(value["inPort"]);
+ entry.push(value["dlSrc"]);
+ entry.push(value["dlDst"]);
+ entry.push(value["dlType"]);
+ entry.push(value["dlVlan"]);
+ entry.push(value["nwSrc"]);
+ entry.push(value["nwDst"]);
+ entry.push(value["nwProto"]);
+ entry.push(value["tpSrc"]);
+ entry.push(value["tpDst"]);
+ entry.push(value["actions"]);
+ entry.push(value["byteCount"]);
+ entry.push(value["packetCount"]);
+ entry.push(value["durationSeconds"]);
+ entry.push(value["idleTimeout"]);
+ entry.push(value["outPorts"]);
+ entry.push(value["outVlanId"]);
+ entry.push(value["priority"]);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ }
+ }
};
one.f.troubleshooting.uptime = {
- id: {
- popout: "one_f_troubleshooting_existingNodes_id_popout",
- modal: "one_f_troubleshooting_existingNodes_id_modal"
- },
+ id: {
+ popout: "one_f_troubleshooting_uptime_id_popout",
+ modal: "one_f_troubleshooting_uptime_id_modal",
+ datagrid: "one_f_troubleshooting_uptime_id_datagrid"
+ },
- dashlet: function($dashlet) {
- one.lib.dashlet.empty($dashlet);
- $dashlet.append(one.lib.dashlet.header(one.f.dashlet.uptime.name));
- var url = one.f.troubleshooting.rootUrl + "/uptime";
- one.f.troubleshooting.uptime.ajax.main(url , {} ,function(content) {
- var body = one.f.troubleshooting.uptime.data.uptime(content);
- var $table = one.f.troubleshooting.createTable(content.columnNames, body);
- $dashlet.append($table);
- });
- },
-
- ajax : {
- main : function(url, requestData, callback) {
- $.getJSON(url, requestData, function(data) {
- callback(data);
- });
- }
- },
-
- data: {
- uptime: function(data) {
- var result = [];
- $.each(data.nodeData, function(key, value) {
- var tr = {};
- var entry = [];
- entry.push(value["nodeName"]);
- entry.push(value["nodeId"]);
- entry.push(value["connectedSince"]);
- tr.entry = entry;
- result.push(tr);
- });
- return result;
- }
- },
+ dashlet: function($dashlet) {
+ one.lib.dashlet.empty($dashlet);
+ $dashlet.append(one.lib.dashlet.header(one.f.dashlet.uptime.name));
+ var url = one.f.troubleshooting.rootUrl + "/uptime";
+ one.f.troubleshooting.uptime.ajax.main(url , {} ,function(content) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.troubleshooting.uptime.id.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.troubleshooting.uptime.data.uptimeDataGrid(content);
+ $("#" + one.f.troubleshooting.uptime.id.datagrid).datagrid({dataSource: dataSource});
+ });
+ },
+
+ ajax : {
+ main : function(url, requestData, callback) {
+ $.getJSON(url, requestData, function(data) {
+ callback(data);
+ });
+ }
+ },
+
+ data: {
+ uptimeDataGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Node',
+ sortable: true
+ },
+ {
+ property: 'nodeId',
+ label: 'Node ID',
+ sortable: true
+ },
+ {
+ property: 'connectedSince',
+ label: 'Statistics',
+ sortable: true
+ }
+ ],
+ data: data.nodeData,
+ delay: 0
+ });
+ return source;
+ },
+ uptime: function(data) {
+ var result = [];
+ $.each(data.nodeData, function(key, value) {
+ var tr = {};
+ var entry = [];
+ entry.push(value["nodeName"]);
+ entry.push(value["nodeId"]);
+ entry.push(value["connectedSince"]);
+ tr.entry = entry;
+ result.push(tr);
+ });
+ return result;
+ }
+ },
};
one.f.troubleshooting.statistics = {
- dashlet : function($dashlet) {
+ dashlet : function($dashlet) {
var $h4 = one.lib.dashlet.header("Statistics");
$dashlet.append($h4);
- // empty
- var $none = $(document.createElement('div'));
- $none.addClass('none');
- var $p = $(document.createElement('p'));
- $p.text('Please select a Flow or Ports statistics');
- $p.addClass('text-center').addClass('text-info');
-
- $dashlet.append($none)
- .append($p);
- }
+ // empty
+ var $none = $(document.createElement('div'));
+ $none.addClass('none');
+ var $p = $(document.createElement('p'));
+ $p.text('Please select a Flow or Ports statistics');
+ $p.addClass('text-center').addClass('text-info');
+
+ $dashlet.append($none)
+ .append($p);
+ }
};
// bind dashlet nav
var menu = one.f.dashlet;
switch (id) {
case menu.existingNodes.id:
- one.f.troubleshooting.existingNodes.load.main($dashlet);
+ one.f.troubleshooting.existingNodes.load.main($dashlet);
break;
case menu.uptime.id:
- one.f.troubleshooting.uptime.dashlet($dashlet);
- break;
- case menu.flowsOrPorts.id:
- one.f.troubleshooting.statistics.dashlet($dashlet);
- break;
+ one.f.troubleshooting.uptime.dashlet($dashlet);
+ break;
+ case menu.flowsOrPorts.id:
+ one.f.troubleshooting.statistics.dashlet($dashlet);
+ break;
};
});