From: Sam Hague Date: Wed, 21 Oct 2015 12:03:37 +0000 (+0000) Subject: Merge "Unit test for ovsdb.southbound.ovsdb.transact" X-Git-Tag: release/beryllium-sr2~342 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=72fe035fa1234f1f5a068979e5d71ea94e901e2b;hp=d7aaa886a18dfcb65fbd59f4a1595ed5359ecc64;p=netvirt.git Merge "Unit test for ovsdb.southbound.ovsdb.transact" --- diff --git a/commons/parent/pom.xml b/commons/parent/pom.xml index f00ce8b4c7..641001fb55 100644 --- a/commons/parent/pom.xml +++ b/commons/parent/pom.xml @@ -101,9 +101,8 @@ and is available at http://www.eclipse.org/legal/epl-v10.html 4.2.1 2.0 1.5.2 + 0.3.0-SNAPSHOT 0.1.0-SNAPSHOT - 0.3.0-SNAPSHOT - 0.3.0-SNAPSHOT diff --git a/features/ovsdb/pom.xml b/features/ovsdb/pom.xml index 1e8e2fd665..94cf2abe84 100644 --- a/features/ovsdb/pom.xml +++ b/features/ovsdb/pom.xml @@ -61,9 +61,9 @@ and is available at http://www.eclipse.org/legal/epl-v10.html 0.2.0-SNAPSHOT 0.10.0-SNAPSHOT 0.8.0-SNAPSHOT - 0.3.0-SNAPSHOT - 0.1.0-SNAPSHOT 0.4.0-SNAPSHOT + 0.3.0-SNAPSHOT + 0.1.0-SNAPSHOT @@ -226,6 +226,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html ${networkconfig.neutron.version} features xml + + + org.opendaylight.neutron + dummyprovider + ${networkconfig.neutron.version} org.opendaylight.openflowplugin @@ -309,5 +314,18 @@ and is available at http://www.eclipse.org/legal/epl-v10.html xml features + + + org.opendaylight.dlux + features-dlux + ${dlux.version} + features + xml + + + org.opendaylight.ovsdb + ovsdb-ui-bundle + ${ovsdb.ui.version} + diff --git a/features/ovsdb/src/main/features/features.xml b/features/ovsdb/src/main/features/features.xml index 15bd93066b..1abdab4e67 100644 --- a/features/ovsdb/src/main/features/features.xml +++ b/features/ovsdb/src/main/features/features.xml @@ -8,6 +8,7 @@ mvn:org.opendaylight.ovsdb/southbound-features/1.2.1-SNAPSHOT/xml/features mvn:org.opendaylight.controller/features-mdsal/${mdsal.version}/xml/features mvn:org.opendaylight.ovsdb/library-features/${ovsdb.library.version}/xml/features + mvn:org.opendaylight.dlux/features-dlux/${dlux.version}/xml/features @@ -37,11 +38,12 @@ mvn:org.opendaylight.ovsdb/utils.servicehelper/${ovsdb.utils.servicehelper.version} mvn:org.opendaylight.ovsdb/openstack.net-virt/${openstack.netvirt.version} mvn:org.opendaylight.ovsdb/openstack.net-virt-providers/${openstack.netvirt.providers.version} + mvn:org.opendaylight.neutron/dummyprovider/${networkconfig.neutron.version} mvn:org.opendaylight.ovsdb/openstack.net-virt/${project.version}/xml/config mvn:org.opendaylight.ovsdb/openstack.net-virt-providers/${project.version}/xml/config - odl-dlux-core + odl-dlux-core mvn:org.opendaylight.ovsdb/ovsdb-ui-bundle/${ovsdb.ui.version} diff --git a/openstack/net-virt-providers/pom.xml b/openstack/net-virt-providers/pom.xml index ceb03c0330..76ca876bdd 100644 --- a/openstack/net-virt-providers/pom.xml +++ b/openstack/net-virt-providers/pom.xml @@ -103,11 +103,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html org.opendaylight.controller.model model-inventory - - org.opendaylight.neutron - neutron-spi - ${networkconfig.neutron.version} - org.opendaylight.openflowplugin.model model-flow-base @@ -263,6 +258,9 @@ and is available at http://www.eclipse.org/legal/epl-v10.html utils.config,utils.mdsal-openflow;type=!pom;inline=false true + + org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.* + diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13Provider.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13Provider.java index 12879dadda..c354d2dcef 100644 --- a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13Provider.java +++ b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13Provider.java @@ -19,10 +19,10 @@ import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.neutron.spi.NeutronNetwork; -import org.opendaylight.neutron.spi.NeutronPort; -import org.opendaylight.neutron.spi.NeutronSecurityGroup; -import org.opendaylight.neutron.spi.Neutron_IPs; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronPort; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup; +import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs; import org.opendaylight.ovsdb.openstack.netvirt.MdsalHelper; import org.opendaylight.ovsdb.openstack.netvirt.NetworkHandler; import org.opendaylight.ovsdb.openstack.netvirt.api.BridgeConfigurationManager; @@ -1044,6 +1044,62 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider { LOG.warn("programLocalRules: No DCHP port seen in network of {}", intf); } } + + /* + * The function is for the new compute node joining the existing network. + * When a new VM is instantiated in the new compute node, neutron port add + * event is generated. This event is processed only for that node. So, + * loop through all the ports of the same network and install unicast mac + * flow for the VM's created on the TEP of the destination node in src node. + * This function will be executed even for any new VM creation in an existing + * network. If a cache is maintained to optimize the below flow addition, it will + * work only for one unstack and restack. For the next unstack and restack, + * it will not work since the cache would have been already deleted. + */ + private void programTunnelRulesInNewNode(NeutronNetwork network, + String networkType, String segmentationId, + InetAddress src, InetAddress dst, + Node srcBridgeNode, Node dstBridgeNode, + OvsdbTerminationPointAugmentation intf){ + try { + long localPort = southbound.getOFPort(intf); + if(localPort != 0) + { + LOG.debug("Interface update details {}", intf); + + /* + * When a network is added and the TEP destination is not present in a + * node C1, tunnelin and broadcast rules will not be programmed, since + * OF port is not created. So, when a new node C2 joins and create a new + * VM, the tunnelin and broadcast rule will not be present in C1. + * So, handling it in the case below to make ping work. + */ + if(securityServicesManager.getNeutronPortFromDhcpIntf(intf) == null){ + programTunnelRules(networkType, segmentationId, src, dstBridgeNode, intf, true); + } + + /* + * FIX for 4208 - loop through all the ports and add the VM's + * unicast mac rule of the destination node in the source node. + * When a new node is added, it needs to configure the VM unicast mac + * flow rules which were created before it was joined to an existing + * network. + */ + List ports = southbound.getTerminationPointsOfBridge(dstBridgeNode); + for (OvsdbTerminationPointAugmentation port : ports) { + if(network == tenantNetworkManager.getTenantNetwork(port)){ + programTunnelRules(networkType, segmentationId, dst, srcBridgeNode, port, false); + } + else{ + LOG.trace("Port {} is not part of network {}", port, network); + } + } + } + } catch (Exception e) { + LOG.error("Exception during handlingNeutron network add", e); + } + } + @Override public boolean handleInterfaceUpdate(NeutronNetwork network, Node srcNode, OvsdbTerminationPointAugmentation intf) { @@ -1060,7 +1116,7 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider { programVlanRules(network, srcNode, intf); } else if (isTunnel(networkType)){ - boolean sourceTunnelStatus; + boolean sourceTunnelStatus = false; boolean destTunnelStatus = false; for (Node dstNode : nodes.values()) { InetAddress src = configurationService.getTunnelEndPoint(srcNode); @@ -1080,6 +1136,8 @@ public class OF13Provider implements ConfigInterface, NetworkingProvider { } if (destTunnelStatus) { programTunnelRules(networkType, segmentationId, src, dstBridgeNode, intf, false); + programTunnelRulesInNewNode(network, networkType, segmentationId, src, dst, + srcBridgeNode, dstBridgeNode, intf); } } else { LOG.warn("Tunnel end-point configuration missing. Please configure it in OpenVSwitch Table. " diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclService.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclService.java index ea0ab828b4..7faf6b048f 100644 --- a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclService.java +++ b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclService.java @@ -11,9 +11,9 @@ package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services; import java.math.BigInteger; import java.util.List; -import org.opendaylight.neutron.spi.NeutronSecurityGroup; -import org.opendaylight.neutron.spi.NeutronSecurityRule; -import org.opendaylight.neutron.spi.Neutron_IPs; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityRule; +import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs; import org.opendaylight.ovsdb.openstack.netvirt.api.Constants; import org.opendaylight.ovsdb.openstack.netvirt.api.EgressAclProvider; import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager; @@ -49,6 +49,8 @@ public class EgressAclService extends AbstractServiceInstance implements EgressA private static final int DHCP_SOURCE_PORT = 67; private static final int DHCP_DESTINATION_PORT = 68; private static final String HOST_MASK = "/32"; + private static final int PORT_RANGE_MIN = 1; + private static final int PORT_RANGE_MAX = 65535; public EgressAclService() { super(Service.EGRESS_ACL); @@ -345,13 +347,22 @@ public class EgressAclService extends AbstractServiceInstance implements EgressA NeutronSecurityRule portSecurityRule, String dstAddress, boolean write, Integer protoPortMatchPriority) { MatchBuilder matchBuilder = new MatchBuilder(); - String flowId = "Egress_Custom_Tcp" + segmentationId + "_" + srcMac + "_"; + String flowId = "Egress_TCP_" + segmentationId + "_" + srcMac + "_"; matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,srcMac,null); + + /* Custom TCP Match */ if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) { - flowId = flowId + portSecurityRule.getSecurityRulePortMin(); + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"; matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0, portSecurityRule.getSecurityRulePortMin()); } else { + /* All TCP Match */ + if(portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN) + && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) { + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_" + + portSecurityRule.getSecurityRulePortMax()+ "_"; + matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0, 0); + } /*TODO TCP PortRange Match*/ } @@ -367,7 +378,7 @@ public class EgressAclService extends AbstractServiceInstance implements EgressA new Ipv4Prefix(portSecurityRule .getSecurityRuleRemoteIpPrefix())); } - flowId = flowId + "_Permit_"; + flowId = flowId + "_Permit"; String nodeName = Constants.OPENFLOW_NODE_PREFIX + dpidLong; NodeBuilder nodeBuilder = createNodeBuilder(nodeName); syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false); @@ -430,13 +441,22 @@ public class EgressAclService extends AbstractServiceInstance implements EgressA boolean write, Integer protoPortMatchPriority) { MatchBuilder matchBuilder = new MatchBuilder(); - String flowId = "Eress_UDP" + segmentationId + "_" + srcMac + "_"; + String flowId = "Egress_UDP_" + segmentationId + "_" + srcMac + "_"; matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,srcMac,null); + + /* Custom UDP Match */ if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) { - flowId = flowId + portSecurityRule.getSecurityRulePortMin(); + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"; matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0, portSecurityRule.getSecurityRulePortMin()); } else { + /* All UDP Match */ + if(portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN) + && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) { + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_" + + portSecurityRule.getSecurityRulePortMax()+ "_"; + matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0, 0); + } /*TODO UDP PortRange Match*/ } @@ -452,7 +472,7 @@ public class EgressAclService extends AbstractServiceInstance implements EgressA new Ipv4Prefix(portSecurityRule .getSecurityRuleRemoteIpPrefix())); } - flowId = flowId + "_Permit_"; + flowId = flowId + "_Permit"; String nodeName = Constants.OPENFLOW_NODE_PREFIX + dpidLong; NodeBuilder nodeBuilder = createNodeBuilder(nodeName); syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false); diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/InboundNatService.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/InboundNatService.java index 2fe4ec5f77..2b9fb0ee01 100644 --- a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/InboundNatService.java +++ b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/InboundNatService.java @@ -10,6 +10,8 @@ package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services; import java.math.BigInteger; import java.net.InetAddress; +import java.net.Inet6Address; +import java.net.UnknownHostException; import java.util.List; import org.opendaylight.ovsdb.openstack.netvirt.api.Action; @@ -42,8 +44,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev14 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxRegCaseBuilder; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class InboundNatService extends AbstractServiceInstance implements ConfigInterface, InboundNatProvider { + private static final Logger LOG = LoggerFactory.getLogger(InboundNatService.class); public static final Class REG_FIELD = NxmNxReg3.class; public InboundNatService() { @@ -127,6 +132,20 @@ public class InboundNatService extends AbstractServiceInstance implements Config InstructionBuilder ib; MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(segmentationId)); + String ipAddress = excludedCidr.substring(0, excludedCidr.indexOf("/")); + InetAddress inetAddress; + try { + inetAddress = InetAddress.getByName(ipAddress); + } catch (UnknownHostException e) { + return new Status(StatusCode.BADREQUEST); + } + if (inetAddress instanceof Inet6Address) { + // WORKAROUND: For now ipv6 is not supported + // TODO: implement ipv6 cidr case + LOG.debug("ipv6 cidr is not implemented yet. cidr {}", + excludedCidr); + return new Status(StatusCode.NOTIMPLEMENTED); + } MatchUtils.createDstL3IPv4Match(matchBuilder, new Ipv4Prefix(excludedCidr)); // Goto Next Table diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclService.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclService.java index 9a2eb90f75..e8fa6b14c6 100644 --- a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclService.java +++ b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclService.java @@ -11,9 +11,9 @@ package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services; import java.math.BigInteger; import java.util.List; -import org.opendaylight.neutron.spi.NeutronSecurityGroup; -import org.opendaylight.neutron.spi.NeutronSecurityRule; -import org.opendaylight.neutron.spi.Neutron_IPs; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityRule; +import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs; import org.opendaylight.ovsdb.openstack.netvirt.api.Constants; import org.opendaylight.ovsdb.openstack.netvirt.api.IngressAclProvider; import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager; @@ -46,6 +46,8 @@ public class IngressAclService extends AbstractServiceInstance implements Ingres private static final Logger LOG = LoggerFactory.getLogger(IngressAclService.class); private volatile SecurityServicesManager securityServicesManager; + private static final int PORT_RANGE_MIN = 1; + private static final int PORT_RANGE_MAX = 65535; public IngressAclService() { super(Service.INGRESS_ACL); @@ -312,13 +314,22 @@ public class IngressAclService extends AbstractServiceInstance implements Ingres MatchBuilder matchBuilder = new MatchBuilder(); FlowBuilder flowBuilder = new FlowBuilder(); - String flowId = "Ingress_Custom_Tcp" + segmentationId + "_" + dstMac + "_"; + String flowId = "Ingress_TCP_" + segmentationId + "_" + dstMac + "_"; matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,null,dstMac); + + /* Custom TCP Match*/ if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) { - flowId = flowId + portSecurityRule.getSecurityRulePortMin(); + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"; matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0, portSecurityRule.getSecurityRulePortMin()); } else { + /* All TCP Match */ + if(portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN) + && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) { + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_" + + portSecurityRule.getSecurityRulePortMax()+ "_"; + matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.TCP_SHORT, 0, 0); + } /*TODO TCP PortRange Match*/ } @@ -336,7 +347,7 @@ public class IngressAclService extends AbstractServiceInstance implements Ingres } String nodeName = Constants.OPENFLOW_NODE_PREFIX + dpidLong; NodeBuilder nodeBuilder = createNodeBuilder(nodeName); - flowId = flowId + "_Permit_"; + flowId = flowId + "_Permit"; syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false); } @@ -357,13 +368,22 @@ public class IngressAclService extends AbstractServiceInstance implements Ingres NeutronSecurityRule portSecurityRule, String srcAddress, boolean write, Integer protoPortMatchPriority ) { MatchBuilder matchBuilder = new MatchBuilder(); - String flowId = "ingressAclUDP" + segmentationId + "_" + dstMac + "_"; + String flowId = "Ingress_UDP_" + segmentationId + "_" + dstMac + "_"; matchBuilder = MatchUtils.createEtherMatchWithType(matchBuilder,null,dstMac); + + /* Custom UDP Match */ if (portSecurityRule.getSecurityRulePortMin().equals(portSecurityRule.getSecurityRulePortMax())) { - flowId = flowId + portSecurityRule.getSecurityRulePortMin(); + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_"; matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0, portSecurityRule.getSecurityRulePortMin()); } else { + /* All UDP Match */ + if(portSecurityRule.getSecurityRulePortMin().equals(PORT_RANGE_MIN) + && portSecurityRule.getSecurityRulePortMax().equals(PORT_RANGE_MAX)) { + flowId = flowId + portSecurityRule.getSecurityRulePortMin() + "_" + + portSecurityRule.getSecurityRulePortMax()+ "_"; + matchBuilder = MatchUtils.addLayer4Match(matchBuilder, MatchUtils.UDP_SHORT, 0, 0); + } /*TODO TCP PortRange Match*/ } @@ -380,7 +400,7 @@ public class IngressAclService extends AbstractServiceInstance implements Ingres } String nodeName = Constants.OPENFLOW_NODE_PREFIX + dpidLong; NodeBuilder nodeBuilder = createNodeBuilder(nodeName); - flowId = flowId + "_Permit_"; + flowId = flowId + "_Permit"; syncFlow(flowId, nodeBuilder, matchBuilder, protoPortMatchPriority, write, false); } diff --git a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/OutboundNatService.java b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/OutboundNatService.java index d5389b1241..6b44c38d57 100644 --- a/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/OutboundNatService.java +++ b/openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/OutboundNatService.java @@ -10,6 +10,8 @@ package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services; import java.math.BigInteger; import java.net.InetAddress; +import java.net.Inet6Address; +import java.net.UnknownHostException; import java.util.List; import org.opendaylight.ovsdb.openstack.netvirt.api.Action; @@ -44,8 +46,12 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N import com.google.common.collect.Lists; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class OutboundNatService extends AbstractServiceInstance implements OutboundNatProvider, ConfigInterface { + private static final Logger LOG = LoggerFactory.getLogger(OutboundNatService.class); + public OutboundNatService() { super(Service.OUTBOUND_NAT); } @@ -164,6 +170,20 @@ public class OutboundNatService extends AbstractServiceInstance implements Outbo InstructionBuilder ib; MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(segmentationId)); + String ipAddress = excludedCidr.substring(0, excludedCidr.indexOf("/")); + InetAddress inetAddress; + try { + inetAddress = InetAddress.getByName(ipAddress); + } catch (UnknownHostException e) { + return new Status(StatusCode.BADREQUEST); + } + if (inetAddress instanceof Inet6Address) { + // WORKAROUND: For now ipv6 is not supported + // TODO: implement ipv6 cidr case + LOG.debug("ipv6 cidr is not implemented yet. cidr {}", + excludedCidr); + return new Status(StatusCode.NOTIMPLEMENTED); + } MatchUtils.createDstL3IPv4Match(matchBuilder, new Ipv4Prefix(excludedCidr)); // Goto Next Table diff --git a/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13ProviderTest.java b/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13ProviderTest.java index 4dff41cf63..a4ce294d9d 100644 --- a/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13ProviderTest.java +++ b/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/OF13ProviderTest.java @@ -34,7 +34,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.opendaylight.neutron.spi.NeutronNetwork; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork; import org.opendaylight.ovsdb.openstack.netvirt.MdsalHelper; import org.opendaylight.ovsdb.openstack.netvirt.NetworkHandler; import org.opendaylight.ovsdb.openstack.netvirt.api.BridgeConfigurationManager; diff --git a/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclServiceTest.java b/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclServiceTest.java index 531c238a15..e72b4ce687 100644 --- a/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclServiceTest.java +++ b/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/EgressAclServiceTest.java @@ -31,9 +31,10 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.neutron.spi.NeutronSecurityGroup; -import org.opendaylight.neutron.spi.NeutronSecurityRule; -import org.opendaylight.neutron.spi.Neutron_IPs; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityRule; +import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs; +import org.opendaylight.ovsdb.openstack.netvirt.api.Constants; import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager; import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrchestrator; import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service; @@ -43,6 +44,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Icmpv4Match; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.powermock.api.mockito.PowerMockito; import org.powermock.modules.junit4.PowerMockRunner; @@ -85,6 +88,8 @@ public class EgressAclServiceTest { private static final String SEGMENT_ID = "2"; private static final Long DP_ID_LONG = (long) 1554; private static final Long LOCAL_PORT = (long) 124; + private static final int PORT_RANGE_MIN = 1; + private static final int PORT_RANGE_MAX = 65535; private static FlowBuilder flowBuilder; private static NodeBuilder nodeBuilder; @@ -314,15 +319,24 @@ public class EgressAclServiceTest { @Test public void testProgramPortSecurityACLRuleAddTcp1() throws Exception { when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); - when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); - when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(20); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(20); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); - verify(writeTransaction, times(2)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match=(TcpMatch) match.getLayer4Match(); + Assert.assertEquals(20, layer4Match.getTcpDestinationPort().getValue().intValue()); + int port=portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** @@ -331,15 +345,25 @@ public class EgressAclServiceTest { @Test public void testProgramPortSecurityACLRuleRemoveTcp1() throws Exception { when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); - when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); - when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(30); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(30); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); - verify(writeTransaction, times(1)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match = (TcpMatch) match.getLayer4Match(); + Assert.assertEquals(30, layer4Match.getTcpDestinationPort().getValue().intValue()); + int port=portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** @@ -348,16 +372,31 @@ public class EgressAclServiceTest { @Test public void testProgramPortSecurityACLRuleAddTcp2() throws Exception { when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); - when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); - when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); - when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(40); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(40); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); - verify(writeTransaction, times(4)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match=(TcpMatch) match.getLayer4Match(); + int port=portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** @@ -368,14 +407,138 @@ public class EgressAclServiceTest { when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match=(TcpMatch) match.getLayer4Match(); + int port=portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + /** + * Test TCP add with port range (All TCP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddTcpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); + + Match match = flowBuilder.getMatch(); + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match=(TcpMatch) match.getLayer4Match(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + } + + /** + * Test TCP remove with port range (All TCP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveTcpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + Assert.assertEquals("Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); + } + + /** + * Test TCP add with port range (All TCP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddTcpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); - verify(writeTransaction, times(2)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + String expectedFlowId1 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + /** + * Test TCP remove with port range (All TCP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveTcpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + String expectedFlowId1 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Egress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** @@ -387,16 +550,25 @@ public class EgressAclServiceTest { when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); - verify(writeTransaction, times(2)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** - * Test UDP add with port no and CIDR selected. + * Test UDP remove with port no and CIDR selected. */ @Test public void testProgramPortSecurityACLRuleRemoveUdp1() throws Exception { @@ -404,12 +576,21 @@ public class EgressAclServiceTest { when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); - verify(writeTransaction, times(1)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** @@ -422,16 +603,33 @@ public class EgressAclServiceTest { when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); - verify(writeTransaction, times(4)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** - * Test UDP add with port no and remote SG selected. + * Test UDP remove with port no and remote SG selected. */ @Test public void testProgramPortSecurityACLRuleRemoveUdp2() throws Exception { @@ -440,12 +638,138 @@ public class EgressAclServiceTest { when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); - egressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); - verify(writeTransaction, times(2)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + + /** + * Test UDP add with port (All UDP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddUdpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + Assert.assertEquals("Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); + } + + /** + * Test UDP remove with port (All UDP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveUdpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + Assert.assertEquals("Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); + } + + /** + * Test UDP add with port (All UDP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddUdpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + String expectedFlowId1 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + /** + * Test UDP remove with port (All UDP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveUdpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(egressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + egressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + String expectedFlowId1 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Egress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** @@ -526,9 +850,9 @@ public class EgressAclServiceTest { Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); Short type = portSecurityRule.getSecurityRulePortMin().shortValue(); Short code = portSecurityRule.getSecurityRulePortMax().shortValue(); - String expectedFlowId1 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + type + "_" + code + "_" + String expectedFlowId1 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + type + "_" + code + "_" + DEST_IP_1 + "_Permit"; - String expectedFlowId2 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + type + "_" + code + "_" + String expectedFlowId2 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + type + "_" + code + "_" + DEST_IP_2 + "_Permit"; String actualFlowId = flowBuilder.getFlowName(); if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { @@ -561,9 +885,9 @@ public class EgressAclServiceTest { Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetSource().getAddress().getValue()); Short type = portSecurityRule.getSecurityRulePortMin().shortValue(); Short code = portSecurityRule.getSecurityRulePortMax().shortValue(); - String expectedFlowId1 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + type + "_" + code + "_" + String expectedFlowId1 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + type + "_" + code + "_" + DEST_IP_1 + "_Permit"; - String expectedFlowId2 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + type + "_" + code + "_" + String expectedFlowId2 = "Egress_ICMP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + type + "_" + code + "_" + DEST_IP_2 + "_Permit"; String actualFlowId = flowBuilder.getFlowName(); if(actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { diff --git a/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclServiceTest.java b/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclServiceTest.java index 6704a0a506..51734e827e 100644 --- a/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclServiceTest.java +++ b/openstack/net-virt-providers/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/IngressAclServiceTest.java @@ -37,9 +37,9 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.neutron.spi.NeutronSecurityGroup; -import org.opendaylight.neutron.spi.NeutronSecurityRule; -import org.opendaylight.neutron.spi.Neutron_IPs; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup; +import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityRule; +import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs; import org.opendaylight.ovsdb.openstack.netvirt.api.Constants; import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager; import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrchestrator; @@ -50,6 +50,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Icmpv4Match; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch; +import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.powermock.api.mockito.PowerMockito; import org.powermock.modules.junit4.PowerMockRunner; @@ -93,6 +95,8 @@ public class IngressAclServiceTest { private static final String SEGMENT_ID = "2"; private static final Long DP_ID_LONG = (long) 1554; private static final Long LOCAL_PORT = (long) 124; + private static final int PORT_RANGE_MIN = 1; + private static final int PORT_RANGE_MAX = 65535; private static FlowBuilder flowBuilder; private static NodeBuilder nodeBuilder; @@ -309,15 +313,25 @@ public class IngressAclServiceTest { @Test public void testProgramPortSecurityACLRuleAddTcp1() throws Exception { when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); - when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); - when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(20); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(20); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - verify(writeTransaction, times(2)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match = (TcpMatch) match.getLayer4Match(); + Assert.assertEquals(20, layer4Match.getTcpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** @@ -326,15 +340,25 @@ public class IngressAclServiceTest { @Test public void testProgramPortSecurityACLRuleRemoveTcp1() throws Exception { when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); - when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); - when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(15); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(15); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); - verify(writeTransaction, times(1)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match = (TcpMatch) match.getLayer4Match(); + Assert.assertEquals(15, layer4Match.getTcpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** @@ -347,12 +371,29 @@ public class IngressAclServiceTest { when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); - verify(writeTransaction, times(4)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match = (TcpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getTcpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** @@ -365,12 +406,143 @@ public class IngressAclServiceTest { when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); - verify(writeTransaction, times(2)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match = (TcpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getTcpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + + /** + * Test TCP add with port (All TCP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddTcpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + Assert.assertEquals("Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); + } + + /** + * Test TCP remove with port (All TCP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveTcpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + Assert.assertEquals("Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); + } + + /** + * Test TCP add with port (All TCP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddTcpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + String expectedFlowId1 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + /** + * Test TCP remove with port (All TCP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveTcpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("tcp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof TcpMatch); + TcpMatch layer4Match = (TcpMatch) match.getLayer4Match(); + String expectedFlowId1 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Ingress_TCP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** @@ -382,16 +554,26 @@ public class IngressAclServiceTest { when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - verify(writeTransaction, times(2)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** - * Test UDP add with port no and CIDR selected. + * Test UDP remove with port no and CIDR selected. */ @Test public void testProgramPortSecurityACLRuleRemoveUdp1() throws Exception { @@ -399,12 +581,22 @@ public class IngressAclServiceTest { when(portSecurityRule.getSecurityRulePortMax()).thenReturn(50); when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); - verify(writeTransaction, times(1)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(1)).submit(); - verify(commitFuture, times(1)).get(); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + Assert.assertEquals("Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + + "_" + port + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); } /** @@ -417,16 +609,33 @@ public class IngressAclServiceTest { when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,true); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); - verify(writeTransaction, times(4)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class), eq(true)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** - * Test UDP add with port no and remote SG selected. + * Test UDP remove with port no and remote SG selected. */ @Test public void testProgramPortSecurityACLRuleRemoveUdp2() throws Exception { @@ -435,12 +644,141 @@ public class IngressAclServiceTest { when(portSecurityRule.getSecurityRulePortMin()).thenReturn(50); when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); - ingressAclServiceSpy.programPortSecurityAcl(Long.valueOf(1554), "2", MAC_ADDRESS, 124, securityGroup,neutronSrcIpList,false); + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); - verify(writeTransaction, times(2)).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)); - verify(writeTransaction, times(2)).submit(); - verify(commitFuture, times(2)).get(); + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + UdpMatch layer4Match = (UdpMatch) match.getLayer4Match(); + Assert.assertEquals(50, layer4Match.getUdpDestinationPort().getValue().intValue()); + int port = portSecurityRule.getSecurityRulePortMin(); + String expectedFlowId1 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_1 + + "_Permit"; + String expectedFlowId2 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + port + "_" + DEST_IP_2 + + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + /** + * Test UDP add with ports (All UDP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddUdpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + Assert.assertEquals("Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); + } + + /** + * Test UDP remove with ports (All UDP) and CIDR selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveUdpAll1() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + Assert.assertEquals("Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS + "_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_0.0.0.0/24_Permit", flowBuilder.getFlowName()); + } + + /** + * Test UDP add with ports (All UDP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleAddUdpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "writeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, true); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + String expectedFlowId1 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } + } + + /** + * Test UDP remove with ports (All UDP) and remote SG selected. + */ + @Test + public void testProgramPortSecurityACLRuleRemoveUdpAll2() throws Exception { + when(portSecurityRule.getSecurityRuleProtocol()).thenReturn("udp"); + when(portSecurityRule.getSecurityRulePortMax()).thenReturn(PORT_RANGE_MAX); + when(portSecurityRule.getSecurityRulePortMin()).thenReturn(PORT_RANGE_MIN); + when(portSecurityRule.getSecurityRuleRemoteIpPrefix()).thenReturn("0.0.0.0/24"); + when(portSecurityRule.getSecurityRemoteGroupID()).thenReturn("85cc3048-abc3-43cc-89b3-377341426ac5"); + PowerMockito.doAnswer(answer()).when(ingressAclServiceSpy, "removeFlow", any(FlowBuilder.class), + any(NodeBuilder.class)); + ingressAclServiceSpy.programPortSecurityAcl(DP_ID_LONG, SEGMENT_ID, MAC_ADDRESS, LOCAL_PORT, securityGroup, + neutronSrcIpList, false); + + Match match = flowBuilder.getMatch(); + EthernetMatch ethMatch = match.getEthernetMatch(); + Assert.assertEquals(MAC_ADDRESS, ethMatch.getEthernetDestination().getAddress().getValue()); + + Assert.assertTrue(match.getLayer4Match() instanceof UdpMatch); + String expectedFlowId1 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_1 + "_Permit"; + String expectedFlowId2 = "Ingress_UDP_" + SEGMENT_ID + "_" + MAC_ADDRESS +"_" + PORT_RANGE_MIN + "_" + + PORT_RANGE_MAX + "_" + DEST_IP_2 + "_Permit"; + String actualFlowId = flowBuilder.getFlowName(); + if (actualFlowId.equals(expectedFlowId1) || actualFlowId.equals(expectedFlowId2)) { + Assert.assertTrue(true); + } else { + Assert.assertTrue(false); + } } /** diff --git a/openstack/net-virt-sfc/api/pom.xml b/openstack/net-virt-sfc/api/pom.xml index 706531d6df..8ffd3c9421 100644 --- a/openstack/net-virt-sfc/api/pom.xml +++ b/openstack/net-virt-sfc/api/pom.xml @@ -22,6 +22,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html 1.2.1-SNAPSHOT bundle + + 0.2.0-SNAPSHOT + + org.opendaylight.mdsal.model @@ -35,5 +39,28 @@ and is available at http://www.eclipse.org/legal/epl-v10.html org.opendaylight.mdsal.model yang-ext + + org.opendaylight.sfc + sfc-model + ${sfc.project.version} + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + org.opendaylight.yang.gen.v1.*; + + + + + + + diff --git a/openstack/net-virt-sfc/api/src/main/yang/ietf-acl.yang b/openstack/net-virt-sfc/api/src/main/yang/ietf-acl.yang deleted file mode 100644 index 3549cb4cc7..0000000000 --- a/openstack/net-virt-sfc/api/src/main/yang/ietf-acl.yang +++ /dev/null @@ -1,199 +0,0 @@ -module ietf-acl { - yang-version 1; - - namespace "urn:ietf:params:xml:ns:yang:ietf-acl"; - - prefix acl; - - import ietf-yang-types { - prefix "ietf"; - } - - import packet-fields { - prefix "packet-fields"; - } - - organization - "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; - - contact - "WG Web: http://tools.ietf.org/wg/netmod/ - WG List: netmod@ietf.org - - WG Chair: Juergen Schoenwaelder - j.schoenwaelder@jacobs-university.de - - WG Chair: Tom Nadeau - tnadeau@lucidvision.com - - Editor: Dean Bogdanovic - deanb@juniper.net - - Editor: Kiran Agrahara Sreenivasa - kkoushik@brocade.com - - Editor: Lisa Huang - yihuan@cisco.com - - Editor: Dana Blair - dblair@cisco.com"; - - description - "This YANG module defines a component that describing the - configuration of Access Control Lists (ACLs)."; - - revision 2014-10-10 { - description "Creating base model for netmod."; - reference - "RFC 6020: YANG - A Data Modeling Language for the - Network Configuration Protocol (NETCONF)"; - } - - identity acl-base { - description "Base acl type for all ACL type identifiers."; - } - - identity ip-acl { - base "acl:acl-base"; - description "layer 3 ACL type"; - } - identity eth-acl { - base "acl:acl-base"; - description "layer 2 ACL type"; - } - - typedef acl-type { - type identityref { - base "acl-base"; - } - description - "This type is used to refer to an Access Control List - (ACL) type"; - } - - typedef acl-ref { - type leafref { - path "/acl:access-lists/acl:access-list/acl:acl-name"; - } - description "This type is used by data models that - need to referenced an acl"; - } - - container access-lists { - description - "Access control lists."; - - list access-list { - key acl-name; - description " - An access list (acl) is an ordered list of - access list entries (ace). Each ace has a - sequence number to define the order, list - of match criteria, and a list of actions. - Since there are several kinds of acls - implementeded with different attributes for - each and different for each vendor, this - model accomodates customizing acls for - each kind and for each vendor. - "; - - leaf acl-name { - type string; - description "The name of access-list. - A device MAY restrict the length and value of - this name, possibly space and special - characters are not allowed."; - } - - leaf acl-type { - type acl-type; - description "Type of ACL"; - } - - container acl-oper-data { - config false; - - description "Overall ACL operational data"; - leaf match-counter { - type ietf:counter64; - description "Total match count for ACL"; - } - - leaf-list targets { - type string; - description "List of targets where ACL is applied"; - } - } - - container access-list-entries { - description "The access-list-entries container contains - a list of access-list-entry(ACE)."; - - list access-list-entry { - key rule-name; - ordered-by user; - - description "List of access list entries(ACE)"; - leaf rule-name { - type string; - description "Entry name."; - } - - container matches { - description "Define match criteria"; - choice ace-type { - description "Type of ace."; - case ace-ip { - uses packet-fields:acl-ip-header-fields; - choice ace-ip-version { - description "Choice of IP version."; - case ace-ipv4 { - uses packet-fields:acl-ipv4-header-fields; - } - case ace-ipv6 { - uses packet-fields:acl-ipv6-header-fields; - } - } - } - case ace-eth { - uses packet-fields:acl-eth-header-fields; - } - } - uses packet-fields:metadata; - } - - container actions { - description "Define action criteria"; - choice packet-handling { - default deny; - - description "Packet handling action."; - case deny { - leaf deny { - type empty; - description "Deny action."; - } - } - case permit { - leaf permit { - type empty; - description "Permit action."; - } - } - } - } - - container ace-oper-data { - config false; - - description "Per ace operational data"; - leaf match-counter { - type ietf:counter64; - description "Number of matches for an ace"; - } - } - } - } - } - } -} diff --git a/openstack/net-virt-sfc/api/src/main/yang/netvirt-acl.yang b/openstack/net-virt-sfc/api/src/main/yang/netvirt-acl.yang index 50b90bdb44..3d1a813e15 100644 --- a/openstack/net-virt-sfc/api/src/main/yang/netvirt-acl.yang +++ b/openstack/net-virt-sfc/api/src/main/yang/netvirt-acl.yang @@ -3,7 +3,7 @@ module netvirt-sfc-acl { namespace "urn:opendaylight:params:xml:ns:yang:netvirt:sfc:acl"; prefix "acl"; - import ietf-acl { prefix ietf-acl;} + import ietf-access-control-list { prefix ietf-acl;} import yang-ext { prefix ext; } revision "2015-01-05" { @@ -11,8 +11,8 @@ module netvirt-sfc-acl { } // TODO: Add choice for Neutron and add fields there instead of at the root of matches - augment "/ietf-acl:access-lists/ietf-acl:access-list/ietf-acl:access-list-entries" + - "/ietf-acl:access-list-entry/ietf-acl:matches" { + //augment "/ietf-acl:access-lists/ietf-acl:access-list/ietf-acl:access-list-entries/ietf-acl:access-list-entry/ietf-acl:matches" { + augment "/ietf-acl:access-lists/ietf-acl:acl/ietf-acl:access-list-entries/ietf-acl:ace/ietf-acl:matches" { description "Neutron network uuid"; leaf network-uuid { type string; @@ -20,8 +20,7 @@ module netvirt-sfc-acl { } // TODO: Add choice for Neutron and add fields there instead of at the root of matches - augment "/ietf-acl:access-lists/ietf-acl:access-list/ietf-acl:access-list-entries" + - "/ietf-acl:access-list-entry/ietf-acl:actions" { + augment "/ietf-acl:access-lists/ietf-acl:acl/ietf-acl:access-list-entries/ietf-acl:ace/ietf-acl:actions" { description "Redirect traffic to SFC identified by SFC Path ID"; leaf redirect-sfc { type string; diff --git a/openstack/net-virt-sfc/api/src/main/yang/packet-fields.yang b/openstack/net-virt-sfc/api/src/main/yang/packet-fields.yang deleted file mode 100644 index bea6777071..0000000000 --- a/openstack/net-virt-sfc/api/src/main/yang/packet-fields.yang +++ /dev/null @@ -1,152 +0,0 @@ -module packet-fields { - yang-version 1; - - namespace "urn:ietf:params:xml:ns:yang:packet-fields"; - - prefix packet-fields; - - import ietf-inet-types { - prefix "inet"; - } - - import ietf-yang-types { - prefix "yang"; - } - - revision 2014-06-25 { - description "Initial version of packet fields used by access-lists"; - } - - grouping acl-transport-header-fields { - description "Transport header fields"; - - container source-port-range { - description "inclusive range of source ports"; - leaf lower-port { - mandatory true; - type inet:port-number; - } - leaf upper-port { - type inet:port-number; - } - } - - container destination-port-range { - description "inclusive range of destination ports"; - leaf lower-port { - mandatory true; - type inet:port-number; - } - leaf upper-port { - type inet:port-number; - } - } - } - - grouping acl-ip-header-fields { - description "Header fields common to ipv4 and ipv6"; - - uses acl-transport-header-fields; - - leaf dscp { - type inet:dscp; - } - - leaf ip-protocol { - type uint8; - } - - } - - grouping acl-ipv4-header-fields { - description "fields in IPv4 header"; - - leaf destination-ipv4-address { - type inet:ipv4-prefix; - } - - leaf source-ipv4-address { - type inet:ipv4-prefix; - } - - } - - grouping acl-ipv6-header-fields { - description "fields in IPv6 header"; - - leaf destination-ipv6-address { - type inet:ipv6-prefix; - } - - leaf source-ipv6-address { - type inet:ipv6-prefix; - } - - leaf flow-label { - type inet:ipv6-flow-label; - } - - } - - grouping acl-eth-header-fields { - description "fields in ethernet header"; - - leaf destination-mac-address { - type yang:mac-address; - } - - leaf destination-mac-address-mask { - type yang:mac-address; - } - - leaf source-mac-address { - type yang:mac-address; - } - - leaf source-mac-address-mask { - type yang:mac-address; - } - } - - grouping timerange { - description "Define time range entries to restrict - the access. The time range is identified by a name - and then referenced by a function, so that those - time restrictions are imposed on the function itself."; - - container absolute { - description - "Absolute time and date that - the associated function starts - going into effect."; - - leaf start { - type yang:date-and-time; - description - "Start time and date"; - } - leaf end { - type yang:date-and-time; - description "Absolute end time and date"; - } - leaf active { - type boolean; - default "true"; - description - "Specify the associated function - active or inactive state when - starts going into effect"; - } - } // container absolute - } //grouping timerange - - grouping metadata { - description "Fields associated with a packet but not in the header"; - - leaf input-interface { - description "Packet was received on this interface"; - type string; - } - uses timerange; - } -} diff --git a/openstack/net-virt-sfc/features/production/pom.xml b/openstack/net-virt-sfc/features/production/pom.xml index 6953792d36..01fb239e0a 100644 --- a/openstack/net-virt-sfc/features/production/pom.xml +++ b/openstack/net-virt-sfc/features/production/pom.xml @@ -24,12 +24,14 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL 3.1.1 + etc/opendaylight/karaf + 0.3.0-SNAPSHOT 0.8.0-SNAPSHOT 1.3.0-SNAPSHOT + 0.2.0-SNAPSHOT 1.3.0-SNAPSHOT + 0.2.0-SNAPSHOT 0.8.0-SNAPSHOT - 0.3.0-SNAPSHOT - etc/opendaylight/karaf @@ -48,6 +50,13 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL pom import + + org.opendaylight.ovsdb + ovsdb-artifacts + ${project.version} + pom + import + @@ -91,6 +100,37 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL xml runtime + + org.opendaylight.ovsdb + features-ovsdb + features + ${project.version} + xml + runtime + + + org.opendaylight.openflowplugin + features-openflowplugin + ${openflowplugin.version} + features + xml + + + org.opendaylight.sfc + features-sfc + ${sfc.version} + features + xml + runtime + + + org.opendaylight.sfc + features-sfc-ovs + ${sfc.version} + features + xml + runtime + ${project.groupId} openstack.net-virt-sfc-impl @@ -120,5 +160,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL utils.mdsal-utils ${project.version} + + org.opendaylight.ovsdb + utils.servicehelper + ${project.version} + diff --git a/openstack/net-virt-sfc/features/production/src/main/features/features.xml b/openstack/net-virt-sfc/features/production/src/main/features/features.xml index 09cff5347d..1b4ca16239 100644 --- a/openstack/net-virt-sfc/features/production/src/main/features/features.xml +++ b/openstack/net-virt-sfc/features/production/src/main/features/features.xml @@ -10,22 +10,32 @@ and is available at http://www.eclipse.org/legal/epl-v10.html - mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features mvn:org.opendaylight.controller/features-mdsal/${mdsal.version}/xml/features + mvn:org.opendaylight.dlux/features-dlux/${dlux.version}/xml/features mvn:org.opendaylight.mdsal.model/features-mdsal-model/${mdsal.model.version}/xml/features mvn:org.opendaylight.netconf/features-restconf/${restconf.version}/xml/features - mvn:org.opendaylight.dlux/features-dlux/${dlux.version}/xml/features - mvn:org.opendaylight.ovsdb/southbound-features/1.2.1-SNAPSHOT/xml/features + mvn:org.opendaylight.openflowplugin/features-openflowplugin/${openflowplugin.version}/xml/features + mvn:org.opendaylight.ovsdb/features-ovsdb/${project.version}/xml/features + mvn:org.opendaylight.ovsdb/southbound-features/${project.version}/xml/features + mvn:org.opendaylight.sfc/features-sfc/${sfc.version}/xml/features + mvn:org.opendaylight.sfc/features-sfc-ovs/${sfc.version}/xml/features + mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features odl-mdsal-models mvn:org.opendaylight.ovsdb/openstack.net-virt-sfc-api/${project.version} odl-mdsal-broker - odl-ovsdb-sfc-api + odl-openflowplugin-nsf-model odl-ovsdb-southbound-impl - mvn:org.opendaylight.ovsdb/openstack.net-virt-sfc-impl/${project.version} + odl-ovsdb-openstack + odl-sfc-core + odl-sfc-ovs + odl-openflowplugin-flow-services + odl-ovsdb-sfc-api mvn:org.opendaylight.ovsdb/utils.mdsal-utils/${project.version} + mvn:org.opendaylight.ovsdb/utils.servicehelper/${project.version} + mvn:org.opendaylight.ovsdb/openstack.net-virt-sfc-impl/${project.version} mvn:org.opendaylight.ovsdb/openstack.net-virt-sfc-impl/${project.version}/xml/config diff --git a/openstack/net-virt-sfc/features/test/src/main/features/features.xml b/openstack/net-virt-sfc/features/test/src/main/features/features.xml index 224fc9bb08..9dd3154362 100644 --- a/openstack/net-virt-sfc/features/test/src/main/features/features.xml +++ b/openstack/net-virt-sfc/features/test/src/main/features/features.xml @@ -14,7 +14,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mvn:org.opendaylight.ovsdb/utils.mdsal-utils/${project.version} mvn:org.opendaylight.ovsdb/utils.southbound-utils/${project.version} - odl-ovsdb-southbound-impl-ui odl-ovsdb-sfc-ui diff --git a/openstack/net-virt-sfc/impl/pom.xml b/openstack/net-virt-sfc/impl/pom.xml index 9de4d9c88a..80f9a173c9 100644 --- a/openstack/net-virt-sfc/impl/pom.xml +++ b/openstack/net-virt-sfc/impl/pom.xml @@ -25,7 +25,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html bundle + 0.6.0-SNAPSHOT + 0.2.0-SNAPSHOT ../it/target/jacoco-it.exec + 0.2.0-SNAPSHOT @@ -34,6 +37,31 @@ and is available at http://www.eclipse.org/legal/epl-v10.html openstack.net-virt-sfc-api ${project.version} + + ${project.groupId} + openstack.net-virt + ${project.version} + + + ${project.groupId} + openstack.net-virt-providers + ${project.version} + + + ${project.groupId} + utils.mdsal-utils + ${project.version} + + + ${project.groupId} + utils.mdsal-openflow + ${project.version} + + + org.opendaylight.ovsdb + utils.servicehelper + ${project.version} + org.slf4j slf4j-api @@ -59,9 +87,33 @@ and is available at http://www.eclipse.org/legal/epl-v10.html sal-common-api - org.opendaylight.ovsdb - utils.mdsal-utils - ${project.version} + org.opendaylight.openflowplugin.model + model-flow-base + ${openflowplugin.version} + + + org.opendaylight.openflowplugin.model + model-flow-service + ${openflowplugin.version} + + + org.opendaylight.mdsal.model + ietf-topology + + + org.opendaylight.sfc + sfc-model + ${sfc.project.version} + + + org.opendaylight.sfc + sfc-ovs + ${sfc.project.version} + + + org.opendaylight.sfc + sfc-provider + ${sfc.project.version} org.opendaylight.yangtools @@ -92,11 +144,22 @@ and is available at http://www.eclipse.org/legal/epl-v10.html + + org.apache.felix + maven-bundle-plugin + true + + + utils.mdsal-openflow;type=!pom;inline=false + true + + + org.apache.maven.plugins maven-checkstyle-plugin - checkstyle.checker.severity=error + checkstyle.violationSeverity=error diff --git a/openstack/net-virt-sfc/impl/src/main/config/default-config.xml b/openstack/net-virt-sfc/impl/src/main/config/default-config.xml index 6423930f6f..1aa106ae18 100644 --- a/openstack/net-virt-sfc/impl/src/main/config/default-config.xml +++ b/openstack/net-virt-sfc/impl/src/main/config/default-config.xml @@ -11,6 +11,8 @@ and is available at http://www.eclipse.org/legal/epl-v10.html urn:opendaylight:params:xml:ns:yang:netvirt:sfc?module=netvirt-sfc&revision=2014-12-10 urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 urn:opendaylight:params:xml:ns:yang:southbound:impl?module=southbound-impl&revision=2014-12-10 + urn:opendaylight:params:xml:ns:yang:netvirt:impl?module=netvirt-impl&revision=2015-05-13 + urn:opendaylight:params:xml:ns:yang:netvirt:providers:impl?module=netvirt-providers-impl&revision=2015-05-13 diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcAclListener.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcAclListener.java index 302be54ae4..f263c0f5df 100644 --- a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcAclListener.java +++ b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcAclListener.java @@ -14,12 +14,12 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.ovsdb.openstack.netvirt.sfc.openflow13.INetvirtSfcOF13Provider; import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.AccessLists; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessList; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessListKey; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.AccessListEntries; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.AccessListEntry; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.AccessListEntryKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.AccessLists; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.AclKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.AccessListEntries; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.AceKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff; @@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory; /** * Data tree listener for AccessList. */ -public class NetvirtSfcAclListener extends AbstractDataTreeListener { +public class NetvirtSfcAclListener extends AbstractDataTreeListener { private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcAclListener.class); private ListenerRegistration listenerRegistration; private MdsalUtils dbutils; @@ -42,7 +42,7 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener * @param db MdSal {@link DataBroker} */ public NetvirtSfcAclListener(final INetvirtSfcOF13Provider provider, final DataBroker db) { - super(provider, AccessList.class); + super(provider, Acl.class); Preconditions.checkNotNull(db, "DataBroker can not be null!"); dbutils = new MdsalUtils(db); @@ -50,10 +50,10 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener } private void registrationListener(final DataBroker db) { - final DataTreeIdentifier treeId = + final DataTreeIdentifier treeId = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, getIetfAclIid()); try { - LOG.info("Registering Data Change Listener for Netvirt AccesList configuration."); + LOG.info("Registering Data Change Listener for NetvirtSfc AccesList configuration."); listenerRegistration = db.registerDataTreeChangeListener(treeId, this); } catch (final Exception e) { LOG.warn("Netvirt AccesList DataChange listener registration fail!"); @@ -76,8 +76,8 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener } @Override - public void remove(final InstanceIdentifier identifier, - final AccessList removeDataObj) { + public void remove(final InstanceIdentifier identifier, + final Acl removeDataObj) { Preconditions.checkNotNull(removeDataObj, "Removed object can not be null!"); String aclName = removeDataObj.getAclName(); @@ -96,13 +96,13 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener } @Override - public void update(final InstanceIdentifier identifier, - final AccessList original, final AccessList update) { + public void update(final InstanceIdentifier identifier, + final Acl original, final Acl update) { } @Override - public void add(final InstanceIdentifier identifier, - final AccessList addDataObj) { + public void add(final InstanceIdentifier identifier, + final Acl addDataObj) { Preconditions.checkNotNull(addDataObj, "Added object can not be null!"); String aclName = addDataObj.getAclName(); LOG.debug("Adding accesslist = {}", identifier); @@ -124,19 +124,19 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener return InstanceIdentifier.create(Classifiers.class); } - public InstanceIdentifier getIetfAclIid() { - return InstanceIdentifier.create(AccessLists.class).child(AccessList.class); + public InstanceIdentifier getIetfAclIid() { + return InstanceIdentifier.create(AccessLists.class).child(Acl.class); } /** - * Create an {@link AccessListEntry} {@link InstanceIdentifier}. + * Create an {@link Ace} {@link InstanceIdentifier}. * @param aclName is the name of the ACL * @param ruleName is the name of the rule - * @return the {@link AccessListEntry} {@link InstanceIdentifier} + * @return the {@link Ace} {@link InstanceIdentifier} */ - public InstanceIdentifier getIetfAclEntryIid(String aclName, String ruleName) { - return InstanceIdentifier.create(AccessLists.class).child(AccessList.class, - new AccessListKey(aclName)).child(AccessListEntries.class).child(AccessListEntry.class, - new AccessListEntryKey(ruleName)); + public InstanceIdentifier getIetfAclEntryIid(String aclName, String ruleName) { + return InstanceIdentifier.create(AccessLists.class).child(Acl.class, + new AclKey(aclName)).child(AccessListEntries.class).child(Ace.class, + new AceKey(ruleName)); } } diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcClassifierListener.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcClassifierListener.java index df1102f83c..af8c8968f3 100644 --- a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcClassifierListener.java +++ b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcClassifierListener.java @@ -14,9 +14,9 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.ovsdb.openstack.netvirt.sfc.openflow13.INetvirtSfcOF13Provider; import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.AccessLists; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessList; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessListKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.AccessLists; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.AclKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff; @@ -51,7 +51,7 @@ public class NetvirtSfcClassifierListener extends AbstractDataTreeListener treeId = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, getClassifierIid()); try { - LOG.info("Registering Data Change Listener for Netvirt Classifier configuration."); + LOG.info("Registering Data Change Listener for NetvirtSfc Classifier configuration."); listenerRegistration = db.registerDataTreeChangeListener(treeId, this); } catch (final Exception e) { LOG.warn("Netvirt Classifier DataChange listener registration fail!"); @@ -79,7 +79,7 @@ public class NetvirtSfcClassifierListener extends AbstractDataTreeListener getIetfAclIid(String aclName) { - return InstanceIdentifier.create(AccessLists.class).child(AccessList.class, new AccessListKey(aclName)); + private InstanceIdentifier getIetfAclIid(String aclName) { + return InstanceIdentifier.create(AccessLists.class).child(Acl.class, new AclKey(aclName)); } } diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcProvider.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcProvider.java index 144514f9df..3dba7b4b57 100644 --- a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcProvider.java +++ b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcProvider.java @@ -19,26 +19,17 @@ import org.slf4j.LoggerFactory; public class NetvirtSfcProvider implements BindingAwareProvider, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcProvider.class); - private DataBroker dataBroker = null; - private BundleContext bundleContext = null; private NetvirtSfcAclListener aclListener; - private NetvirtSfcClassifierListener classfierListener; - private INetvirtSfcOF13Provider provider; - - public NetvirtSfcProvider(BundleContext bundleContext) { - LOG.info("NetvirtProvider: bundleContext: {}", bundleContext); - this.bundleContext = bundleContext; - } @Override public void onSessionInitiated(ProviderContext session) { LOG.info("NetvirtSfcProvider Session Initiated"); - dataBroker = session.getSALService(DataBroker.class); + DataBroker dataBroker = session.getSALService(DataBroker.class); - provider = new NetvirtSfcOF13Provider(this.dataBroker); - aclListener = new NetvirtSfcAclListener(provider, this.dataBroker); - classfierListener = new NetvirtSfcClassifierListener(provider, this.dataBroker); + INetvirtSfcOF13Provider provider = new NetvirtSfcOF13Provider(dataBroker); + aclListener = new NetvirtSfcAclListener(provider, dataBroker); + classfierListener = new NetvirtSfcClassifierListener(provider, dataBroker); } @Override diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/INetvirtSfcOF13Provider.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/INetvirtSfcOF13Provider.java index 067999ec2d..43ff43f021 100644 --- a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/INetvirtSfcOF13Provider.java +++ b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/INetvirtSfcOF13Provider.java @@ -8,7 +8,7 @@ package org.opendaylight.ovsdb.openstack.netvirt.sfc.openflow13; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessList; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff; /** @@ -24,7 +24,7 @@ public interface INetvirtSfcOF13Provider { * @param sff - Service Function Forwarder * @param acl - Access list includes rules that need to be installed in a SFF. */ - public void addClassifierRules(Sff sff, AccessList acl); + public void addClassifierRules(Sff sff, Acl acl); /** * Method removes the OF rules corresponding to rules within ACL @@ -33,5 +33,5 @@ public interface INetvirtSfcOF13Provider { * @param sff - Service Function Forwarder * @param acl - Access list includes rules that need to be installed in a SFF. */ - public void removeClassifierRules(Sff sff, AccessList acl); + public void removeClassifierRules(Sff sff, Acl acl); } diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NetvirtSfcOF13Provider.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NetvirtSfcOF13Provider.java index ca75a5879c..a44b62360f 100644 --- a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NetvirtSfcOF13Provider.java +++ b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NetvirtSfcOF13Provider.java @@ -8,32 +8,281 @@ package org.opendaylight.ovsdb.openstack.netvirt.sfc.openflow13; +import java.util.Iterator; +import java.util.List; + import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessList; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.ovsdb.openstack.netvirt.api.Constants; +import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager; +import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound; +import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrchestrator; +import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service; +import org.opendaylight.ovsdb.utils.mdsal.openflow.InstructionUtils; +import org.opendaylight.ovsdb.utils.mdsal.openflow.MatchUtils; +import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils; +import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper; +import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI; +import org.opendaylight.sfc.sfc_ovs.provider.SfcOvsUtil; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.Actions; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.Matches; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.actions.packet.handling.Deny; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Open vSwitch OpenFlow 1.3 Networking Provider for Netvirt SFC * @author Arun Yerra */ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{ + private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcOF13Provider.class); + private static final int DEFAULT_FLOW_PRIORITY = 32768; + private volatile NodeCacheManager nodeCacheManager; + private volatile Southbound southbound; + private MdsalUtils dbutils; + private PipelineOrchestrator orchestrator; - private final DataBroker dataService; - + /** + * {@link NetvirtSfcOF13Provider} constructor. + * @param dataBroker MdSal {@link DataBroker} + */ public NetvirtSfcOF13Provider(final DataBroker dataBroker) { - this.dataService = Preconditions.checkNotNull(dataBroker, "DataBroker can not be null!"); + Preconditions.checkNotNull(dataBroker, "Input dataBroker cannot be NULL!"); + + //this.dataService = dataBroker; + dbutils = new MdsalUtils(dataBroker); + + this.setDependencies(null); } @Override - public void addClassifierRules(Sff sff, AccessList acl) { - // TODO Auto-generated method stub + public void addClassifierRules(Sff sff, Acl acl) { + Preconditions.checkNotNull(sff, "Input service function forwarder cannot be NULL!"); + Preconditions.checkNotNull(acl, "Input accesslist cannot be NULL!"); + + // Validate if any service function forwarder exists by the name, using SFC provider APIs. + ServiceFunctionForwarder serviceForwarder = + SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sff.getName()); + if (serviceForwarder == null) { + LOG.debug("Service Function Forwarder = {} not yet configured. Skip processing !!", sff.getName()); + return; + } + + // If a service function forwarder exists, then get the corresponding OVS Bridge details and Openflow NodeId. + // If OVS Bridge augmentation is configured, the following API returns NULL. + String datapathId = SfcOvsUtil.getOpenFlowNodeIdForSff(serviceForwarder); + if (datapathId == null) { + LOG.debug("Service Function Forwarder = {} is not augemented with " + + "OVS Bridge Information. Skip processing!!", sff.getName()); + } + // If openflow Node Id is NULL, get all the bridge nodes using southbound apis and fetch + // SFF with matching name. From this bridge name, get the openflow data path ID. + if (datapathId == null) { + Node node = null; + final List nodes = nodeCacheManager.getBridgeNodes(); + if (nodes.isEmpty()) { + LOG.debug("Noop with Classifier Creation on SFF={}. No Bridges configured YET!!", sff.getName()); + } else { + for (Node dstNode : nodes) { + LOG.debug("Processing Node={}, sff={}", dstNode.getNodeId().getValue(), sff.getName()); + if (dstNode.getNodeId().getValue().equalsIgnoreCase(sff.getName())) { + LOG.debug("Found matching OVSDB Bridge Name!!= {}", dstNode.getNodeId().getValue()); + node = dstNode; + break; + } + } + } + } + + LOG.debug("Processing the Classifier rules on Node={}", datapathId); + if (datapathId != null) { + // Program the OF flow on the corresponding open flow node. + Iterator itr = acl.getAccessListEntries().getAce().iterator(); + while (itr.hasNext()) { + Ace entry = itr.next(); + programOfRules(entry, datapathId, true); + } + } + } + + private void programOfRules(Ace entry, String datapathId, boolean write) { + NodeBuilder nodeBuilder = new NodeBuilder(); + nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + datapathId)); + nodeBuilder.setKey(new NodeKey(nodeBuilder.getId())); + + //Create the match using match builder, by parsing the Accesslist Entry Match. + MatchBuilder matchBuilder = null; + matchBuilder = buildMatch(entry.getRuleName(), entry.getMatches(), datapathId); + + InstructionsBuilder isb = null; + isb = buildActions(entry.getRuleName(), entry.getActions(), datapathId); + + String flowId = "NETVIRT_SFC_FLOW" + "_" + entry.getRuleName(); + + FlowBuilder flowBuilder = new FlowBuilder(); + flowBuilder.setId(new FlowId(flowId)); + FlowKey key = new FlowKey(new FlowId(flowId)); + flowBuilder.setMatch(matchBuilder.build()); + flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY); + flowBuilder.setBarrier(true); + flowBuilder.setTableId(this.getTable()); + flowBuilder.setKey(key); + flowBuilder.setFlowName(flowId); + flowBuilder.setHardTimeout(0); + flowBuilder.setIdleTimeout(0); + + flowBuilder.setInstructions(isb.build()); + + if (write) { + writeFlow(flowBuilder, nodeBuilder); + } else { + removeFlow(flowBuilder, nodeBuilder); + } + } + + private InstructionsBuilder buildActions(String ruleName, Actions actions, String datapathId) { + InstructionBuilder ib = new InstructionBuilder(); + + if (actions.getPacketHandling() instanceof Deny) { + InstructionUtils.createDropInstructions(ib); + } else if (actions.getPacketHandling() instanceof Permit) { + //Permit actPermit = (Permit) actions.getPacketHandling(); + } else { + InstructionUtils.createDropInstructions(ib); + } + + ib.setOrder(0); + ib.setKey(new InstructionKey(0)); + // Instructions List Stores Individual Instructions + List instructions = Lists.newArrayList(); + instructions.add(ib.build()); + + // Call the InstructionBuilder Methods Containing Actions + ib = this.getMutablePipelineInstructionBuilder(); + ib.setOrder(1); + ib.setKey(new InstructionKey(1)); + instructions.add(ib.build()); + // Add InstructionBuilder to the Instruction(s)Builder List + InstructionsBuilder isb = new InstructionsBuilder(); + isb.setInstruction(instructions); + return isb; + } + + private MatchBuilder buildMatch(String ruleName, Matches matches, String dpId) { + MatchBuilder matchBuilder = new MatchBuilder(); + + if (matches.getAceType() instanceof AceIp) { + AceIp aceIp = (AceIp)matches.getAceType(); + if (aceIp.getAceIpVersion() instanceof AceIpv4) { + AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion(); + MatchUtils.createSrcL3IPv4Match(matchBuilder, aceIpv4.getSourceIpv4Network()); + MatchUtils.createDstL3IPv4Match(matchBuilder, aceIpv4.getDestinationIpv4Network()); + MatchUtils.createIpProtocolMatch(matchBuilder, aceIp.getProtocol()); + MatchUtils.addLayer4Match(matchBuilder, aceIp.getProtocol().intValue(), + aceIp.getSourcePortRange().getLowerPort().getValue().intValue(), + aceIp.getDestinationPortRange().getLowerPort().getValue().intValue()); + } + } else if (matches.getAceType() instanceof AceEth) { + AceEth aceEth = (AceEth) matches.getAceType(); + MatchUtils.createEthSrcMatch(matchBuilder, new MacAddress(aceEth.getSourceMacAddress().getValue())); + MatchUtils.createDestEthMatch(matchBuilder, new MacAddress(aceEth.getDestinationMacAddress().getValue()), + new MacAddress(aceEth.getDestinationMacAddressMask().getValue())); + } + + //MatchUtils.createInPortMatch(matchBuilder, Long.getLong(dpId), Long.getLong(matches.getInputInterface())); + return matchBuilder; } @Override - public void removeClassifierRules(Sff sff, AccessList acl) { + public void removeClassifierRules(Sff sff, Acl acl) { // TODO Auto-generated method stub } + + + protected void writeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) { + LOG.debug("writeFlow: flowBuilder: {}, nodeBuilder: {}", + flowBuilder.build(), nodeBuilder.build()); + dbutils.merge(LogicalDatastoreType.CONFIGURATION, createNodePath(nodeBuilder), nodeBuilder.build()); + dbutils.put(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder), flowBuilder.build()); + } + + protected void removeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) { + dbutils.delete(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder)); + } + + private String getDpid(Node node) { + long dpid = southbound.getDataPathId(node); + if (dpid == 0) { + LOG.warn("getDpid: DPID could not be found for node: {}", node.getNodeId().getValue()); + } + return String.valueOf(dpid); + } + + private static InstanceIdentifier createFlowPath(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) { + return InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, + nodeBuilder.getKey()) + .augmentation(FlowCapableNode.class) + .child(Table.class, new TableKey(flowBuilder.getTableId())) + .child(Flow.class, flowBuilder.getKey()).build(); + } + + private static InstanceIdentifier + createNodePath(NodeBuilder nodeBuilder) { + return InstanceIdentifier.builder(Nodes.class) + .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, + nodeBuilder.getKey()).build(); + } + + private short getTable() { + return Service.INGRESS_ACL.getTable(); + } + + private final InstructionBuilder getMutablePipelineInstructionBuilder() { + Service nextService = orchestrator.getNextServiceInPipeline(Service.INGRESS_ACL); + if (nextService != null) { + return InstructionUtils.createGotoTableInstructions(new InstructionBuilder(), nextService.getTable()); + } else { + return InstructionUtils.createDropInstructions(new InstructionBuilder()); + } + } + + private void setDependencies(ServiceReference serviceReference) { + nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this); + southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this); + orchestrator = (PipelineOrchestrator) ServiceHelper.getGlobalInstance(PipelineOrchestrator.class, this); + } } diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModule.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModule.java index 9135aeb68c..137b77f8ae 100644 --- a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModule.java +++ b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModule.java @@ -8,7 +8,6 @@ import org.osgi.framework.BundleContext; public class NetvirtSfcModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.rev141210.AbstractNetvirtSfcModule { private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcModule.class); - private BundleContext bundleContext = null; public NetvirtSfcModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); @@ -26,13 +25,8 @@ public class NetvirtSfcModule extends org.opendaylight.yang.gen.v1.urn.opendayli @Override public java.lang.AutoCloseable createInstance() { LOG.info("Netvirt SFC module initialization."); - //final NetvirtSfcProvider sfcProvider = new NetvirtSfcProvider(getDataBrokerDependency()); - final NetvirtSfcProvider sfcProvider = new NetvirtSfcProvider(bundleContext); + NetvirtSfcProvider sfcProvider = new NetvirtSfcProvider(); getBrokerDependency().registerProvider(sfcProvider); return sfcProvider; } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } } diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleFactory.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleFactory.java index 6bbc368238..8468b165e5 100644 --- a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleFactory.java +++ b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleFactory.java @@ -17,36 +17,4 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NetvirtSfcModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.rev141210.AbstractNetvirtSfcModuleFactory { - private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcModuleFactory.class); - - @Override - public Module createModule(String instanceName, - DependencyResolver dependencyResolver, - DynamicMBeanWithInstance old, BundleContext bundleContext) - throws Exception { - Module module = super.createModule(instanceName, dependencyResolver, old, bundleContext); - LOG.info("Created NetvirtSfcModule1= {}!!", (module instanceof NetvirtSfcModule)); - setModuleBundleContext(bundleContext, module); - return module; - } - - @Override - public Module createModule(String instanceName, - DependencyResolver dependencyResolver, BundleContext bundleContext) { - Module module = super.createModule(instanceName, dependencyResolver, bundleContext); - LOG.info("Created NetvirtSfcModule2= {}!!", (module instanceof NetvirtSfcModule)); - setModuleBundleContext(bundleContext, module); - return module; - } - - private void setModuleBundleContext(BundleContext bundleContext, - Module module) { - if (module instanceof NetvirtSfcModule) { - LOG.info("Setting Bundle Context for NetvirtSfcModule!!"); - ((NetvirtSfcModule)module).setBundleContext(bundleContext); - } else { - LOG.warn("Module is of type {} expected type {}", - module.getClass(), NetvirtSfcModule.class); - } - } } diff --git a/openstack/net-virt-sfc/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleTest.java b/openstack/net-virt-sfc/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleTest.java index 6bb1e19d29..c5b03f0ed7 100644 --- a/openstack/net-virt-sfc/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleTest.java +++ b/openstack/net-virt-sfc/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/netvirt/sfc/rev141210/NetvirtSfcModuleTest.java @@ -14,6 +14,7 @@ import org.opendaylight.controller.config.api.ModuleIdentifier; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.ovsdb.openstack.netvirt.sfc.NetvirtSfcProvider; +import org.osgi.framework.BundleContext; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; import javax.management.ObjectName; @@ -45,7 +46,6 @@ public class NetvirtSfcModuleTest { // create instance of module with injected mocks NetvirtSfcModule module = new NetvirtSfcModule(mock(ModuleIdentifier.class), dependencyResolver); - //module.setDataBroker(mock(ObjectName.class)); // getInstance calls resolveInstance to get the broker dependency and then calls createInstance AutoCloseable closeable = module.getInstance(); ((NetvirtSfcProvider)closeable).onSessionInitiated(session); diff --git a/openstack/net-virt-sfc/it/pom.xml b/openstack/net-virt-sfc/it/pom.xml index bb8fc92fbb..a9dc254eb8 100644 --- a/openstack/net-virt-sfc/it/pom.xml +++ b/openstack/net-virt-sfc/it/pom.xml @@ -53,15 +53,18 @@ and is available at http://www.eclipse.org/legal/epl-v10.html ${project.groupId} utils.mdsal-utils ${project.version} + test ${project.groupId} utils.southbound-utils ${project.version} + test org.opendaylight.yangtools concepts + test org.codehaus.sonar-plugins.java @@ -76,8 +79,20 @@ and is available at http://www.eclipse.org/legal/epl-v10.html org.jacoco jacoco-maven-plugin + + org.apache.maven.plugins + maven-checkstyle-plugin + + checkstyle.violationSeverity=error + + maven-failsafe-plugin + diff --git a/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcIT.java b/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcIT.java index 9eef40d7af..443656e68e 100644 --- a/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcIT.java +++ b/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcIT.java @@ -8,6 +8,7 @@ package org.opendaylight.ovsdb.openstack.netvirt.sfc; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -30,20 +31,21 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; +import org.opendaylight.ovsdb.openstack.netvirt.api.Constants; import org.opendaylight.ovsdb.openstack.netvirt.sfc.utils.AclUtils; import org.opendaylight.ovsdb.openstack.netvirt.sfc.utils.ClassifierUtils; import org.opendaylight.ovsdb.openstack.netvirt.sfc.utils.SfcUtils; -import org.opendaylight.ovsdb.southbound.SouthboundConstants; import org.opendaylight.ovsdb.southbound.SouthboundUtil; import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils; import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.AccessLists; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.AccessListsBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessListBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.AccessListEntriesBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.AccessListEntryBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.access.list.entry.ActionsBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.access.list.entry.MatchesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.AccessLists; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.AccessListsBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.AccessListEntriesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.AceBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.ActionsBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.MatchesBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.ClassifiersBuilder; @@ -55,6 +57,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt. import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -63,6 +69,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.karaf.options.LogLevelOption; import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel; import org.ops4j.pax.exam.options.MavenUrlReference; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; @@ -89,7 +96,8 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase { public static final String CONNECTION_TYPE_ACTIVE = "active"; public static final String CONNECTION_TYPE_PASSIVE = "passive"; public static final String DEFAULT_SERVER_PORT = "6640"; - public static final String BRIDGE_NAME = "brtest"; + public static final String INTEGRATION_BRIDGE_NAME = "br-int"; + private static final String NETVIRT_TOPOLOGY_ID = "netvirt:1"; @Override public String getModuleName() { @@ -145,11 +153,17 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase { @Override public Option getLoggingOption() { - Option option = editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG, - logConfiguration(NetvirtSfcIT.class), - LogLevel.INFO.name()); - option = composite(option, super.getLoggingOption()); - return option; + return composite( + editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG, + logConfiguration(NetvirtSfcIT.class), + LogLevel.INFO.name()), + editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG, + "log4j.logger.org.opendaylight.ovsdb.openstack.netvirt.sfc", + LogLevel.INFO.name()), + /*editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG, + "log4j.logger.org.opendaylight.ovsdb", + LogLevelOption.LogLevel.TRACE.name()),*/ + super.getLoggingOption()); } protected String usage() { @@ -181,16 +195,19 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase { } try { + Thread.sleep(1000); super.setup(); } catch (Exception e) { e.printStackTrace(); } + getProperties(); + DataBroker dataBroker = getDatabroker(getProviderContext()); mdsalUtils = new MdsalUtils(dataBroker); assertNotNull("mdsalUtils should not be null", mdsalUtils); southboundUtils = new SouthboundUtils(mdsalUtils); - getProperties(); + assertTrue("Did not find " + NETVIRT_TOPOLOGY_ID, getNetvirtTopology()); setup.set(true); } @@ -224,6 +241,30 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase { return dataBroker; } + private Boolean getNetvirtTopology() { + LOG.info("getNetvirtTopology: looking for {}...", NETVIRT_TOPOLOGY_ID); + Boolean found = false; + final TopologyId topologyId = new TopologyId(new Uri(NETVIRT_TOPOLOGY_ID)); + InstanceIdentifier path = + InstanceIdentifier.create(NetworkTopology.class).child(Topology.class, new TopologyKey(topologyId)); + for (int i = 0; i < 60; i++) { + Topology topology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, path); + if (topology != null) { + LOG.info("getNetvirtTopology: found {}...", NETVIRT_TOPOLOGY_ID); + found = true; + break; + } else { + LOG.info("getNetvirtTopology: still looking ({})...", i); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + return found; + } + @Test public void testNetvirtSfcFeatureLoad() { assertTrue(true); @@ -232,11 +273,11 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase { private AccessListsBuilder setAccessLists () { MatchesBuilder matchesBuilder = aclUtils.createMatches(new MatchesBuilder(), 80); ActionsBuilder actionsBuilder = aclUtils.createActions(new ActionsBuilder(), Boolean.TRUE); - AccessListEntryBuilder accessListEntryBuilder = aclUtils.createAccessListEntryBuilder( - new AccessListEntryBuilder(), "http", matchesBuilder, actionsBuilder); + AceBuilder accessListEntryBuilder = aclUtils.createAccessListEntryBuilder( + new AceBuilder(), "http", matchesBuilder, actionsBuilder); AccessListEntriesBuilder accessListEntriesBuilder = aclUtils.createAccessListEntries( new AccessListEntriesBuilder(), accessListEntryBuilder); - AccessListBuilder accessListBuilder = aclUtils.createAccessList(new AccessListBuilder(), + AclBuilder accessListBuilder = aclUtils.createAccessList(new AclBuilder(), "http", accessListEntriesBuilder); AccessListsBuilder accessListsBuilder = aclUtils.createAccessLists(new AccessListsBuilder(), accessListBuilder); @@ -286,35 +327,41 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase { assertNull(clazz.getSimpleName() + " should be null", result); } + /* + * Connect to an ovsdb node. Netvirt should add br-int, add the controller address + * and program the pipeline flows. + */ @Test public void testDoIt() throws InterruptedException { ConnectionInfo connectionInfo = southboundUtils.getConnectionInfo(addressStr, portStr); - Node ovsdbNode = southboundUtils.connectOvsdbNode(connectionInfo); - - String controllerTarget = SouthboundUtil.getControllerTarget(ovsdbNode); - assertNotNull("Failed to get controller target", controllerTarget); - List setControllerEntry = southboundUtils.createControllerEntry(controllerTarget); - Uri setUri = new Uri(controllerTarget); - Assert.assertTrue(southboundUtils.addBridge(connectionInfo, null, BRIDGE_NAME, null, true, - SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, - setControllerEntry, null)); - OvsdbBridgeAugmentation bridge = southboundUtils.getBridge(connectionInfo, BRIDGE_NAME); - Assert.assertNotNull("bridge was not found: " + BRIDGE_NAME, bridge); - Assert.assertNotNull("ControllerEntry was not found: " + setControllerEntry.iterator().next(), - bridge.getControllerEntry()); - List getControllerEntries = bridge.getControllerEntry(); - for (ControllerEntry entry : getControllerEntries) { - if (entry.getTarget() != null) { - Assert.assertEquals(setUri.toString(), entry.getTarget().toString()); + assertNotNull("connection failed", southboundUtils.connectOvsdbNode(connectionInfo)); + assertNotNull("node is not connected", southboundUtils.getOvsdbNode(connectionInfo)); + ControllerEntry controllerEntry; + // Loop 10s checking if the controller was added + for (int i = 0; i < 10; i++) { + Node ovsdbNode = southboundUtils.getOvsdbNode(connectionInfo); + assertNotNull("ovsdb node not found", ovsdbNode); + String controllerTarget = SouthboundUtil.getControllerTarget(ovsdbNode); + assertNotNull("Failed to get controller target", controllerTarget); + OvsdbBridgeAugmentation bridge = southboundUtils.getBridge(connectionInfo, INTEGRATION_BRIDGE_NAME); + assertNotNull(bridge); + assertNotNull(bridge.getControllerEntry()); + controllerEntry = bridge.getControllerEntry().iterator().next(); + assertEquals(controllerTarget, controllerEntry.getTarget().getValue()); + if (controllerEntry.isIsConnected()) { + Assert.assertTrue(controllerEntry.isIsConnected()); + break; } + Thread.sleep(1000); } /* TODO: add code to write to mdsal to exercise the sfc dataChangeListener */ /* allow some time to let the impl code do it's work to push flows */ /* or just comment out below lines and just manually verify on the bridges and reset them */ - Thread.sleep(10000); + //Thread.sleep(10000); - Assert.assertTrue(southboundUtils.deleteBridge(connectionInfo, BRIDGE_NAME)); - southboundUtils.disconnectOvsdbNode(connectionInfo); + assertTrue(southboundUtils.deleteBridge(connectionInfo, INTEGRATION_BRIDGE_NAME)); + Thread.sleep(1000); + assertTrue(southboundUtils.disconnectOvsdbNode(connectionInfo)); } } diff --git a/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/utils/AclUtils.java b/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/utils/AclUtils.java index ac214bb98f..dd6cce50dc 100644 --- a/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/utils/AclUtils.java +++ b/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/utils/AclUtils.java @@ -10,18 +10,18 @@ package org.opendaylight.ovsdb.openstack.netvirt.sfc.utils; import java.util.ArrayList; import java.util.List; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.AccessListsBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessList; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.AccessListBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.AccessListEntriesBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.AccessListEntry; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.AccessListEntryBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.access.list.entry.ActionsBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.access.list.entry.MatchesBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.access.list.entry.actions.packet.handling.PermitBuilder; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev141010.access.lists.access.list.access.list.entries.access.list.entry.matches.ace.type.AceIpBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.AccessListsBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.AccessListEntriesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.AceBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.ActionsBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.MatchesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.actions.packet.handling.PermitBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.DestinationPortRangeBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev150611.acl.transport.header.fields.DestinationPortRangeBuilder; public class AclUtils { public MatchesBuilder createMatches (MatchesBuilder matchesBuilder, int destPort) { @@ -45,10 +45,10 @@ public class AclUtils { return actionsBuilder; } - public AccessListEntryBuilder createAccessListEntryBuilder(AccessListEntryBuilder accessListEntryBuilder, - String ruleName, - MatchesBuilder matchesBuilder, - ActionsBuilder actionsBuilder) { + public AceBuilder createAccessListEntryBuilder(AceBuilder accessListEntryBuilder, + String ruleName, + MatchesBuilder matchesBuilder, + ActionsBuilder actionsBuilder) { accessListEntryBuilder.setRuleName(ruleName); accessListEntryBuilder.setMatches(matchesBuilder.build()); accessListEntryBuilder.setActions(actionsBuilder.build()); @@ -57,16 +57,16 @@ public class AclUtils { } public AccessListEntriesBuilder createAccessListEntries(AccessListEntriesBuilder accessListEntriesBuilder, - AccessListEntryBuilder accessListEntryBuilder) { - List accessListEntriesList = new ArrayList<>(); + AceBuilder accessListEntryBuilder) { + List accessListEntriesList = new ArrayList<>(); accessListEntriesList.add(accessListEntryBuilder.build()); return accessListEntriesBuilder; } - public AccessListBuilder createAccessList(AccessListBuilder accessListBuilder, - String aclName, - AccessListEntriesBuilder accessListEntriesBuilder) { + public AclBuilder createAccessList(AclBuilder accessListBuilder, + String aclName, + AccessListEntriesBuilder accessListEntriesBuilder) { accessListBuilder.setAclName(aclName); accessListBuilder.setAccessListEntries(accessListEntriesBuilder.build()); @@ -74,10 +74,10 @@ public class AclUtils { } public AccessListsBuilder createAccessLists(AccessListsBuilder accessListsBuilder, - AccessListBuilder accessListBuilder) { - List accessListList = new ArrayList<>(); + AclBuilder accessListBuilder) { + List accessListList = new ArrayList<>(); accessListList.add(accessListBuilder.build()); - accessListsBuilder.setAccessList(accessListList); + accessListsBuilder.setAcl(accessListList); return accessListsBuilder; } diff --git a/openstack/net-virt-sfc/karaf/pom.xml b/openstack/net-virt-sfc/karaf/pom.xml index b7c0375e2f..2119c4f54a 100644 --- a/openstack/net-virt-sfc/karaf/pom.xml +++ b/openstack/net-virt-sfc/karaf/pom.xml @@ -25,6 +25,17 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL odl-ovsdb-sfc-test + + + + ${project.groupId} + openstack.net-virt-sfc-artifacts + ${project.version} + pom + import + + + - -
- In progress - -
diff --git a/ovsdb-ui/module/src/main/resources/ovsdb/lib/d3.min.js b/ovsdb-ui/module/src/main/resources/ovsdb/lib/d3.min.js new file mode 100644 index 0000000000..34d5513ebf --- /dev/null +++ b/ovsdb-ui/module/src/main/resources/ovsdb/lib/d3.min.js @@ -0,0 +1,5 @@ +!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function r(n){return null===n?0/0:+n}function u(n){return!isNaN(n)}function i(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function l(){this._=Object.create(null)}function s(n){return(n+="")===pa||n[0]===va?va+n:n}function f(n){return(n+="")[0]===va?n.slice(1):n}function h(n){return s(n)in this._}function g(n){return(n=s(n))in this._&&delete this._[n]}function p(){var n=[];for(var t in this._)n.push(f(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function m(){this._=Object.create(null)}function y(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=da.length;r>e;++e){var u=da[e]+t;if(u in n)return u}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function Z(n){return ya(n,Sa),n}function V(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var l=ka.get(n);return l&&(n=l,c=B),a?t?u:r:t?b:i}function $(n,t){return function(e){var r=ta.event;ta.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ta.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Aa,u="click"+r,i=ta.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ea&&(Ea="onselectstart"in e?!1:x(e.style,"userSelect")),Ea){var o=n(e).style,a=o[Ea];o[Ea]="none"}return function(n){if(i.on(r,null),Ea&&(o[Ea]=a),n){var t=function(){i.on(u,null)};i.on(u,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var u=r.createSVGPoint();if(0>Na){var i=t(n);if(i.scrollX||i.scrollY){r=ta.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Na=!(o.f||o.e),r.remove()}}return Na?(u.x=e.pageX,u.y=e.pageY):(u.x=e.clientX,u.y=e.clientY),u=u.matrixTransform(n.getScreenCTM().inverse()),[u.x,u.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ta.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nt(n){return n>1?0:-1>n?qa:Math.acos(n)}function tt(n){return n>1?Ra:-1>n?-Ra:Math.asin(n)}function et(n){return((n=Math.exp(n))-1/n)/2}function rt(n){return((n=Math.exp(n))+1/n)/2}function ut(n){return((n=Math.exp(2*n))-1)/(n+1)}function it(n){return(n=Math.sin(n/2))*n}function ot(){}function at(n,t,e){return this instanceof at?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof at?new at(n.h,n.s,n.l):bt(""+n,_t,at):new at(n,t,e)}function ct(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,new mt(u(n+120),u(n),u(n-120))}function lt(n,t,e){return this instanceof lt?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof lt?new lt(n.h,n.c,n.l):n instanceof ft?gt(n.l,n.a,n.b):gt((n=wt((n=ta.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new lt(n,t,e)}function st(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new ft(e,Math.cos(n*=Da)*t,Math.sin(n)*t)}function ft(n,t,e){return this instanceof ft?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof ft?new ft(n.l,n.a,n.b):n instanceof lt?st(n.h,n.c,n.l):wt((n=mt(n)).r,n.g,n.b):new ft(n,t,e)}function ht(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=pt(u)*Xa,r=pt(r)*$a,i=pt(i)*Ba,new mt(dt(3.2404542*u-1.5371385*r-.4985314*i),dt(-.969266*u+1.8760108*r+.041556*i),dt(.0556434*u-.2040259*r+1.0572252*i))}function gt(n,t,e){return n>0?new lt(Math.atan2(e,t)*Pa,Math.sqrt(t*t+e*e),n):new lt(0/0,0/0,n)}function pt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function vt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function dt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mt(n,t,e){return this instanceof mt?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mt?new mt(n.r,n.g,n.b):bt(""+n,mt,ct):new mt(n,t,e)}function yt(n){return new mt(n>>16,n>>8&255,255&n)}function Mt(n){return yt(n)+""}function xt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ga.get(n.toLowerCase()))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function _t(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),new at(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=vt((.4124564*n+.3575761*t+.1804375*e)/Xa),u=vt((.2126729*n+.7151522*t+.072175*e)/$a),i=vt((.0193339*n+.119192*t+.9503041*e)/Ba);return ft(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Nt(t,e,n,r)}}function Nt(n,t,e,r){function u(){var n,t=c.status;if(!t&&zt(c)||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return void o.error.call(i,r)}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=ta.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=ta.event;ta.event=n;try{o.progress.call(i,c)}finally{ta.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ra(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},ta.rebind(i,o,"on"),null==r?i:i.get(Ct(r))}function Ct(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function zt(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qt(){var n=Lt(),t=Tt()-n;t>24?(isFinite(t)&&(clearTimeout(tc),tc=setTimeout(qt,t)),nc=0):(nc=1,rc(qt))}function Lt(){var n=Date.now();for(ec=Ka;ec;)n>=ec.t&&(ec.f=ec.c(n-ec.t)),ec=ec.n;return n}function Tt(){for(var n,t=Ka,e=1/0;t;)t.f?t=n?n.n=t.n:Ka=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r&&e?function(n,t){for(var u=n.length,i=[],o=0,a=r[0],c=0;u>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),i.push(n.substring(u-=a,u+a)),!((c+=a+1)>t));)a=r[o=(o+1)%r.length];return i.reverse().join(e)}:y;return function(n){var e=ic.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",c=e[4]||"",l=e[5],s=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1,y=!0;switch(h&&(h=+h.substring(1)),(l||"0"===r&&"="===o)&&(l=r="0",o="="),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":y=!1;case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=oc.get(g)||Ut;var M=l&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>p){var c=ta.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=y?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!l&&f&&(x=i(x,1/0));var S=v.length+x.length+b.length+(M?0:u.length),k=s>S?new Array(S=s-S+1).join(r):"";return M&&(x=i(k+x,k.length?s-b.length:1/0)),u+=v,n=x+b,("<"===o?u+n+k:">"===o?k+u+n:"^"===o?k.substring(0,S>>=1)+u+n+k.substring(S):u+(M?n:k+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ft(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new cc(e-1)),1),e}function i(n,e){return t(n=new cc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{cc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{cc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ht(n);return c.floor=c,c.round=Ht(r),c.ceil=Ht(u),c.offset=Ht(i),c.range=a,n}function Ht(n){return function(t,e){try{cc=jt;var r=new jt;return r._=t,n(r,e)._}finally{cc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++aa;){if(r>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in sc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.slice(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,N.c.toString(),t,r)}function c(n,t,r){return e(n,N.x.toString(),t,r)}function l(n,t,r){return e(n,N.X.toString(),t,r)}function s(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{cc=jt;var t=new cc;return t._=n,r(t)}finally{cc=Date}}var r=t(n);return e.parse=function(n){try{cc=jt;var t=r.parse(n);return t&&t._}finally{cc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var M=ta.map(),x=Yt(v),b=Zt(v),_=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),A=Zt(y);p.forEach(function(n,t){M.set(n.toLowerCase(),t)});var N={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+ac.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(ac.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(ac.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:s,S:re,U:Xt,w:Vt,W:$t,x:c,X:l,y:Wt,Y:Bt,Z:Jt,"%":oe};return t}function It(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(ta.requote).join("|")+")","i")}function Zt(n){for(var t=new l,e=-1,r=n.length;++e68?1900:2e3)}function Kt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=ga(t)/60|0,u=ga(t)%60;return e+It(r,"0",2)+It(u,"0",2)}function oe(n,t,e){hc.lastIndex=0;var r=hc.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);yc.add(Math.atan2(h,f)),r=n,u=c,i=l}var t,e,r,u,i;Mc.point=function(o,a){Mc.point=n,r=(t=o)*Da,u=Math.cos(a=(e=a)*Da/2+qa/4),i=Math.sin(a)},Mc.lineEnd=function(){n(t,e)}}function pe(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Me(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function xe(n){return[Math.atan2(n[1],n[0]),tt(n[2])]}function be(n,t){return ga(n[0]-t[0])a;++a)u.point((e=n[a])[0],e[1]);return void u.lineEnd()}var c=new qe(e,n,null,!0),l=new qe(e,null,c,!1);c.o=l,i.push(c),o.push(l),c=new qe(r,n,null,!1),l=new qe(r,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),ze(i),ze(o),i.length){for(var a=0,c=e,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function ze(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r0){for(b||(i.polygonStart(),b=!0),i.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Te))}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:l,polygonStart:function(){y.point=s,y.lineStart=f,y.lineEnd=h,g=[],p=[]},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=l,g=ta.merge(g);var n=Fe(m,p);g.length?(b||(i.polygonStart(),b=!0),Ce(g,De,n,e,i)):n&&(b||(i.polygonStart(),b=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),b&&(i.polygonEnd(),b=!1),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},M=Re(),x=t(M),b=!1;return y}}function Te(n){return n.length>1}function Re(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function De(n,t){return((n=n.x)[0]<0?n[1]-Ra-Ca:Ra-n[1])-((t=t.x)[0]<0?t[1]-Ra-Ca:Ra-t[1])}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?qa:-qa,c=ga(i-e);ga(c-qa)0?Ra:-Ra),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=qa&&(ga(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*Ra,r.point(-qa,u),r.point(0,u),r.point(qa,u),r.point(qa,0),r.point(qa,-u),r.point(0,-u),r.point(-qa,-u),r.point(-qa,0),r.point(-qa,u);else if(ga(n[0]-t[0])>Ca){var i=n[0]a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+qa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+qa/4,M=Math.sin(y),x=Math.cos(y),b=m-h,_=b>=0?1:-1,w=_*b,S=w>qa,k=p*M;if(yc.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),i+=S?b+_*La:b,S^h>=e^m>=e){var E=de(pe(f),pe(n));Me(E);var A=de(u,E);Me(A);var N=(S^b>=0?-1:1)*tt(A[2]);(r>N||r===N&&(E[0]||E[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=m,p=M,v=x,f=n}}return(-Ca>i||Ca>i&&0>yc)^1&o}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?qa:-qa),h):0;if(!e&&(l=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(be(e,g)||be(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&be(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return s|(l&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),l=a[0],s=c-l*l;if(!s)return!e&&n;var f=i*c/s,h=-i*l/s,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),M=m*m-y*(ve(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=ye(d,(-m-x)/y);if(me(b,p),b=xe(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=ga(A-qa)A;if(!N&&k>E&&(_=k,k=E,E=_),C?N?k+E>0^b[1]<(ga(b[0]-w)qa^(w<=b[0]&&b[0]<=S)){var z=ye(d,(-m+x)/y);return me(z,p),[b,xe(z)]}}}function u(t,e){var r=o?n:qa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ga(i)>Ca,c=gr(n,6*Da);return Le(t,e,c,o?[0,-n]:[-qa,n-qa])}function Oe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Ie(n,t,e,r){function u(r,u){return ga(r[0]-n)0?0:3:ga(r[0]-e)0?2:1:ga(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&Q(l,i,n)>0&&++t:i[1]<=r&&Q(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:e,s>1?r:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,b=_=0/0}function g(){v&&(p(y,M),x&&w&&A.rejoin(),v.push(A.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=s(n,t);if(d&&m.push([n,t]),S)y=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};N(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,m,y,M,x,b,_,w,S,k,E=a,A=Re(),N=Oe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=ta.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Ce(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n){var t=0,e=qa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*qa/180,e=n[1]*qa/180):[t/qa*180,e/qa*180]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,tt((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){Dc+=u*n-r*t,r=n,u=t}var t,e,r,u;Hc.point=function(i,o){Hc.point=n,t=r=i,e=u=o},Hc.lineEnd=function(){n(t,e)}}function Xe(n,t){Pc>n&&(Pc=n),n>jc&&(jc=n),Uc>t&&(Uc=t),t>Fc&&(Fc=t)}function $e(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function We(n,t){_c+=n,wc+=t,++Sc}function Je(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);kc+=o*(t+n)/2,Ec+=o*(e+r)/2,Ac+=o,We(t=n,e=r)}var t,e;Ic.point=function(r,u){Ic.point=n,We(t=r,e=u)}}function Ge(){Ic.point=We}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);kc+=o*(r+n)/2,Ec+=o*(u+t)/2,Ac+=o,o=u*n-r*t,Nc+=o*(r+n),Cc+=o*(u+t),zc+=3*o,We(r=n,u=t)}var t,e,r,u;Ic.point=function(i,o){Ic.point=n,We(t=r=i,e=u=o)},Ic.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,La)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(M,x,y,b,_,w,M=o[0],x=o[1],y=e,b=i[0],_=i[1],w=i[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c +},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,M=f-e,x=y*y+M*M;if(x>4*i&&d--){var b=a+g,_=c+p,w=l+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),E=ga(ga(w)-1)i||ga((y*z+M*q)/x-.5)>.3||o>a*g+c*p+l*v)&&(u(t,e,r,a,c,l,N,C,E,b/=S,_/=S,w,d,m),m.point(N,C),u(N,C,E,b,_,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Da),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Pa,e*Pa])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*Da,n[1]*Da),[n[0]*h+c,l-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Pa,n[1]*Pa]}function r(){a=Ae(o=lr(m,M,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,M=0,x=0,b=Lc,_=y,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=or(b(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Lc):He((w=+n)*Da),u()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Ie(n[0][0],n[0][1],n[1][0],n[1][1]):y,u()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Da,d=n[1]%360*Da,r()):[v*Pa,d*Pa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Da,M=n[1]%360*Da,x=n.length>2?n[2]%360*Da:0,r()):[m*Pa,M*Pa,x*Pa]},ta.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*Da,e*Da)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>qa?n-La:-qa>n?n+La:n,t]}function lr(n,t,e){return n?t||e?Ae(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function sr(n){return function(t,e){return t+=n,[t>qa?t-La:-qa>t?t+La:t,e]}}function fr(n){var t=sr(n);return t.invert=sr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*r+a*u;return[Math.atan2(c*i-s*o,a*r-l*u),tt(s*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*r+s*u),tt(s*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*La)):(u=n+o*La,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=xe([e,-r*Math.cos(s),-r*Math.sin(s)]))[0],l[1])}}function pr(n,t){var e=pe(t);e[0]-=n,Me(e);var r=nt(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function vr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function Mr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(it(r-t)+u*o*it(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*s,u=e*l+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Pa,Math.atan2(o,Math.sqrt(r*r+u*u))*Pa]}:function(){return[n*Pa,t*Pa]};return p.distance=h,p}function xr(){function n(n,u){var i=Math.sin(u*=Da),o=Math.cos(u),a=ga((n*=Da)-t),c=Math.cos(a);Yc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Zc.point=function(u,i){t=u*Da,e=Math.sin(i*=Da),r=Math.cos(i),Zc.point=n},Zc.lineEnd=function(){Zc.point=Zc.lineEnd=b}}function br(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function _r(n,t){function e(n,t){o>0?-Ra+Ca>t&&(t=-Ra+Ca):t>Ra-Ca&&(t=Ra-Ca);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(qa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=K(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ra]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ga(u)u;u++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function zr(n,t){return n[0]-t[0]||n[1]-t[1]}function qr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Lr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],l=e[1],s=t[1]-c,f=r[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Tr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=el.pop()||new Rr;return t.site=n,t}function Pr(n){Xr(n),Qc.remove(n),el.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&ga(e-c.circle.x)s;++s)l=a[s],c=a[s-1],Kr(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=Jr(c.site,l.site,null,u),Vr(c),Vr(l)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Qc._;a;)if(r=Fr(a,o)-i,r>Ca)a=a.L;else{if(u=i-Hr(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Qc.insert(t,c),t||e){if(t===e)return Xr(t),e=Dr(t.site),Qc.insert(c,e),c.edge=e.edge=Jr(t.site,c.site),Vr(t),void Vr(e);if(!e)return void(c.edge=Jr(t.site,c.site));Xr(t),Xr(e);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=e.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,M=v*v+d*d,x={x:(d*y-g*M)/m+s,y:(h*M-v*y)/m+f};Kr(e.edge,l,p,x),c.edge=Jr(l,n,null,x),e.edge=Jr(n,p,null,x),Vr(t),Vr(e)}}function Fr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,l=c-t;if(!l)return a;var s=a-r,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+r:(r+a)/2}function Hr(n,t){var e=n.N;if(e)return Fr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Ir(n){for(var t,e,r,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Kc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),r=s.x,u=s.y,l=a[++o%c].start(),t=l.x,e=l.y,(ga(r-t)>Ca||ga(u-e)>Ca)&&(a.splice(o,0,new Qr(Gr(i.site,s,ga(r-f)Ca?{x:f,y:ga(t-f)Ca?{x:ga(e-p)Ca?{x:h,y:ga(t-h)Ca?{x:ga(e-g)=-za)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=rl.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,M=tl._;M;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};e={x:d,y:l}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/r,y:c};e={x:(l-u)/r,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xi||f>o||r>h||u>g)){if(p=n.point){var p,v=t-n.x,d=e-n.y,m=v*v+d*d;if(c>m){var y=Math.sqrt(c=m);r=t-y,u=e-y,i=t+y,o=e+y,a=p}}for(var M=n.nodes,x=.5*(s+h),b=.5*(f+g),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:l(n,s,f,x,b);break;case 1:l(n,x,f,h,b);break;case 2:l(n,s,b,x,g);break;case 3:l(n,x,b,h,g)}}}(n,r,u,i,o),a}function gu(n,t){n=ta.rgb(n),t=ta.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+xt(Math.round(e+i*n))+xt(Math.round(r+o*n))+xt(Math.round(u+a*n))}}function pu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=mu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function vu(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function du(n,t){var e,r,u,i=il.lastIndex=ol.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(e=il.exec(n))&&(r=ol.exec(t));)(u=r.index)>i&&(u=t.slice(i,u),a[o]?a[o]+=u:a[++o]=u),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,c.push({i:o,x:vu(e,r)})),i=ol.lastIndex;return ir;++r)a[(e=c[r]).i]=e.x(n);return a.join("")})}function mu(n,t){for(var e,r=ta.interpolators.length;--r>=0&&!(e=ta.interpolators[r](n,t)););return e}function yu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(mu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Mu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function bu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function wu(n){return n*n*n}function Su(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function ku(n){return function(t){return Math.pow(t,n)}}function Eu(n){return 1-Math.cos(n*Ra)}function Au(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Cu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/La*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*La/t)}}function zu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function qu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Lu(n,t){n=ta.hcl(n),t=ta.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return st(e+i*n,r+o*n,u+a*n)+""}}function Tu(n,t){n=ta.hsl(n),t=ta.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){n=ta.lab(n),t=ta.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ht(e+i*n,r+o*n,u+a*n)+""}}function Du(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Pu(n){var t=[n.a,n.b],e=[n.c,n.d],r=ju(t),u=Uu(t,e),i=ju(Fu(e,t,-u))||0;t[0]*e[1]180?s+=360:s-l>180&&(l+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:vu(l,s)})):s&&r.push(r.pop()+"rotate("+s+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:vu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:vu(g[0],p[0])},{i:e-2,x:vu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i=0;)e.push(u[r])}function Qu(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(i=n.children)&&(u=i.length))for(var u,i,o=-1;++oe;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function si(n){return n.reduce(fi,0)}function fi(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function pi(n){return[ta.min(n),ta.max(n)]}function vi(n,t){return n.value-t.value}function di(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function mi(n,t){n._pack_next=t,t._pack_prev=n}function yi(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Mi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(xi),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(u=e[1],u.x=u.r,u.y=0,t(u),l>2))for(i=e[2],wi(r,u,i),t(i),di(r,i),r._pack_prev=i,di(i,u),u=r._pack_next,o=3;l>o;o++){wi(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(yi(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!yi(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,M=Math.max(M,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=M,e.forEach(bi)}}function xi(n){n._pack_next=n._pack_prev=n}function bi(n){delete n._pack_next,delete n._pack_prev}function _i(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i=0;)t=u[i],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Ci(n,t,e){return n.a.parent===t.parent?n.a:e}function zi(n){return 1+ta.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Li(n){var t=n.children;return t&&t.length?Li(t[0]):n}function Ti(n){var t,e=n.children;return e&&(t=e.length)?Ti(e[t-1]):n}function Ri(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Di(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function Pi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Pi(n.range())}function ji(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Fi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Hi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ml}function Oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Oi:ji,c=r?Iu:Ou;return o=u(n,t,c,e),a=u(t,n,c,mu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Du)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Xi(n,t)},i.tickFormat=function(t,e){return $i(n,t,e)},i.nice=function(t){return Zi(n,t),u()},i.copy=function(){return Ii(n,t,e,r)},u()}function Yi(n,t){return ta.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Zi(n,t){return Fi(n,Hi(Vi(n,t)[2]))}function Vi(n,t){null==t&&(t=10);var e=Pi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Xi(n,t){return ta.range.apply(ta,Vi(n,t))}function $i(n,t,e){var r=Vi(n,t);if(e){var u=ic.exec(e);if(u.shift(),"s"===u[8]){var i=ta.formatPrefix(Math.max(ga(r[0]),ga(r[1])));return u[7]||(u[7]="."+Bi(i.scale(r[2]))),u[8]="f",e=ta.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Wi(u[8],r)),e=u.join("")}else e=",."+Bi(r[2])+"f";return ta.format(e)}function Bi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Wi(n,t){var e=Bi(t[2]);return n in yl?Math.abs(e-Bi(Math.max(ga(t[0]),ga(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ji(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Fi(r.map(u),e?Math:xl);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=Pi(r),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(e){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return Ml;arguments.length<2?t=Ml:"function"!=typeof t&&(t=ta.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ji(n.copy(),t,e,r)},Yi(o,n)}function Gi(n,t,e){function r(t){return n(u(t))}var u=Ki(t),i=Ki(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Xi(e,n)},r.tickFormat=function(n,t){return $i(e,n,t)},r.nice=function(n){return r.domain(Zi(e,n))},r.exponent=function(o){return arguments.length?(u=Ki(t=o),i=Ki(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Gi(n.copy(),t,e)},Yi(r,n)}function Ki(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Qi(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return ta.range(n.length).map(function(n){return t+e*n})}var u,i,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new l;for(var i,o=-1,a=r.length;++oe?[0/0,0/0]:[e>0?a[e-1]:n[0],et?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return to(n,t,e)},u()}function eo(n,t){function e(e){return e>=e?t[ta.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return eo(n,t)},e}function ro(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Xi(n,t)},t.tickFormat=function(t,e){return $i(n,t,e)},t.copy=function(){return ro(n)},t}function uo(){return 0}function io(n){return n.innerRadius}function oo(n){return n.outerRadius}function ao(n){return n.startAngle}function co(n){return n.endAngle}function lo(n){return n&&n.padAngle}function so(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function fo(n,t,e,r,u){var i=n[0]-t[0],o=n[1]-t[1],a=(u?r:-r)/Math.sqrt(i*i+o*o),c=a*o,l=-a*i,s=n[0]+c,f=n[1]+l,h=t[0]+c,g=t[1]+l,p=(s+h)/2,v=(f+g)/2,d=h-s,m=g-f,y=d*d+m*m,M=e-r,x=s*g-h*f,b=(0>m?-1:1)*Math.sqrt(M*M*y-x*x),_=(x*m-d*b)/y,w=(-x*d-m*b)/y,S=(x*m+d*b)/y,k=(-x*d+m*b)/y,E=_-p,A=w-v,N=S-p,C=k-v;return E*E+A*A>N*N+C*C&&(_=S,w=k),[[_-c,w-l],[_*e/M,w*e/M]]}function ho(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f1&&u.push("H",r[0]),u.join("")}function mo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?go(n):n[0]+_o(n,Lo(n))}function Ro(n){for(var t,e,r,u=-1,i=n.length;++ur)return s();var u=i[i.active];u&&(--i.count,delete i[i.active],u.event&&u.event.interrupt.call(n,n.__data__,u.index)),i.active=r,o.event&&o.event.start.call(n,n.__data__,t),o.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&v.push(r)}),h=o.ease,f=o.duration,ta.timer(function(){return p.c=l(e||1)?Ne:l,1},0,a)}function l(e){if(i.active!==r)return 1;for(var u=e/f,a=h(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,n.__data__,t),s()):void 0}function s(){return--i.count?delete i[r]:delete n[e],1}var f,h,g=o.delay,p=ec,v=[];return p.t=g+a,u>=g?c(u-g):void(p.c=c)},0,a)}}function Bo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function Wo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function Jo(n){return n.toISOString()}function Go(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=ta.bisect(Vl,u);return i==Vl.length?[t.year,Vi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Vl[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Ko(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Ko(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Pi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Ko(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Go(n.copy(),t,e)},Yi(r,n)}function Ko(n){return new Date(n)}function Qo(n){return JSON.parse(n.responseText)}function na(n){var t=ua.createRange();return t.selectNode(ua.body),t.createContextualFragment(n.responseText)}var ta={version:"3.5.5"},ea=[].slice,ra=function(n){return ea.call(n)},ua=this.document;if(ua)try{ra(ua.documentElement.childNodes)[0].nodeType}catch(ia){ra=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),ua)try{ua.createElement("DIV").style.setProperty("opacity",0,"")}catch(oa){var aa=this.Element.prototype,ca=aa.setAttribute,la=aa.setAttributeNS,sa=this.CSSStyleDeclaration.prototype,fa=sa.setProperty;aa.setAttribute=function(n,t){ca.call(this,n,t+"")},aa.setAttributeNS=function(n,t,e){la.call(this,n,t,e+"")},sa.setProperty=function(n,t,e){fa.call(this,n,t+"",e)}}ta.ascending=e,ta.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},ta.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ur&&(e=r)}else{for(;++u=r){e=r;break}for(;++ur&&(e=r)}return e},ta.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ue&&(e=r)}else{for(;++u=r){e=r;break}for(;++ue&&(e=r)}return e},ta.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},ta.sum=function(n,t){var e,r=0,i=n.length,o=-1;if(1===arguments.length)for(;++o1?c/(s-1):void 0},ta.deviation=function(){var n=ta.variance.apply(this,arguments);return n?Math.sqrt(n):n};var ha=i(e);ta.bisectLeft=ha.left,ta.bisect=ta.bisectRight=ha.right,ta.bisector=function(n){return i(1===n.length?function(t,r){return e(n(t),r)}:n)},ta.shuffle=function(n,t,e){(i=arguments.length)<3&&(e=n.length,2>i&&(t=0));for(var r,u,i=e-t;i;)u=Math.random()*i--|0,r=n[i+t],n[i+t]=n[u+t],n[u+t]=r;return n},ta.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ta.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},ta.zip=function(){if(!(r=arguments.length))return[];for(var n=-1,t=ta.min(arguments,o),e=new Array(t);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ga=Math.abs;ta.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,u=[],i=a(ga(e)),o=-1;if(n*=i,t*=i,e*=i,0>e)for(;(r=n+e*++o)>t;)u.push(r/i);else for(;(r=n+e*++o)=i.length)return r?r.call(u,o):e?o.sort(e):o;for(var c,s,f,h,g=-1,p=o.length,v=i[a++],d=new l;++g=i.length)return n;var r=[],u=o[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,u={},i=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(ta.map,e,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return o[i.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},ta.set=function(n){var t=new m;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},c(m,{has:h,add:function(n){return this._[s(n+="")]=!0,n},remove:g,values:p,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,f(t))}}),ta.behavior={},ta.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ta.event=null,ta.requote=function(n){return n.replace(ma,"\\$&")};var ma=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ya={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},Ma=function(n,t){return t.querySelector(n)},xa=function(n,t){return t.querySelectorAll(n)},ba=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(ba=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(Ma=function(n,t){return Sizzle(n,t)[0]||null},xa=Sizzle,ba=Sizzle.matchesSelector),ta.selection=function(){return ta.select(ua.documentElement)};var _a=ta.selection.prototype=[];_a.select=function(n){var t,e,r,u,i=[];n=N(n);for(var o=-1,a=this.length;++o=0&&(e=n.slice(0,t),n=n.slice(t+1)),wa.hasOwnProperty(e)?{space:wa[e],local:n}:n}},_a.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ta.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},_a.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,u=-1;if(t=e.classList){for(;++uu){if("string"!=typeof n){2>u&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>u){var i=this.node();return t(i).getComputedStyle(i,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},_a.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},_a.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},_a.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},_a.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},_a.insert=function(n,t){return n=j(n),t=N(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},_a.remove=function(){return this.each(F)},_a.data=function(n,t){function e(n,e){var r,u,i,o=n.length,f=e.length,h=Math.min(o,f),g=new Array(f),p=new Array(f),v=new Array(o);if(t){var d,m=new l,y=new Array(o);for(r=-1;++rr;++r)p[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,a.push(p),c.push(g),s.push(v)}var r,u,i=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++ii;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return A(u)},_a.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},_a.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},_a.size=function(){var n=0;return Y(this,function(){++n}),n};var Sa=[];ta.selection.enter=Z,ta.selection.enter.prototype=Sa,Sa.append=_a.append,Sa.empty=_a.empty,Sa.node=_a.node,Sa.call=_a.call,Sa.size=_a.size,Sa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var ka=ta.map({mouseenter:"mouseover",mouseleave:"mouseout"});ua&&ka.forEach(function(n){"on"+n in ua&&ka.remove(n)});var Ea,Aa=0;ta.mouse=function(n){return J(n,k())};var Na=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ta.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,u=0,i=t.length;i>u;++u)if((r=t[u]).identifier===e)return J(n,r)},ta.behavior.drag=function(){function n(){this.on("mousedown.drag",i).on("touchstart.drag",o)}function e(n,t,e,i,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],p|=n|e,M=r,g({type:"drag",x:r[0]+l[0],y:r[1]+l[1],dx:n,dy:e}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&ta.event.target===f),g({type:"dragend"}))}var l,s=this,f=ta.event.target,h=s.parentNode,g=r.of(s,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=ta.select(e(f)).on(i+d,a).on(o+d,c),y=W(f),M=t(h,v);u?(l=u.apply(s,arguments),l=[l.x-M[0],l.y-M[1]]):l=[0,0],g({type:"dragstart"})}}var r=E(n,"drag","dragstart","dragend"),u=null,i=e(b,ta.mouse,t,"mousemove","mouseup"),o=e(G,ta.touch,y,"touchmove","touchend");return n.origin=function(t){return arguments.length?(u=t,n):u},ta.rebind(n,r,"on")},ta.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?ra(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Ca=1e-6,za=Ca*Ca,qa=Math.PI,La=2*qa,Ta=La-Ca,Ra=qa/2,Da=qa/180,Pa=180/qa,Ua=Math.SQRT2,ja=2,Fa=4;ta.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=rt(v),o=i/(ja*h)*(e*ut(Ua*t+v)-et(v));return[r+o*l,u+o*s,i*e/rt(Ua*t+v)]}return[r+n*l,u+n*s,i*Math.exp(Ua*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],l=o-r,s=a-u,f=l*l+s*s,h=Math.sqrt(f),g=(c*c-i*i+Fa*f)/(2*i*ja*h),p=(c*c-i*i-Fa*f)/(2*c*ja*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ua;return e.duration=1e3*y,e},ta.behavior.zoom=function(){function n(n){n.on(q,f).on(Oa+".zoom",g).on("dblclick.zoom",p).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function u(n){k.k=Math.max(N[0],Math.min(N[1],n))}function i(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},u(Math.pow(2,o)),i(d=e,r),t=ta.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function c(n){z++||n({type:"zoomstart"})}function l(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function s(n){--z||n({type:"zoomend"}),d=null}function f(){function n(){f=1,i(ta.mouse(u),g),l(a)}function r(){h.on(L,null).on(T,null),p(f&&ta.event.target===o),s(a)}var u=this,o=ta.event.target,a=D.of(u,arguments),f=0,h=ta.select(t(u)).on(L,n).on(T,r),g=e(ta.mouse(u)),p=W(u);Dl.call(u),c(a)}function h(){function n(){var n=ta.touches(p);return g=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ta.event.target;ta.select(t).on(x,r).on(b,a),_.push(t);for(var e=ta.event.changedTouches,u=0,i=e.length;i>u;++u)d[e[u].identifier]=null;var c=n(),l=Date.now();if(1===c.length){if(500>l-M){var s=c[0];o(p,s,d[s.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=l}else if(c.length>1){var s=c[0],f=c[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function r(){var n,t,e,r,o=ta.touches(p);Dl.call(p);for(var a=0,c=o.length;c>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var s=(s=e[0]-n[0])*s+(s=e[1]-n[1])*s,f=m&&Math.sqrt(s/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],u(f*g)}M=null,i(n,t),l(v)}function a(){if(ta.event.touches.length){for(var t=ta.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}ta.selectAll(_).on(y,null),w.on(q,f).on(R,h),E(),s(v)}var g,p=this,v=D.of(p,arguments),d={},m=0,y=".zoom-"+ta.event.changedTouches[0].identifier,x="touchmove"+y,b="touchend"+y,_=[],w=ta.select(p),E=W(p);t(),c(v),w.on(q,null).on(R,t)}function g(){var n=D.of(this,arguments);y?clearTimeout(y):(v=e(d=m||ta.mouse(this)),Dl.call(this),c(n)),y=setTimeout(function(){y=null,s(n)},50),S(),u(Math.pow(2,.002*Ha())*k.k),i(d,v),l(n)}function p(){var n=ta.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ta.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,m,y,M,x,b,_,w,k={x:0,y:0,k:1},A=[960,500],N=Ia,C=250,z=0,q="mousedown.zoom",L="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=E(n,"zoomstart","zoom","zoomend");return Oa||(Oa="onwheel"in ua?(Ha=function(){return-ta.event.deltaY*(ta.event.deltaMode?120:1)},"wheel"):"onmousewheel"in ua?(Ha=function(){return ta.event.wheelDelta},"mousewheel"):(Ha=function(){return-ta.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Tl?ta.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},c(n)}).tween("zoom:zoom",function(){var e=A[0],r=A[1],u=d?d[0]:e/2,i=d?d[1]:r/2,o=ta.interpolateZoom([(u-k.x)/k.k,(i-k.y)/k.k,e/k.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:u-r[0]*a,y:i-r[1]*a,k:a},l(n)}}).each("interrupt.zoom",function(){s(n)}).each("end.zoom",function(){s(n)}):(this.__chart__=k,c(n),l(n),s(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:+t},a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(N=null==t?Ia:[+t[0],+t[1]],n):N},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(A=t&&[+t[0],+t[1]],n):A},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ta.rebind(n,D,"on")};var Ha,Oa,Ia=[0,1/0];ta.color=ot,ot.prototype.toString=function(){return this.rgb()+""},ta.hsl=at;var Ya=at.prototype=new ot;Ya.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,this.l/n)},Ya.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,n*this.l)},Ya.rgb=function(){return ct(this.h,this.s,this.l)},ta.hcl=lt;var Za=lt.prototype=new ot;Za.brighter=function(n){return new lt(this.h,this.c,Math.min(100,this.l+Va*(arguments.length?n:1)))},Za.darker=function(n){return new lt(this.h,this.c,Math.max(0,this.l-Va*(arguments.length?n:1)))},Za.rgb=function(){return st(this.h,this.c,this.l).rgb()},ta.lab=ft;var Va=18,Xa=.95047,$a=1,Ba=1.08883,Wa=ft.prototype=new ot;Wa.brighter=function(n){return new ft(Math.min(100,this.l+Va*(arguments.length?n:1)),this.a,this.b)},Wa.darker=function(n){return new ft(Math.max(0,this.l-Va*(arguments.length?n:1)),this.a,this.b)},Wa.rgb=function(){return ht(this.l,this.a,this.b)},ta.rgb=mt;var Ja=mt.prototype=new ot;Ja.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),new mt(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mt(u,u,u)},Ja.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mt(n*this.r,n*this.g,n*this.b)},Ja.hsl=function(){return _t(this.r,this.g,this.b)},Ja.toString=function(){return"#"+xt(this.r)+xt(this.g)+xt(this.b)};var Ga=ta.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Ga.forEach(function(n,t){Ga.set(n,yt(t))}),ta.functor=Et,ta.xhr=At(y),ta.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=Nt(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(s>=l)return o;if(u)return u=!1,i;var t=s;if(34===n.charCodeAt(t)){for(var e=t;e++s;){var r=n.charCodeAt(s++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(s)&&(++s,++a);else if(r!==c)continue;return n.slice(t,s-a)}return n.slice(t)}for(var r,u,i={},o={},a=[],l=n.length,s=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,f++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new m,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},ta.csv=ta.dsv(",","text/csv"),ta.tsv=ta.dsv(" ","text/tab-separated-values");var Ka,Qa,nc,tc,ec,rc=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ta.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Qa?Qa.n=i:Ka=i,Qa=i,nc||(tc=clearTimeout(tc),nc=1,rc(qt))},ta.timer.flush=function(){Lt(),Tt()},ta.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var uc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);ta.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=ta.round(n,Rt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),uc[8+e/3]};var ic=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,oc=ta.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ta.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),ac=ta.time={},cc=Date;jt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){lc.setUTCDate.apply(this._,arguments)},setDay:function(){lc.setUTCDay.apply(this._,arguments)},setFullYear:function(){lc.setUTCFullYear.apply(this._,arguments)},setHours:function(){lc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){lc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){lc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){lc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){lc.setUTCSeconds.apply(this._,arguments)},setTime:function(){lc.setTime.apply(this._,arguments)}};var lc=Date.prototype;ac.year=Ft(function(n){return n=ac.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ac.years=ac.year.range,ac.years.utc=ac.year.utc.range,ac.day=Ft(function(n){var t=new cc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ac.days=ac.day.range,ac.days.utc=ac.day.utc.range,ac.dayOfYear=function(n){var t=ac.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ac[n]=Ft(function(n){return(n=ac.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ac[n+"s"]=e.range,ac[n+"s"].utc=e.utc.range,ac[n+"OfYear"]=function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)}}),ac.week=ac.sunday,ac.weeks=ac.sunday.range,ac.weeks.utc=ac.sunday.utc.range,ac.weekOfYear=ac.sundayOfYear;var sc={"-":"",_:" ",0:"0"},fc=/^\s*\d+/,hc=/^%/;ta.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var gc=ta.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ta.format=gc.numberFormat,ta.geo={},ce.prototype={s:0,t:0,add:function(n){le(n,this.t,pc),le(pc.s,this.s,this),this.s?this.t+=pc.t:this.s=pc.t +},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var pc=new ce;ta.geo.stream=function(n,t){n&&vc.hasOwnProperty(n.type)?vc[n.type](n,t):se(n,t)};var vc={Feature:function(n,t){se(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*qa+n:n,Mc.lineStart=Mc.lineEnd=Mc.point=b}};ta.geo.bounds=function(){function n(n,t){M.push(x=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=pe([t*Da,e*Da]);if(m){var u=de(m,r),i=[u[1],-u[0],0],o=de(i,u);Me(o),o=xe(o);var c=t-p,l=c>0?1:-1,v=o[0]*Pa*l,d=ga(c)>180;if(d^(v>l*p&&l*t>v)){var y=o[1]*Pa;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>l*p&&l*t>v)){var y=-o[1]*Pa;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t)}else n(t,e);m=r,p=t}function e(){b.point=t}function r(){x[0]=s,x[1]=h,b.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=ga(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Mc.point(n,e),t(n,e)}function i(){Mc.lineStart()}function o(){u(v,d),Mc.lineEnd(),ga(y)>Ca&&(s=-(h=180)),x[0]=s,x[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nyc?(s=-(h=180),f=-(g=90)):y>Ca?g=90:-Ca>y&&(f=-90),x[0]=s,x[1]=h}};return function(n){g=h=-(s=f=1/0),M=[],ta.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,u=M[0],i=[u];t>r;++r)e=M[r],l(e[0],u)||l(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,s=e[0],h=u[1])}return M=x=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),ta.geo.centroid=function(n){xc=bc=_c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,qc);var t=Nc,e=Cc,r=zc,u=t*t+e*e+r*r;return za>u&&(t=kc,e=Ec,r=Ac,Ca>bc&&(t=_c,e=wc,r=Sc),u=t*t+e*e+r*r,za>u)?[0/0,0/0]:[Math.atan2(e,t)*Pa,tt(r/Math.sqrt(u))*Pa]};var xc,bc,_c,wc,Sc,kc,Ec,Ac,Nc,Cc,zc,qc={sphere:b,point:_e,lineStart:Se,lineEnd:ke,polygonStart:function(){qc.lineStart=Ee},polygonEnd:function(){qc.lineStart=Se}},Lc=Le(Ne,Pe,je,[-qa,-qa/2]),Tc=1e9;ta.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Ie(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ta.geo.conicEqualArea=function(){return Ye(Ze)}).raw=Ze,ta.geo.albers=function(){return ta.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ta.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=ta.geo.albers(),o=ta.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ta.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var l=i.scale(),s=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,r=o.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Ca,f+.12*l+Ca],[s-.214*l-Ca,f+.234*l-Ca]]).stream(c).point,u=a.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Ca,f+.166*l+Ca],[s-.115*l-Ca,f+.234*l-Ca]]).stream(c).point,n},n.scale(1070)};var Rc,Dc,Pc,Uc,jc,Fc,Hc={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Dc=0,Hc.lineStart=Ve},polygonEnd:function(){Hc.lineStart=Hc.lineEnd=Hc.point=b,Rc+=ga(Dc/2)}},Oc={point:Xe,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Ic={point:We,lineStart:Je,lineEnd:Ge,polygonStart:function(){Ic.lineStart=Ke},polygonEnd:function(){Ic.point=We,Ic.lineStart=Je,Ic.lineEnd=Ge}};ta.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),ta.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Rc=0,ta.geo.stream(n,u(Hc)),Rc},n.centroid=function(n){return _c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,u(Ic)),zc?[Nc/zc,Cc/zc]:Ac?[kc/Ac,Ec/Ac]:Sc?[_c/Sc,wc/Sc]:[0/0,0/0]},n.bounds=function(n){return jc=Fc=-(Pc=Uc=1/0),ta.geo.stream(n,u(Oc)),[[Pc,Uc],[jc,Fc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||tr(n):y,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new $e:new Qe(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(ta.geo.albersUsa()).context(null)},ta.geo.transform=function(n){return{stream:function(t){var e=new er(t);for(var r in n)e[r]=n[r];return e}}},er.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ta.geo.projection=ur,ta.geo.projectionMutator=ir,(ta.geo.equirectangular=function(){return ur(ar)}).raw=ar.invert=ar,ta.geo.rotation=function(n){function t(t){return t=n(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t}return n=lr(n[0]%360*Da,n[1]*Da,n.length>2?n[2]*Da:0),t.invert=function(t){return t=n.invert(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t},t},cr.invert=ar,ta.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=lr(-n[0]*Da,-n[1]*Da,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Pa,n[1]*=Pa}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=gr((t=+r)*Da,u*Da),n):t},n.precision=function(r){return arguments.length?(e=gr(t*Da,(u=+r)*Da),n):u},n.angle(90)},ta.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Da,u=n[1]*Da,i=t[1]*Da,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),l=Math.cos(u),s=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=l*s-c*f*a)*e),c*s+l*f*a)},ta.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ta.range(Math.ceil(i/d)*d,u,d).map(h).concat(ta.range(Math.ceil(l/m)*m,c,m).map(g)).concat(ta.range(Math.ceil(r/p)*p,e,p).filter(function(n){return ga(n%d)>Ca}).map(s)).concat(ta.range(Math.ceil(a/v)*v,o,v).filter(function(n){return ga(n%m)>Ca}).map(f))}var e,r,u,i,o,a,c,l,s,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],l=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[i,l],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,s=vr(a,o,90),f=dr(r,e,y),h=vr(l,c,90),g=dr(i,u,y),n):y},n.majorExtent([[-180,-90+Ca],[180,90-Ca]]).minorExtent([[-180,-80-Ca],[180,80+Ca]])},ta.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=mr,u=yr;return n.distance=function(){return ta.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},ta.geo.interpolate=function(n,t){return Mr(n[0]*Da,n[1]*Da,t[0]*Da,t[1]*Da)},ta.geo.length=function(n){return Yc=0,ta.geo.stream(n,Zc),Yc};var Yc,Zc={sphere:b,point:b,lineStart:xr,lineEnd:b,polygonStart:b,polygonEnd:b},Vc=br(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ta.geo.azimuthalEqualArea=function(){return ur(Vc)}).raw=Vc;var Xc=br(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},y);(ta.geo.azimuthalEquidistant=function(){return ur(Xc)}).raw=Xc,(ta.geo.conicConformal=function(){return Ye(_r)}).raw=_r,(ta.geo.conicEquidistant=function(){return Ye(wr)}).raw=wr;var $c=br(function(n){return 1/n},Math.atan);(ta.geo.gnomonic=function(){return ur($c)}).raw=$c,Sr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ra]},(ta.geo.mercator=function(){return kr(Sr)}).raw=Sr;var Bc=br(function(){return 1},Math.asin);(ta.geo.orthographic=function(){return ur(Bc)}).raw=Bc;var Wc=br(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ta.geo.stereographic=function(){return ur(Wc)}).raw=Wc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ra]},(ta.geo.transverseMercator=function(){var n=kr(Er),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Er,ta.geom={},ta.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(e),i=Et(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(zr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=Cr(a),s=Cr(c),f=s[0]===l[0],h=s[s.length-1]===l[l.length-1],g=[];for(t=l.length-1;t>=0;--t)g.push(n[a[l[t]][2]]);for(t=+f;t=r&&l.x<=i&&l.y>=u&&l.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];s.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ca)*Ca,y:Math.round(o(n,t)/Ca)*Ca,i:t}})}var r=Ar,u=Nr,i=r,o=u,a=ul;return n?t(n):(t.links=function(n){return iu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return iu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(Yr),c=-1,l=a.length,s=a[l-1].edge,f=s.l===o?s.r:s.l;++c=l,h=r>=s,g=h<<1|f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=su()),f?u=l:a=l,h?o=s:c=s,i(n,t,e,r,u,o,a,c)}var s,f,h,g,p,v,d,m,y,M=Et(a),x=Et(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)s=n[g],s.xm&&(m=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var b=+M(s=n[g],g),_=+x(s,g);v>b&&(v=b),d>_&&(d=_),b>m&&(m=b),_>y&&(y=_),f.push(b),h.push(_)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=su();if(k.add=function(n){i(k,n,+M(n,++g),+x(n,g),v,d,m,y)},k.visit=function(n){fu(n,k,v,d,m,y)},k.find=function(n){return hu(k,n[0],n[1],v,d,m,y)},g=-1,null==t){for(;++g=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=cl.get(e)||al,r=ll.get(r)||y,Mu(r(e.apply(null,ea.call(arguments,1))))},ta.interpolateHcl=Lu,ta.interpolateHsl=Tu,ta.interpolateLab=Ru,ta.interpolateRound=Du,ta.transform=function(n){var t=ua.createElementNS(ta.ns.prefix.svg,"g");return(ta.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Pu(e?e.matrix:sl)})(n)},Pu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var sl={a:1,b:0,c:0,d:1,e:0,f:0};ta.interpolateTransform=Hu,ta.layout={},ta.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/d){if(p>c){var l=t.charge/c;n.px-=i*l,n.py-=o*l}return!0}if(t.point&&c&&p>c){var l=t.pointCharge/c;n.px-=i*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=ta.event.x,n.py=ta.event.y,a.resume()}var e,r,u,i,o,a={},c=ta.dispatch("start","tick","end"),l=[1,1],s=.9,f=fl,h=hl,g=-30,p=gl,v=.1,d=.64,m=[],M=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,y,x,b=m.length,_=M.length;for(e=0;_>e;++e)a=M[e],f=a.source,h=a.target,y=h.x-f.x,x=h.y-f.y,(p=y*y+x*x)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,y*=p,x*=p,h.x-=y*(d=f.weight/(h.weight+f.weight)),h.y-=x*d,f.x+=y*(d=1-d),f.y+=x*d);if((d=r*v)&&(y=l[0]/2,x=l[1]/2,e=-1,d))for(;++e0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),ta.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=M[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,l=o.length;++at;++t)(r=m[t]).index=t,r.weight=0;for(t=0;s>t;++t)r=M[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;s>t;++t)u[t]=+f.call(this,M[t],t);else for(t=0;s>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;s>t;++t)i[t]=+h.call(this,M[t],t);else for(t=0;s>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=ta.behavior.drag().origin(y).on("dragstart.force",Xu).on("drag.force",t).on("dragend.force",$u)),arguments.length?void this.on("mouseover.force",Bu).on("mouseout.force",Wu).call(e):e},ta.rebind(a,c,"on")};var fl=20,hl=1,gl=1/0;ta.layout.hierarchy=function(){function n(u){var i,o=[u],a=[];for(u.depth=0;null!=(i=o.pop());)if(a.push(i),(l=e.call(n,i,i.depth))&&(c=l.length)){for(var c,l,s;--c>=0;)o.push(s=l[c]),s.parent=i,s.depth=i.depth+1;r&&(i.value=0),i.children=l}else r&&(i.value=+r.call(n,i,i.depth)||0),delete i.children;return Qu(u,function(n){var e,u;t&&(e=n.children)&&e.sort(t),r&&(u=n.parent)&&(u.value+=n.value)}),a}var t=ei,e=ni,r=ti;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(Ku(t,function(n){n.children&&(n.value=0)}),Qu(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ta.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,l=-1;for(r=t.value?r/t.value:0;++lf?-1:1),p=(f-c*g)/ta.sum(l),v=ta.range(c),d=[];return null!=e&&v.sort(e===pl?function(n,t){return l[t]-l[n]}:function(n,t){return e(o[n],o[t])}),v.forEach(function(n){d[n]={data:o[n],value:a=l[n],startAngle:s,endAngle:s+=a*p+g,padAngle:h}}),d}var t=Number,e=pl,r=0,u=La,i=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n.padAngle=function(t){return arguments.length?(i=t,n):i},n};var pl={};ta.layout.stack=function(){function n(a,c){if(!(h=a.length))return a;var l=a.map(function(e,r){return t.call(n,e,r)}),s=l.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,s,c);l=ta.permute(l,f),s=ta.permute(s,f);var h,g,p,v,d=r.call(n,s,c),m=l[0].length;for(p=0;m>p;++p)for(u.call(n,l[0][p],v=d[p],s[0][p][1]),g=1;h>g;++g)u.call(n,l[g][p],v+=s[g-1][p][1],s[g][p][1]);return a}var t=y,e=ai,r=ci,u=oi,i=ui,o=ii;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:vl.get(t)||ai,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:dl.get(t)||ci,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var vl=ta.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(li),i=n.map(si),o=ta.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,l=[],s=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],l.push(e)):(c+=i[e],s.push(e));return s.reverse().concat(l)},reverse:function(n){return ta.range(n.length).reverse()},"default":ai}),dl=ta.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,u=0;s>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];s>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ci});ta.layout.histogram=function(){function n(n,i){for(var o,a,c=[],l=n.map(e,this),s=r.call(this,l,i),f=u.call(this,s,l,i),i=-1,h=l.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=s[0]&&a<=s[1]&&(o=c[ta.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=pi,u=hi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=Et(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return gi(n,t)}:Et(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ta.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],l=u[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Qu(a,function(n){n.r=+s(n.value)}),Qu(a,Mi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;Qu(a,function(n){n.r+=f}),Qu(a,Mi),Qu(a,function(n){n.r-=f})}return _i(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,e=ta.layout.hierarchy().sort(vi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Gu(n,e)},ta.layout.tree=function(){function n(n,u){var s=o.call(this,n,u),f=s[0],h=t(f);if(Qu(h,e),h.parent.m=-h.z,Ku(h,r),l)Ku(f,i);else{var g=f,p=f,v=f;Ku(f,function(n){n.xp.x&&(p=n),n.depth>v.depth&&(v=n)});var d=a(g,p)/2-g.x,m=c[0]/(p.x+a(p,g)/2+d),y=c[1]/(v.depth||1);Ku(f,function(n){n.x=(n.x+d)*m,n.y=n.depth*y})}return s}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var u,i=t.children,o=0,a=i.length;a>o;++o)r.push((i[o]=u={_:i[o],parent:t,children:(u=i[o].children)&&u.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=u);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Ni(n);var i=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-i):n.z=i}else r&&(n.z=r.z+a(n._,r._));n.parent.A=u(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function u(n,t,e){if(t){for(var r,u=n,i=n,o=t,c=u.parent.children[0],l=u.m,s=i.m,f=o.m,h=c.m;o=Ei(o),u=ki(u),o&&u;)c=ki(c),i=Ei(i),i.a=n,r=o.z+f-u.z-l+a(o._,u._),r>0&&(Ai(Ci(o,n,e),n,r),l+=r,s+=r),f+=o.m,l+=u.m,h+=c.m,s+=i.m;o&&!Ei(i)&&(i.t=o,i.m+=f-s),u&&!ki(c)&&(c.t=u,c.m+=l-h,e=n)}return e}function i(n){n.x*=c[0],n.y=n.depth*c[1]}var o=ta.layout.hierarchy().sort(null).value(null),a=Si,c=[1,1],l=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(l=null==(c=t)?i:null,n):l?null:c},n.nodeSize=function(t){return arguments.length?(l=null==(c=t)?null:i,n):l?c:null},Gu(n,o)},ta.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],l=0;Qu(c,function(n){var t=n.children;t&&t.length?(n.x=qi(t),n.y=zi(t)):(n.x=o?l+=e(n,o):0,n.y=0,o=n)});var s=Li(c),f=Ti(c),h=s.x-e(s,f)/2,g=f.x+e(f,s)/2;return Qu(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=ta.layout.hierarchy().sort(null).value(null),e=Si,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Gu(n,t)},ta.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++ut?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,l=f(e),s=[],h=i.slice(),p=1/0,v="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),s.area=0;(c=h.length)>0;)s.push(o=h[c-1]),s.area+=o.area,"squarify"!==g||(a=r(s,v))<=p?(h.pop(),p=a):(s.area-=s.pop().area,u(s,v,l,!1),v=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(u(s,v,l,!0),s.length=s.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++oe&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,l=e.y,s=t?c(n.area/t):0;if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++ie.dx)&&(s=e.dx);++ie&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=ta.random.normal.apply(ta,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ta.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ta.scale={};var ml={floor:y,ceil:y};ta.scale.linear=function(){return Ii([0,1],[0,1],mu,!1)};var yl={s:1,g:1,p:1,r:1,e:1};ta.scale.log=function(){return Ji(ta.scale.linear().domain([0,1]),10,!0,[1,10])};var Ml=ta.format(".0e"),xl={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ta.scale.pow=function(){return Gi(ta.scale.linear(),1,[0,1])},ta.scale.sqrt=function(){return ta.scale.pow().exponent(.5)},ta.scale.ordinal=function(){return Qi([],{t:"range",a:[[]]})},ta.scale.category10=function(){return ta.scale.ordinal().range(bl)},ta.scale.category20=function(){return ta.scale.ordinal().range(_l)},ta.scale.category20b=function(){return ta.scale.ordinal().range(wl)},ta.scale.category20c=function(){return ta.scale.ordinal().range(Sl)};var bl=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(Mt),_l=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(Mt),wl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(Mt),Sl=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(Mt);ta.scale.quantile=function(){return no([],[])},ta.scale.quantize=function(){return to(0,1,[0,1])},ta.scale.threshold=function(){return eo([.5],[0,1])},ta.scale.identity=function(){return ro([0,1])},ta.svg={},ta.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),l=Math.max(0,+r.apply(this,arguments)),s=o.apply(this,arguments)-Ra,f=a.apply(this,arguments)-Ra,h=Math.abs(f-s),g=s>f?0:1;if(n>l&&(p=l,l=n,n=p),h>=Ta)return t(l,g)+(n?t(n,1-g):"")+"Z";var p,v,d,m,y,M,x,b,_,w,S,k,E=0,A=0,N=[];if((m=(+c.apply(this,arguments)||0)/2)&&(d=i===kl?Math.sqrt(n*n+l*l):+i.apply(this,arguments),g||(A*=-1),l&&(A=tt(d/l*Math.sin(m))),n&&(E=tt(d/n*Math.sin(m)))),l){y=l*Math.cos(s+A),M=l*Math.sin(s+A),x=l*Math.cos(f-A),b=l*Math.sin(f-A);var C=Math.abs(f-s-2*A)<=qa?0:1;if(A&&so(y,M,x,b)===g^C){var z=(s+f)/2;y=l*Math.cos(z),M=l*Math.sin(z),x=b=null}}else y=M=0;if(n){_=n*Math.cos(f-E),w=n*Math.sin(f-E),S=n*Math.cos(s+E),k=n*Math.sin(s+E);var q=Math.abs(s-f+2*E)<=qa?0:1;if(E&&so(_,w,S,k)===1-g^q){var L=(s+f)/2;_=n*Math.cos(L),w=n*Math.sin(L),S=k=null}}else _=w=0;if((p=Math.min(Math.abs(l-n)/2,+u.apply(this,arguments)))>.001){v=l>n^g?0:1;var T=null==S?[_,w]:null==x?[y,M]:Lr([y,M],[S,k],[x,b],[_,w]),R=y-T[0],D=M-T[1],P=x-T[0],U=b-T[1],j=1/Math.sin(Math.acos((R*P+D*U)/(Math.sqrt(R*R+D*D)*Math.sqrt(P*P+U*U)))/2),F=Math.sqrt(T[0]*T[0]+T[1]*T[1]);if(null!=x){var H=Math.min(p,(l-F)/(j+1)),O=fo(null==S?[_,w]:[S,k],[y,M],l,H,g),I=fo([x,b],[_,w],l,H,g);p===H?N.push("M",O[0],"A",H,",",H," 0 0,",v," ",O[1],"A",l,",",l," 0 ",1-g^so(O[1][0],O[1][1],I[1][0],I[1][1]),",",g," ",I[1],"A",H,",",H," 0 0,",v," ",I[0]):N.push("M",O[0],"A",H,",",H," 0 1,",v," ",I[0])}else N.push("M",y,",",M);if(null!=S){var Y=Math.min(p,(n-F)/(j-1)),Z=fo([y,M],[S,k],n,-Y,g),V=fo([_,w],null==x?[y,M]:[x,b],n,-Y,g);p===Y?N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",V[1],"A",n,",",n," 0 ",g^so(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-g," ",Z[1],"A",Y,",",Y," 0 0,",v," ",Z[0]):N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",Z[0])}else N.push("L",_,",",w)}else N.push("M",y,",",M),null!=x&&N.push("A",l,",",l," 0 ",C,",",g," ",x,",",b),N.push("L",_,",",w),null!=S&&N.push("A",n,",",n," 0 ",q,",",1-g," ",S,",",k);return N.push("Z"),N.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=io,r=oo,u=uo,i=kl,o=ao,a=co,c=lo;return n.innerRadius=function(t){return arguments.length?(e=Et(t),n):e},n.outerRadius=function(t){return arguments.length?(r=Et(t),n):r},n.cornerRadius=function(t){return arguments.length?(u=Et(t),n):u},n.padRadius=function(t){return arguments.length?(i=t==kl?kl:Et(t),n):i},n.startAngle=function(t){return arguments.length?(o=Et(t),n):o},n.endAngle=function(t){return arguments.length?(a=Et(t),n):a},n.padAngle=function(t){return arguments.length?(c=Et(t),n):c},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Ra;return[Math.cos(t)*n,Math.sin(t)*n]},n};var kl="auto";ta.svg.line=function(){return ho(y)};var El=ta.map({linear:go,"linear-closed":po,step:vo,"step-before":mo,"step-after":yo,basis:So,"basis-open":ko,"basis-closed":Eo,bundle:Ao,cardinal:bo,"cardinal-open":Mo,"cardinal-closed":xo,monotone:To});El.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Al=[0,2/3,1/3,0],Nl=[0,1/3,2/3,0],Cl=[0,1/6,2/3,1/6];ta.svg.line.radial=function(){var n=ho(Ro);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},mo.reverse=yo,yo.reverse=mo,ta.svg.area=function(){return Do(y)},ta.svg.area.radial=function(){var n=Do(Ro);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ta.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),l=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+u(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)-Ra,s=l.call(n,u,r)-Ra;return{r:i,a0:o,a1:s,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(s),i*Math.sin(s)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>qa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=mr,o=yr,a=Po,c=ao,l=co;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(l=Et(t),n):l},n},ta.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=mr,e=yr,r=Uo;return n.source=function(e){return arguments.length?(t=Et(e),n):t},n.target=function(t){return arguments.length?(e=Et(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ta.svg.diagonal.radial=function(){var n=ta.svg.diagonal(),t=Uo,e=n.projection;return n.projection=function(n){return arguments.length?e(jo(t=n)):t},n},ta.svg.symbol=function(){function n(n,r){return(zl.get(t.call(this,n,r))||Oo)(e.call(this,n,r))}var t=Ho,e=Fo;return n.type=function(e){return arguments.length?(t=Et(e),n):t},n.size=function(t){return arguments.length?(e=Et(t),n):e},n};var zl=ta.map({circle:Oo,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Ll)),e=t*Ll;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ta.svg.symbolTypes=zl.keys();var ql=Math.sqrt(3),Ll=Math.tan(30*Da);_a.transition=function(n){for(var t,e,r=Tl||++Ul,u=Xo(n),i=[],o=Rl||{time:Date.now(),ease:Su,delay:0,duration:250},a=-1,c=this.length;++ai;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Yo(u,this.namespace,this.id)},Pl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(u){u[r][e].tween.set(n,t)})},Pl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Hu:mu,a=ta.ns.qualify(n);return Zo(this,"attr."+n,t,a.local?i:u)},Pl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=ta.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Pl.style=function(n,e,r){function u(){this.style.removeProperty(n)}function i(e){return null==e?u:(e+="",function(){var u,i=t(this).getComputedStyle(this,null).getPropertyValue(n);return i!==e&&(u=mu(i,e),function(t){this.style.setProperty(n,u(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Zo(this,"style."+n,e,i)},Pl.styleTween=function(n,e,r){function u(u,i){var o=e.call(this,u,i,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,u)},Pl.text=function(n){return Zo(this,"text",n,Vo)},Pl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Pl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ta.ease.apply(ta,arguments)),Y(this,function(r){r[e][t].ease=n}))},Pl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,u,i){r[e][t].delay=+n.call(r,r.__data__,u,i)}:(n=+n,function(r){r[e][t].delay=n}))},Pl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,u,i){r[e][t].duration=Math.max(1,n.call(r,r.__data__,u,i))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Pl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var u=Rl,i=Tl;try{Tl=e,Y(this,function(t,u,i){Rl=t[r][e],n.call(t,t.__data__,u,i)})}finally{Rl=u,Tl=i}}else Y(this,function(u){var i=u[r][e];(i.event||(i.event=ta.dispatch("start","end","interrupt"))).on(n,t)});return this},Pl.transition=function(){for(var n,t,e,r,u=this.id,i=++Ul,o=this.namespace,a=[],c=0,l=this.length;l>c;c++){a.push(n=[]);for(var t=this[c],s=0,f=t.length;f>s;s++)(e=t[s])&&(r=e[o][u],$o(e,s,o,i,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Yo(a,o,i)},ta.svg.axis=function(){function n(n){n.each(function(){var n,l=ta.select(this),s=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):y:t,p=l.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ca),d=ta.transition(p.exit()).style("opacity",Ca).remove(),m=ta.transition(p.order()).style("opacity",1),M=Math.max(u,0)+o,x=Ui(f),b=l.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ta.transition(b));v.append("line"),v.append("text");var w,S,k,E,A=v.select("line"),N=m.select("line"),C=p.select("text").text(g),z=v.select("text"),q=m.select("text"),L="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=Bo,w="x",k="y",S="x2",E="y2",C.attr("dy",0>L?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+L*i+"V0H"+x[1]+"V"+L*i)):(n=Wo,w="y",k="x",S="y2",E="x2",C.attr("dy",".32em").style("text-anchor",0>L?"end":"start"),_.attr("d","M"+L*i+","+x[0]+"H0V"+x[1]+"H"+L*i)),A.attr(E,L*u),z.attr(k,L*M),N.attr(S,0).attr(E,L*u),q.attr(w,0).attr(k,L*M),f.rangeBand){var T=f,R=T.rangeBand()/2;s=f=function(n){return T(n)+R}}else s.rangeBand?s=f:d.call(n,f,s);v.call(n,s,f),m.call(n,f,f)})}var t,e=ta.scale.linear(),r=jl,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Fl?t+"":jl,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var jl="bottom",Fl={top:1,right:1,bottom:1,left:1};ta.svg.brush=function(){function n(t){t.each(function(){var t=ta.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",i).on("touchstart.brush",i),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,y);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Hl[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var c,f=ta.transition(t),h=ta.transition(o);l&&(c=Ui(l),h.attr("x",c[0]).attr("width",c[1]-c[0]),r(f)),s&&(c=Ui(s),h.attr("y",c[0]).attr("height",c[1]-c[0]),u(f)),e(f)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+f[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",f[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1]-f[0])}function u(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function i(){function i(){32==ta.event.keyCode&&(C||(M=null,q[0]-=f[1],q[1]-=h[1],C=2),S())}function v(){32==ta.event.keyCode&&2==C&&(q[0]+=f[1],q[1]+=h[1],C=0,S())}function d(){var n=ta.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ta.event.altKey?(M||(M=[(f[0]+f[1])/2,(h[0]+h[1])/2]),q[0]=f[+(n[0]s?(u=r,r=s):u=s),v[0]!=r||v[1]!=u?(e?a=null:o=null,v[0]=r,v[1]=u,!0):void 0}function y(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ta.select("body").style("cursor",null),L.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ta.select(ta.event.target),w=c.of(b,arguments),k=ta.select(b),E=_.datum(),A=!/^(n|s)$/.test(E)&&l,N=!/^(e|w)$/.test(E)&&s,C=_.classed("extent"),z=W(b),q=ta.mouse(b),L=ta.select(t(b)).on("keydown.brush",i).on("keyup.brush",v);if(ta.event.changedTouches?L.on("touchmove.brush",d).on("touchend.brush",y):L.on("mousemove.brush",d).on("mouseup.brush",y),k.interrupt().selectAll("*").interrupt(),C)q[0]=f[0]-q[0],q[1]=h[0]-q[1];else if(E){var T=+/w$/.test(E),R=+/^n/.test(E);x=[f[1-T]-q[0],h[1-R]-q[1]],q[0]=f[T],q[1]=h[R]}else ta.event.altKey&&(M=q.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ta.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,c=E(n,"brushstart","brush","brushend"),l=null,s=null,f=[0,0],h=[0,0],g=!0,p=!0,v=Ol[0];return n.event=function(n){n.each(function(){var n=c.of(this,arguments),t={x:f,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Tl?ta.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,f=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=yu(f,t.x),r=yu(h,t.y);return o=a=null,function(u){f=t.x=e(u),h=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(l=t,v=Ol[!l<<1|!s],n):l},n.y=function(t){return arguments.length?(s=t,v=Ol[!l<<1|!s],n):s},n.clamp=function(t){return arguments.length?(l&&s?(g=!!t[0],p=!!t[1]):l?g=!!t:s&&(p=!!t),n):l&&s?[g,p]:l?g:s?p:null},n.extent=function(t){var e,r,u,i,c;return arguments.length?(l&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),o=[e,r],l.invert&&(e=l(e),r=l(r)),e>r&&(c=e,e=r,r=c),(e!=f[0]||r!=f[1])&&(f=[e,r])),s&&(u=t[0],i=t[1],l&&(u=u[1],i=i[1]),a=[u,i],s.invert&&(u=s(u),i=s(i)),u>i&&(c=u,u=i,i=c),(u!=h[0]||i!=h[1])&&(h=[u,i])),n):(l&&(o?(e=o[0],r=o[1]):(e=f[0],r=f[1],l.invert&&(e=l.invert(e),r=l.invert(r)),e>r&&(c=e,e=r,r=c))),s&&(a?(u=a[0],i=a[1]):(u=h[0],i=h[1],s.invert&&(u=s.invert(u),i=s.invert(i)),u>i&&(c=u,u=i,i=c))),l&&s?[[e,u],[r,i]]:l?[e,r]:s&&[u,i])},n.clear=function(){return n.empty()||(f=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!l&&f[0]==f[1]||!!s&&h[0]==h[1]},ta.rebind(n,c,"on")};var Hl={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Ol=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Il=ac.format=gc.timeFormat,Yl=Il.utc,Zl=Yl("%Y-%m-%dT%H:%M:%S.%LZ");Il.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Jo:Zl,Jo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Jo.toString=Zl.toString,ac.second=Ft(function(n){return new cc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ac.seconds=ac.second.range,ac.seconds.utc=ac.second.utc.range,ac.minute=Ft(function(n){return new cc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ac.minutes=ac.minute.range,ac.minutes.utc=ac.minute.utc.range,ac.hour=Ft(function(n){var t=n.getTimezoneOffset()/60;return new cc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ac.hours=ac.hour.range,ac.hours.utc=ac.hour.utc.range,ac.month=Ft(function(n){return n=ac.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ac.months=ac.month.range,ac.months.utc=ac.month.utc.range;var Vl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Xl=[[ac.second,1],[ac.second,5],[ac.second,15],[ac.second,30],[ac.minute,1],[ac.minute,5],[ac.minute,15],[ac.minute,30],[ac.hour,1],[ac.hour,3],[ac.hour,6],[ac.hour,12],[ac.day,1],[ac.day,2],[ac.week,1],[ac.month,1],[ac.month,3],[ac.year,1]],$l=Il.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",Ne]]),Bl={range:function(n,t,e){return ta.range(Math.ceil(n/e)*e,+t,e).map(Ko)},floor:y,ceil:y};Xl.year=ac.year,ac.scale=function(){return Go(ta.scale.linear(),Xl,$l)};var Wl=Xl.map(function(n){return[n[0].utc,n[1]]}),Jl=Yl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",Ne]]);Wl.year=ac.year.utc,ac.scale.utc=function(){return Go(ta.scale.linear(),Wl,Jl)},ta.text=At(function(n){return n.responseText}),ta.json=function(n,t){return Nt(n,"application/json",Qo,t)},ta.html=function(n,t){return Nt(n,"text/html",na,t)},ta.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(ta):"object"==typeof module&&module.exports&&(module.exports=ta),this.d3=ta}(); \ No newline at end of file diff --git a/ovsdb-ui/module/src/main/resources/ovsdb/lib/select2.full.min.js b/ovsdb-ui/module/src/main/resources/ovsdb/lib/select2.full.min.js new file mode 100755 index 0000000000..59d8c1aaa6 --- /dev/null +++ b/ovsdb-ui/module/src/main/resources/ovsdb/lib/select2.full.min.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.0 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){return n.apply(b,v.call(arguments,0).concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;hc;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('
    ');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('
  • '),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),this.$results.append(d)},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";{a(h)}this.template(b,h);for(var i=[],j=0;j",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b){var c=this,d=b.id+"-results";this.$results.attr("id",d),b.on("results:all",function(a){c.clear(),c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("results:append",function(a){c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("query",function(a){c.showLoading(a)}),b.on("select",function(){b.isOpen()&&c.setClasses()}),b.on("unselect",function(){b.isOpen()&&c.setClasses()}),b.on("open",function(){c.$results.attr("aria-expanded","true"),c.$results.attr("aria-hidden","false"),c.setClasses(),c.ensureHighlightVisible()}),b.on("close",function(){c.$results.attr("aria-expanded","false"),c.$results.attr("aria-hidden","true"),c.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=c.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=c.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?c.trigger("close"):c.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a);if(0!==d){var e=d-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top,h=f.offset().top,i=c.$results.scrollTop()+(h-g);0===e?c.$results.scrollTop(0):0>h-g&&c.$results.scrollTop(i)}}),b.on("results:next",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a),e=d+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top+c.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=c.$results.scrollTop()+h-g;0===e?c.$results.scrollTop(0):h>g&&c.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){c.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=c.$results.scrollTop(),d=c.$results.get(0).scrollHeight-c.$results.scrollTop()+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&d<=c.$results.height();e?(c.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(c.$results.scrollTop(c.$results.get(0).scrollHeight-c.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var d=a(this),e=d.data("data");return"true"===d.attr("aria-selected")?void(c.options.get("multiple")?c.trigger("unselect",{originalEvent:b,data:e}):c.trigger("close")):void c.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(){var b=a(this).data("data");c.getHighlightedResults().removeClass("select2-results__option--highlighted"),c.trigger("results:focus",{data:b,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a){var b=this,d=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){b.trigger("focus",a)}),this.$selection.on("blur",function(a){b.trigger("blur",a)}),this.$selection.on("keydown",function(a){b.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){b.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){b.update(a.data)}),a.on("open",function(){b.$selection.attr("aria-expanded","true"),b.$selection.attr("aria-owns",d),b._attachCloseHandler(a)}),a.on("close",function(){b.$selection.attr("aria-expanded","false"),b.$selection.removeAttr("aria-activedescendant"),b.$selection.removeAttr("aria-owns"),b.$selection.focus(),b._detachCloseHandler(a)}),a.on("enable",function(){b.$selection.attr("tabindex",b._tabindex)}),a.on("disable",function(){b.$selection.attr("tabindex","-1")})},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html(''),a},d.prototype.bind=function(a){var b=this;d.__super__.bind.apply(this,arguments);var c=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",c),this.$selection.attr("aria-labelledby",c),this.$selection.on("mousedown",function(a){1===a.which&&b.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(){}),this.$selection.on("blur",function(){}),a.on("selection:update",function(a){b.update(a.data)})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){return a("")},d.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.display(b),d=this.$selection.find(".select2-selection__rendered");d.empty().append(c),d.prop("title",b.title||b.text)},d}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('
      '),a},d.prototype.bind=function(){var b=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){b.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(c){var d=a(this),e=d.parent(),f=e.data("data");b.trigger("unselect",{originalEvent:c,data:f})})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){var b=a('
    • ×
    • ');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},a}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e0||0===c.length)){var d=a('×');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus()}),b.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val(""),e.$search.focus()}),b.on("enable",function(){e.$search.prop("disabled",!1)}),b.on("disable",function(){e.$search.prop("disabled",!0)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e.trigger("blur",a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}}),this.$selection.on("input",".select2-search--inline",function(){e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input",".select2-search--inline",function(a){e.handleSearch(a)})},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.trigger("open"),this.$search.val(b.text+" ")},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ç ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ç¢":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","ï¼¢":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","ï¼£":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","È»":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","ï¼¥":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ç´":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ä ":"G","Ǧ":"G","Ä¢":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","â±µ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","Ä°":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Ä®":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ä´":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ä¿":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ä»":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","â±¢":"L","â± ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","ï¼­":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","â±®":"M","Ɯ":"M","Ⓝ":"N","ï¼®":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","È ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","È®":"O","È°":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Æ ":"O","Ờ":"O","Ớ":"O","á» ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Æ¢":"OI","Ꝏ":"OO","È¢":"OU","Ⓟ":"P","ï¼°":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","â±£":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","ï¼±":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","ï¼²":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","ï¼³":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","á¹ ":"S","Å ":"S","Ṧ":"S","á¹¢":"S","Ṩ":"S","Ș":"S","Ş":"S","â±¾":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","ï¼´":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Å¢":"T","á¹°":"T","á¹®":"T","Ŧ":"T","Ƭ":"T","Æ®":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","ï¼µ":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Å®":"U","Å°":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","á»®":"U","Ử":"U","á»°":"U","Ụ":"U","á¹²":"U","Ų":"U","Ṷ":"U","á¹´":"U","Ʉ":"U","Ⓥ":"V","V":"V","á¹¼":"V","á¹¾":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","ï¼·":"W","Ẁ":"W","Ẃ":"W","Å´":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","â±²":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","ï¼¹":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","á»´":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Å»":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","Ç¡":"a","ä":"a","ǟ":"a","ả":"a","Ã¥":"a","Ç»":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","â±¥":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","Ç£":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","È©":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","Ä¡":"g","ǧ":"g","Ä£":"g","Ç¥":"g","É ":"g","ꞡ":"g","áµ¹":"g","ꝿ":"g","ⓗ":"h","h":"h","Ä¥":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","É¥":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","Ä©":"i","Ä«":"i","Ä­":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","Ç°":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","Ç©":"k","ḳ":"k","Ä·":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","Å¿":"l","ł":"l","ƚ":"l","É«":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","È­":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","È«":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","Æ¡":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","Ç«":"o","Ç­":"o","ø":"o","Ç¿":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","Æ£":"oi","È£":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","Æ¥":"p","áµ½":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","á¹¥":"s","ŝ":"s","ṡ":"s","Å¡":"s","ṧ":"s","á¹£":"s","ṩ":"s","ș":"s","ş":"s","È¿":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","Å¥":"t","á¹­":"t","ț":"t","Å£":"t","á¹±":"t","ṯ":"t","ŧ":"t","Æ­":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","Å©":"u","á¹¹":"u","Å«":"u","á¹»":"u","Å­":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","Æ°":"u","ừ":"u","ứ":"u","ữ":"u","á»­":"u","á»±":"u","ụ":"u","á¹³":"u","ų":"u","á¹·":"u","á¹µ":"u","ʉ":"u","ⓥ":"v","v":"v","á¹½":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","â±³":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","Å·":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","á»·":"y","ẙ":"y","ỵ":"y","Æ´":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","È¥":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Î¥","Ϋ":"Î¥","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f=0){var k=f.filter(d(j)),l=this.item(k),m=(c.extend(!0,{},l,j),this.option(l));k.replaceWith(m)}else{var n=this.option(j);if(j.children){var o=this.convertToOptions(j.children);b.appendMany(n,o)}h.push(n)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(b,c){this.ajaxOptions=this._applyDefaults(c.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),a.__super__.constructor.call(this,b,c)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return{q:a.term}},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url(a)),"function"==typeof f.data&&(f.data=f.data(a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");if(void 0!==f&&(this.createTag=f),b.call(this,c,d),a.isArray(e))for(var g=0;g0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.position=function(){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a){function b(){}return b.prototype.render=function(b){var c=b.call(this),d=a('');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},b.prototype.handleSearch=function(){if(!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},b.prototype.showSearch=function(){return!0},b}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('
    • '),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(a,b,c){this.$dropdownParent=c.get("dropdownParent")||document.body,a.call(this,b,c)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a(""),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c){var d=this,e="scroll.select2."+c.id,f="resize.select2."+c.id,g="orientationchange.select2."+c.id,h=this.$container.parents().filter(b.hasScroll);h.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),h.on(e,function(){var b=a(this).data("select2-scroll-position");a(this).scrollTop(b.y)}),a(window).on(e+" "+f+" "+g,function(){d._positionDropdown(),d._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c){var d="scroll.select2."+c.id,e="resize.select2."+c.id,f="orientationchange.select2."+c.id,g=this.$container.parents().filter(b.hasScroll);g.off(d),a(window).off(d+" "+e+" "+f)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=(this.$container.position(),this.$container.offset());f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.topf.bottom+h.height,l={left:f.left,top:g.bottom};c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){this.$dropdownContainer.width();var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.selectionAdapter=l.multiple?e:d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(this.options.dir=a.prop("dir")?a.prop("dir"):a.closest("[dir]").prop("dir")?a.closest("[dir]").prop("dir"):"ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("focus",function(){a.$container.addClass("select2-container--focus")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open"),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ENTER?(a.trigger("results:select"),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle"),b.preventDefault()):c===d.UP?(a.trigger("results:previous"),b.preventDefault()):c===d.DOWN?(a.trigger("results:next"),b.preventDefault()):(c===d.ESC||c===d.TAB)&&(a.close(),b.preventDefault()):(c===d.ENTER||c===d.SPACE||(c===d.DOWN||c===d.UP)&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable")):this.trigger("enable")},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||(this.trigger("query",{}),this.trigger("open"))},e.prototype.close=function(){this.isOpen()&&this.trigger("close")},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("select2/compat/utils",["jquery"],function(a){function b(b,c,d){var e,f,g=[];e=a.trim(b.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0===this.indexOf("select2-")&&g.push(this)})),e=a.trim(c.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&(f=d(this),null!=f&&g.push(f))})),b.attr("class",g.join(" "))}return{syncCssClasses:b}}),b.define("select2/compat/containerCss",["jquery","./utils"],function(a,b){function c(){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("containerCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptContainerCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("containerCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/dropdownCss",["jquery","./utils"],function(a,b){function c(){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("dropdownCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptDropdownCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("dropdownCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/initSelection",["jquery"],function(a){function b(a,b,c){c.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=c.get("initSelection"),this._isInitialized=!1,a.call(this,b,c)}return b.prototype.current=function(b,c){var d=this;return this._isInitialized?void b.call(this,c):void this.initSelection.call(null,this.$element,function(b){d._isInitialized=!0,a.isArray(b)||(b=[b]),c(b)})},b}),b.define("select2/compat/inputData",["jquery"],function(a){function b(a,b,c){this._currentData=[],this._valueSeparator=c.get("valueSeparator")||",","hidden"===b.prop("type")&&c.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a ` + + + +
      + Subnet + +
      + +
      +
      +
      + +
      +
      + +
      + + + + +
      {{info.key}} {{ info.value }}
      + + + + + + + +
      {{h}}
      {{ value }}
      +
      +
      +
      + + + + + diff --git a/ovsdb-ui/module/src/main/resources/ovsdb/root.tpl.html b/ovsdb-ui/module/src/main/resources/ovsdb/views/root.tpl.html similarity index 97% rename from ovsdb-ui/module/src/main/resources/ovsdb/root.tpl.html rename to ovsdb-ui/module/src/main/resources/ovsdb/views/root.tpl.html index 1340df593b..9e3cf154b7 100644 --- a/ovsdb-ui/module/src/main/resources/ovsdb/root.tpl.html +++ b/ovsdb-ui/module/src/main/resources/ovsdb/views/root.tpl.html @@ -1,8 +1,8 @@ - -
      + +
      diff --git a/resources/commons/NetvirtSfc.json.postman_collection b/resources/commons/NetvirtSfc.json.postman_collection new file mode 100644 index 0000000000..f73f0cb8f0 --- /dev/null +++ b/resources/commons/NetvirtSfc.json.postman_collection @@ -0,0 +1,189 @@ +{ + "id": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "name": "NetvirtSfc", + "requests": [ + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"access-lists\": {\n \"acl\": [\n {\n \"acl-name\": \"http-acl\",\n \"access-list-entries\": {\n \"ace\": [\n {\n \"rule-name\": \"http-rule\",\n \"matches\": {\n \"destination-port-range\": {\n \"lower-port\": \"80\",\n \"upper-port\": \"80\"\n }\n },\n \"actions\": {\n \"netvirt-sfc-acl:redirect-sfc\": \"acl\"\n }\n }\n ]\n }\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Content-Type: application/json\n", + "id": "19b44f74-e2ea-9267-50af-ad79b6309859", + "method": "PUT", + "name": "ietf-acl redirect-sfc", + "time": 1445298337983, + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/ietf-access-control-list:access-lists", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"service-function-forwarders\": {\n \"service-function-forwarder\": [\n {\n \"name\": \"SFF1\",\n \"service-node\": \"OVSDB2\",\n \"service-function-forwarder-ovs:ovs-bridge\": {\n \"bridge-name\": \"sw2\"\n },\n \"service-function-dictionary\": [\n {\n \"name\": \"firewall-72\",\n \"type\": \"service-function-type:firewall\",\n \"sff-sf-data-plane-locator\": {\n \"port\": 6633,\n \"ip\": \"192.168.50.71\",\n \"transport\": \"service-locator:vxlan-gpe\"\n }\n }\n ],\n \"sff-data-plane-locator\": [\n {\n \"name\": \"sfc-tun2\",\n \"data-plane-locator\": {\n \"transport\": \"service-locator:vxlan-gpe\",\n \"port\": 6633,\n \"ip\": \"192.168.50.71\"\n },\n \"service-function-forwarder-ovs:ovs-options\": {\n \"remote-ip\": \"flow\",\n \"dst-port\": \"6633\",\n \"key\": \"flow\",\n \"nsp\": \"flow\",\n \"nsi\": \"flow\",\n \"nshc1\": \"flow\",\n \"nshc2\": \"flow\",\n \"nshc3\": \"flow\",\n \"nshc4\": \"flow\"\n }\n }\n ]\n },\n {\n \"name\": \"SFF2\",\n \"service-node\": \"OVSDB2\",\n \"service-function-forwarder-ovs:ovs-bridge\": {\n \"bridge-name\": \"sw4\"\n },\n \"service-function-dictionary\": [\n {\n \"name\": \"dpi-74\",\n \"type\": \"service-function-type:dpi\",\n \"sff-sf-data-plane-locator\": {\n \"port\": 6633,\n \"ip\": \"192.168.50.73\",\n \"transport\": \"service-locator:vxlan-gpe\"\n }\n }\n ],\n \"sff-data-plane-locator\": [\n {\n \"name\": \"sfc-tun4\",\n \"data-plane-locator\": {\n \"transport\": \"service-locator:vxlan-gpe\",\n \"port\": 6633,\n \"ip\": \"192.168.50.73\"\n },\n \"service-function-forwarder-ovs:ovs-options\": {\n \"remote-ip\": \"flow\",\n \"dst-port\": \"6633\",\n \"key\": \"flow\",\n \"nsp\": \"flow\",\n \"nsi\": \"flow\",\n \"nshc1\": \"flow\",\n \"nshc2\": \"flow\",\n \"nshc3\": \"flow\",\n \"nshc4\": \"flow\"\n }\n }\n ]\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", + "id": "1b067065-f0d5-d871-28cf-5e7e195eb463", + "method": "PUT", + "name": "service-function-forwarders", + "time": 1445302610648, + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/service-function-forwarder:service-function-forwarders", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"access-lists\": {\n \"acl\": [\n {\n \"acl-name\": \"http-acl\",\n \"access-list-entries\": {\n \"ace\": [\n {\n \"rule-name\": \"http-rule\",\n \"matches\": {\n \"destination-port-range\": {\n \"lower-port\": \"80\",\n \"upper-port\": \"80\"\n }\n },\n \"actions\": {\n \"permit\": \"true\"\n\n }\n }\n ]\n }\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Content-Type: application/json\n", + "id": "62e3b4d9-2672-e2bd-25c9-70bfb6771026", + "method": "PUT", + "name": "ietf-acl", + "time": 1445298397905, + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/ietf-access-control-list:access-lists", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"classifiers\": {\n \"classifier\": [\n {\n \"name\": \"http-classifier\",\n \"acl\": \"http-acl\",\n \"sffs\": {\n \"sff\": [\n {\n \"name\": \"sff1\"\n }\n ]\n }\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", + "id": "718ed9fd-f7f3-3862-4a53-b12d83c880ae", + "method": "PUT", + "name": "Classifier", + "time": 1444924721709, + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/netvirt-sfc-classifier:classifiers", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"access-lists\": {\n \"acl\": [\n {\n \"acl-name\": \"http-acl\",\n \"access-list-entries\": {\n \"ace\": [\n {\n \"rule-name\": \"http-rule\",\n \"matches\": {\n \"destination-port-range\": {\n \"lower-port\": \"80\",\n \"upper-port\": \"80\"\n }\n },\n \"actions\": {\n \"permit\": {}\n }\n }\n ]\n }\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Content-Type: application/json\n", + "id": "7412ee0c-b116-5cf1-0646-efa65f0da784", + "method": "GET", + "name": "ietf-acl", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/ietf-access-control-list:access-lists", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"classifiers\": {\n \"classifier\": [\n {\n \"name\": \"http-classifier\",\n \"acl\": \"http-acl\",\n \"sffs\": {\n \"sff\": [\n {\n \"name\": \"sff1\"\n }\n ]\n }\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", + "id": "76b32432-5a24-9d6d-8ba2-76eb816411af", + "method": "DELETE", + "name": "Classifier", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/netvirt-sfc-classifier:classifiers", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"access-lists\": {\n \"acl\": [\n {\n \"acl-name\": \"http-acl\",\n \"access-list-entries\": {\n \"ace\": [\n {\n \"rule-name\": \"http-rule\",\n \"matches\": {\n \"destination-port-range\": {\n \"lower-port\": \"80\",\n \"upper-port\": \"80\"\n }\n },\n \"actions\": {\n \"netvirt-sfc-acl:redirect-sfc\": \"acl\"\n }\n }\n ]\n }\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Content-Type: application/json\n", + "id": "99dc6bb5-f01a-64cd-a024-c333fdf82643", + "method": "DELETE", + "name": "ietf-acl redirect-sfc", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/ietf-access-control-list:access-lists", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n\t\"sfc\": {\n \"name\": \"sfc1\"\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", + "id": "9d418a4b-197d-47c2-e85f-e68f6b5f5c40", + "method": "GET", + "name": "Sfc", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/netvirt-sfc:sfc/", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"classifiers\": {\n \"classifier\": [\n {\n \"name\": \"http-classifier\",\n \"acl\": \"http-acl\",\n \"sffs\": {\n \"sff\": [\n {\n \"name\": \"sff1\"\n }\n ]\n }\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n", + "id": "b2d13061-80e0-ad8f-d0e6-d6939ea2b0a4", + "method": "GET", + "name": "Classifier", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/netvirt-sfc-classifier:classifiers", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"service-functions\": {\n \"service-function\": [\n {\n \"name\": \"firewall-72\",\n \"ip-mgmt-address\": \"192.168.50.72\",\n \"type\": \"service-function-type:firewall\",\n \"nsh-aware\": \"true\",\n \"sf-data-plane-locator\": [\n {\n \"name\": \"2\",\n \"port\": 6633,\n \"ip\": \"192.168.50.72\",\n \"transport\": \"service-locator:vxlan-gpe\",\n \"service-function-forwarder\": \"SFF1\"\n }\n ]\n },\n {\n \"name\": \"dpi-74\",\n \"ip-mgmt-address\": \"192.168.50.74\",\n \"type\": \"service-function-type:dpi\",\n \"nsh-aware\": \"true\",\n \"sf-data-plane-locator\": [\n {\n \"name\": \"3\",\n \"port\": 6633,\n \"ip\": \"192.168.50.74\",\n \"transport\": \"service-locator:vxlan-gpe\",\n \"service-function-forwarder\": \"SFF2\"\n }\n ]\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", + "id": "bf4f1148-5192-fd7d-3026-5a11668db75d", + "method": "PUT", + "name": "service-functions", + "time": 1445302596335, + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/service-function:service-functions", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"service-function-forwarders\": {\n \"service-function-forwarder\": [\n {\n \"name\": \"SFF1\",\n \"service-node\": \"OVSDB2\",\n \"service-function-forwarder-ovs:ovs-bridge\": {\n \"bridge-name\": \"sw2\"\n },\n \"service-function-dictionary\": [\n {\n \"name\": \"firewall-72\",\n \"type\": \"service-function-type:firewall\",\n \"sff-sf-data-plane-locator\": {\n \"port\": 6633,\n \"ip\": \"192.168.50.71\",\n \"transport\": \"service-locator:vxlan-gpe\"\n }\n }\n ],\n \"sff-data-plane-locator\": [\n {\n \"name\": \"sfc-tun2\",\n \"data-plane-locator\": {\n \"transport\": \"service-locator:vxlan-gpe\",\n \"port\": 6633,\n \"ip\": \"192.168.50.71\"\n },\n \"service-function-forwarder-ovs:ovs-options\": {\n \"remote-ip\": \"flow\",\n \"dst-port\": \"6633\",\n \"key\": \"flow\",\n \"nsp\": \"flow\",\n \"nsi\": \"flow\",\n \"nshc1\": \"flow\",\n \"nshc2\": \"flow\",\n \"nshc3\": \"flow\",\n \"nshc4\": \"flow\"\n }\n }\n ]\n },\n {\n \"name\": \"SFF2\",\n \"service-node\": \"OVSDB2\",\n \"service-function-forwarder-ovs:ovs-bridge\": {\n \"bridge-name\": \"sw4\"\n },\n \"service-function-dictionary\": [\n {\n \"name\": \"dpi-74\",\n \"type\": \"service-function-type:dpi\",\n \"sff-sf-data-plane-locator\": {\n \"port\": 6633,\n \"ip\": \"192.168.50.73\",\n \"transport\": \"service-locator:vxlan-gpe\"\n }\n }\n ],\n \"sff-data-plane-locator\": [\n {\n \"name\": \"sfc-tun4\",\n \"data-plane-locator\": {\n \"transport\": \"service-locator:vxlan-gpe\",\n \"port\": 6633,\n \"ip\": \"192.168.50.73\"\n },\n \"service-function-forwarder-ovs:ovs-options\": {\n \"remote-ip\": \"flow\",\n \"dst-port\": \"6633\",\n \"key\": \"flow\",\n \"nsp\": \"flow\",\n \"nsi\": \"flow\",\n \"nshc1\": \"flow\",\n \"nshc2\": \"flow\",\n \"nshc3\": \"flow\",\n \"nshc4\": \"flow\"\n }\n }\n ]\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", + "id": "e6542ed1-832c-71c9-fe74-2d25c9e7f7ad", + "method": "GET", + "name": "service-function-forwarders", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/service-function-forwarder:service-function-forwarders", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n\t\"sfc\": {\n \"name\": \"sfc1\"\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", + "id": "ea1c70a0-6528-862c-cabc-a9b187d483c7", + "method": "PUT", + "name": "Sfc", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/netvirt-sfc:sfc/", + "version": 2 + }, + { + "collectionId": "bc172f70-35df-2cdf-50a4-8ad09b8f17a2", + "data": "{\n \"service-functions\": {\n \"service-function\": [\n {\n \"name\": \"firewall-72\",\n \"ip-mgmt-address\": \"192.168.50.72\",\n \"type\": \"service-function-type:firewall\",\n \"nsh-aware\": \"true\",\n \"sf-data-plane-locator\": [\n {\n \"name\": \"2\",\n \"port\": 6633,\n \"ip\": \"192.168.50.72\",\n \"transport\": \"service-locator:vxlan-gpe\",\n \"service-function-forwarder\": \"SFF1\"\n }\n ]\n },\n {\n \"name\": \"dpi-74\",\n \"ip-mgmt-address\": \"192.168.50.74\",\n \"type\": \"service-function-type:dpi\",\n \"nsh-aware\": \"true\",\n \"sf-data-plane-locator\": [\n {\n \"name\": \"3\",\n \"port\": 6633,\n \"ip\": \"192.168.50.74\",\n \"transport\": \"service-locator:vxlan-gpe\",\n \"service-function-forwarder\": \"SFF2\"\n }\n ]\n }\n ]\n }\n}", + "dataMode": "raw", + "description": "", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n", + "id": "fff50fa7-aa2d-71a4-db49-4ea10b82937e", + "method": "GET", + "name": "service-functions", + "responses": [], + "timestamp": 0, + "url": "http://localhost:8181/restconf/config/service-function:service-functions", + "version": 2 + } + ], + "timestamp": 1444922427094 +} diff --git a/resources/commons/README b/resources/commons/README index 98de4d5bab..25cf72cccf 100644 --- a/resources/commons/README +++ b/resources/commons/README @@ -27,4 +27,4 @@ Contents - 3-Node-Cluster-Setup-Environment-Variables.postman_environment : Postman environment file that defines variables for Restconf request for southbound plugin running in 3 node cluster environment - +- NetvirtSfc.json.postman_collection : Collection of REST-APIs to interact with Netvirt-Sfc. diff --git a/utils/config/src/test/java/org/opendaylight/ovsdb/utils/config/ConfigPropertiesTest.java b/utils/config/src/test/java/org/opendaylight/ovsdb/utils/config/ConfigPropertiesTest.java index 91d7817323..d0fac74adc 100644 --- a/utils/config/src/test/java/org/opendaylight/ovsdb/utils/config/ConfigPropertiesTest.java +++ b/utils/config/src/test/java/org/opendaylight/ovsdb/utils/config/ConfigPropertiesTest.java @@ -1,5 +1,5 @@ /* -* Copyright (C) 2014 Red Hat, Inc. +* Copyright (C) 2014 Red Hat, 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, diff --git a/utils/mdsal-node/src/test/java/org/opendaylight/ovsdb/utils/mdsal/node/NodeUtilsTest.java b/utils/mdsal-node/src/test/java/org/opendaylight/ovsdb/utils/mdsal/node/NodeUtilsTest.java index 291b2349bb..c6b0a2bfa1 100644 --- a/utils/mdsal-node/src/test/java/org/opendaylight/ovsdb/utils/mdsal/node/NodeUtilsTest.java +++ b/utils/mdsal-node/src/test/java/org/opendaylight/ovsdb/utils/mdsal/node/NodeUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Red Hat, Inc. + * Copyright (C) 2015 Red Hat, 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, diff --git a/utils/mdsal-openflow/src/test/java/org/opendaylight/ovsdb/utils/mdsal/openflow/MatchUtilsTest.java b/utils/mdsal-openflow/src/test/java/org/opendaylight/ovsdb/utils/mdsal/openflow/MatchUtilsTest.java index 886ae541ee..807895c9e7 100644 --- a/utils/mdsal-openflow/src/test/java/org/opendaylight/ovsdb/utils/mdsal/openflow/MatchUtilsTest.java +++ b/utils/mdsal-openflow/src/test/java/org/opendaylight/ovsdb/utils/mdsal/openflow/MatchUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2014 Red Hat, 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, diff --git a/utils/servicehelper/src/test/java/org/opendaylight/ovsdb/utils/servicehelper/ServiceHelperTest.java b/utils/servicehelper/src/test/java/org/opendaylight/ovsdb/utils/servicehelper/ServiceHelperTest.java index 72a837e34b..d04b15a970 100644 --- a/utils/servicehelper/src/test/java/org/opendaylight/ovsdb/utils/servicehelper/ServiceHelperTest.java +++ b/utils/servicehelper/src/test/java/org/opendaylight/ovsdb/utils/servicehelper/ServiceHelperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Red Hat, Inc. + * Copyright (C) 2015 Red Hat, 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,