Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986 01/2401/1
authorAshlee Young <ashlee@onosfw.com>
Sat, 10 Oct 2015 01:32:44 +0000 (18:32 -0700)
committerAshlee Young <ashlee@onosfw.com>
Sat, 10 Oct 2015 01:32:44 +0000 (18:32 -0700)
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f

578 files changed:
framework/src/onos/apps/aaa/pom.xml
framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java
framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java [new file with mode: 0644]
framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java
framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java [new file with mode: 0644]
framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java [new file with mode: 0644]
framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java
framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java
framework/src/onos/apps/acl/pom.xml
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml
framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java [new file with mode: 0644]
framework/src/onos/apps/bgprouter/pom.xml
framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java
framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java
framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java
framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java [new file with mode: 0644]
framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java
framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java
framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java
framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java
framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java
framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java
framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java
framework/src/onos/apps/flowanalyzer/pom.xml
framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java [new file with mode: 0644]
framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java
framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml [new file with mode: 0644]
framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java [new file with mode: 0644]
framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java [new file with mode: 0644]
framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java [new file with mode: 0644]
framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java [new file with mode: 0644]
framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java [new file with mode: 0644]
framework/src/onos/apps/igmp/pom.xml [new file with mode: 0644]
framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java [new file with mode: 0644]
framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java [new file with mode: 0644]
framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/pom.xml [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml [new file with mode: 0644]
framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml [new file with mode: 0644]
framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java
framework/src/onos/apps/pim/pom.xml [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml [new file with mode: 0644]
framework/src/onos/apps/pom.xml
framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java
framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java [new file with mode: 0644]
framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java [new file with mode: 0644]
framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java [new file with mode: 0644]
framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java
framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java [new file with mode: 0644]
framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java
framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java [new file with mode: 0644]
framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java
framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java
framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java
framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java
framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java
framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java [new file with mode: 0644]
framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java [new file with mode: 0644]
framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java [new file with mode: 0644]
framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java [new file with mode: 0644]
framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java
framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java
framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java
framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java
framework/src/onos/apps/vtn/app/app.xml [new file with mode: 0644]
framework/src/onos/apps/vtn/app/features.xml [new file with mode: 0644]
framework/src/onos/apps/vtn/app/pom.xml [new file with mode: 0644]
framework/src/onos/apps/vtn/pom.xml
framework/src/onos/apps/vtn/vtnmgr/pom.xml [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/pom.xml [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnweb/pom.xml [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java [new file with mode: 0644]
framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml [new file with mode: 0644]
framework/src/onos/bgp/api/pom.xml [new file with mode: 0755]
framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPCfg.java [new file with mode: 0755]
framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPController.java [new file with mode: 0755]
framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPId.java [new file with mode: 0755]
framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPacketStats.java [new file with mode: 0755]
framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPeerCfg.java [new file with mode: 0755]
framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/pom.xml [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/BGPParseException.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPKeepaliveMsg.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessage.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageReader.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageWriter.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPType.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPVersion.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/IGPRouterID.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/Writeable.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/BGPPrefixLSIdentifier.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/NodeDescriptors.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPKeepaliveMsgVer4.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AreaIDTlv.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AutonomousSystemTlv.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPHeader.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPLSIdentifierTlv.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPValueType.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IPReachabilityInformationTlv.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsNonPseudonode.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsPseudonode.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFNonPseudonode.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFPseudonode.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFRouteTypeTlv.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeFlagBitTlv.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeMultiTopologyId.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/UnSupportedAttribute.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Validation.java [new file with mode: 0644]
framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/pom.xml [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPConfig.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPControllerImpl.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageDecoder.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageEncoder.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPacketStatsImpl.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPeerConfig.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPipelineFactory.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/Controller.java [new file with mode: 0755]
framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/package-info.java [new file with mode: 0755]
framework/src/onos/bgp/pom.xml [new file with mode: 0755]
framework/src/onos/cli/src/main/java/org/onosproject/cli/Comparators.java
framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigCommand.java
framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigRegistryCommand.java
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceControllersCommand.java [new file with mode: 0644]
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceSetControllersCommand.java [new file with mode: 0644]
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java [new file with mode: 0644]
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java [new file with mode: 0644]
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceRemoveCommand.java [new file with mode: 0644]
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/PacketProcessorsListCommand.java
framework/src/onos/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java [new file with mode: 0644]
framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
framework/src/onos/core/api/src/main/java/org/onosproject/cfg/ComponentConfigService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/DefaultDisjointPath.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/DisjointPath.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerConfig.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerInfo.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/config/NetworkConfigService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/config/SubjectFactory.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTypedFlowEntry.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleProviderService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TypedStoredFlowEntry.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostStore.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/FlowRuleIntent.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastEvent.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastListener.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastRoute.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/MulticastRouteService.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/package-info.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketProcessorEntry.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStore.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStoreDelegate.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/device/IntentSetMultimap.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResourceRequest.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticService.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticStore.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/SummaryFlowEntryWithLoad.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/TypedFlowEntryWithLoad.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/DefaultGraphDescription.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/PathService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java
framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java
framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AsyncAtomicCounter.java
framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AtomicCounter.java
framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexExecutionService.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexTask.java [new file with mode: 0644]
framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java [new file with mode: 0644]
framework/src/onos/core/api/src/test/java/org/onosproject/cfg/ComponentConfigAdapter.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/behaviour/ControllerInfoTest.java [new file with mode: 0644]
framework/src/onos/core/api/src/test/java/org/onosproject/net/config/NetworkConfigServiceAdapter.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/FlowRuleServiceAdapter.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/DefaultGraphDescriptionTest.java
framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/PathServiceAdapter.java [new file with mode: 0644]
framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java
framework/src/onos/core/api/src/test/java/org/onosproject/store/service/TestAtomicCounter.java
framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java
framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java [new file with mode: 0644]
framework/src/onos/core/common/src/main/java/org/onosproject/common/DefaultTopology.java
framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java
framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java
framework/src/onos/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java
framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java
framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java
framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimplePacketStore.java
framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleTopologyStore.java
framework/src/onos/core/net/pom.xml
framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigLoader.java [new file with mode: 0644]
framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/FlowStatisticManager.java [new file with mode: 0644]
framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java
framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java
framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ComponentConfigLoaderTest.java [new file with mode: 0644]
framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java
framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java
framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java
framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java
framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badComponent.json [new file with mode: 0644]
framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badConfig.json [new file with mode: 0644]
framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/basic.json [new file with mode: 0644]
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Database.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncAtomicCounter.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAtomicCounter.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/MutexExecutionManager.java [new file with mode: 0644]
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/EventuallyConsistentMapImpl.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/group/impl/DistributedGroupStore.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/host/impl/ECHostStore.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/link/impl/GossipLinkStore.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/packet/impl/DistributedPacketStore.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentIntentSetMultimap.java [new file with mode: 0644]
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentLinkResourceStore.java
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedFlowStatisticStore.java [new file with mode: 0644]
framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java
framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/host/impl/ECHostStoreTest.java [new file with mode: 0644]
framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
framework/src/onos/docs/external-excludes
framework/src/onos/docs/internal-apps
framework/src/onos/drivers/pom.xml
framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java [new file with mode: 0644]
framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java [new file with mode: 0644]
framework/src/onos/drivers/src/main/resources/onos-drivers.xml
framework/src/onos/drivers/src/test/java/org/onosproject/driver/ovsdb/OvsdbControllerConfigTest.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/DomainIntentResource.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentDomainProvider.java
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentResource.java
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/NetworkIntentResource.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/InterfaceAdminService.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/DomainTunnelId.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/NetworkTunnelId.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderRegistry.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java [new file with mode: 0644]
framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java [new file with mode: 0644]
framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/intf/impl/InterfaceManager.java
framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastData.java [new file with mode: 0644]
framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManager.java [new file with mode: 0644]
framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/package-info.java [new file with mode: 0644]
framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java
framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java [new file with mode: 0644]
framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java [new file with mode: 0644]
framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java [new file with mode: 0644]
framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java
framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java
framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java [new file with mode: 0644]
framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/package-info.java [new file with mode: 0644]
framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java
framework/src/onos/ovsdb/api/pom.xml
framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbClientService.java
framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java
framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbController.java
framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java
framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbClientServiceAdapter.java [new file with mode: 0644]
framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbControllerAdapter.java [new file with mode: 0644]
framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java
framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java
framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbJsonRpcHandler.java
framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/notation/Row.java
framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/Bridge.java
framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/TableGenerator.java
framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/utils/FromJsonUtil.java
framework/src/onos/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepErrorVer1.java
framework/src/onos/pom.xml
framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java
framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java [new file with mode: 0644]
framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java [new file with mode: 0644]
framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java [new file with mode: 0644]
framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java
framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java
framework/src/onos/tools/build/conf/pom.xml
framework/src/onos/tools/build/conf/src/main/resources/onos/checkstyle.xml
framework/src/onos/tools/build/docker/Dockerfile
framework/src/onos/tools/build/onos-package
framework/src/onos/tools/dev/bash_profile
framework/src/onos/tools/dev/bin/onos-create-app
framework/src/onos/tools/dev/header.txt
framework/src/onos/tools/package/archetypes/pom.xml
framework/src/onos/tools/package/archetypes/ui/pom.xml
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/pom.xml
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiComponent.java
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiMessageHandler.java
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.css [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.html [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.js [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/css.html
framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/js.html
framework/src/onos/tools/package/archetypes/uitab/pom.xml [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/META-INF/maven/archetype-metadata.xml [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/pom.xml [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableComponent.java [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableMessageHandler.java [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.css [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.html [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.js [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/css.html [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/js.html [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/archetype.properties [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/goal.txt [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/pom.xml [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/META-INF/maven/archetype-metadata.xml [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/pom.xml [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovComponent.java [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovMessageHandler.java [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovOverlay.java [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLink.java [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLinkMap.java [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.css [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.html [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovDemo.js [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovOverlay.js [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/css.html [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/js.html [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/archetype.properties [new file with mode: 0644]
framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/goal.txt [new file with mode: 0644]
framework/src/onos/tools/package/config/samples/component-cfg.json [new file with mode: 0644]
framework/src/onos/tools/package/config/samples/network-cfg.json [new file with mode: 0644]
framework/src/onos/tools/test/bin/ogroup-opts
framework/src/onos/tools/test/bin/onos
framework/src/onos/tools/test/bin/onos-archetypes-test
framework/src/onos/tools/test/bin/onos-batch
framework/src/onos/tools/test/bin/onos-config
framework/src/onos/tools/test/bin/onos-install
framework/src/onos/tools/test/bin/onos-push-bits
framework/src/onos/tools/test/bin/onos-service
framework/src/onos/tools/test/bin/onos-set-controllers
framework/src/onos/tools/test/bin/onos-uninstall
framework/src/onos/tools/test/bin/onos-watch
framework/src/onos/tools/test/cells/andrea [new file with mode: 0644]
framework/src/onos/tools/test/cells/tomx
framework/src/onos/tools/test/scenarios/archetypes.xml
framework/src/onos/tools/test/topos/onos.py
framework/src/onos/tools/test/topos/optical2.py
framework/src/onos/tools/test/topos/solar.py
framework/src/onos/tools/test/topos/sys-nonlinear-10.config
framework/src/onos/tools/test/topos/sys-nonlinear-4.config
framework/src/onos/tools/test/topos/sys.config
framework/src/onos/utils/junit/src/main/java/org/onlab/junit/TestUtils.java
framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java
framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java
framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java
framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java
framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexDump.java [new file with mode: 0755]
framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java
framework/src/onos/utils/misc/src/test/java/org/onlab/graph/SRLGGraphSearchTest.java
framework/src/onos/utils/misc/src/test/java/org/onlab/util/AbstractAccumulatorTest.java
framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java [new file with mode: 0644]
framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimerTest.java [new file with mode: 0644]
framework/src/onos/utils/osgi/src/test/java/org/onlab/osgi/ComponentContextAdapter.java
framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java
framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java
framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java
framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java [new file with mode: 0644]
framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js
framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js
framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js
framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css
framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html
framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js
framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css [new file with mode: 0644]
framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html [new file with mode: 0644]
framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js [new file with mode: 0644]
framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html
framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js
framework/src/onos/web/gui/src/main/webapp/index.html
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json [new file with mode: 0644]
framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json [new file with mode: 0644]

index 78fd4a6..b03930a 100644 (file)
 
         <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onlab-junit</artifactId>
-            <scope>test</scope>
+            <artifactId>onos-api</artifactId>
+            <version>${project.version}</version>
         </dependency>
 
         <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onos-api</artifactId>
+            <artifactId>onos-app-xos-integration</artifactId>
             <version>${project.version}</version>
         </dependency>
 
-         <dependency>
+        <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onlab-osgi</artifactId>
-            <version>${project.version}</version>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-xos-integration</artifactId>
+            <artifactId>onos-api</artifactId>
             <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
 
-        <build>
+    <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.felix</groupId>
index 7e3de88..479ec7e 100644 (file)
  */
 package org.onosproject.aaa;
 
-import com.google.common.base.Strings;
-import com.google.common.collect.Maps;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.Set;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Modified;
-import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.DeserializationException;
@@ -38,14 +39,16 @@ import org.onlab.packet.RADIUSAttribute;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
 import org.onlab.packet.VlanId;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -58,28 +61,21 @@ import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.xosintegration.VoltTenantService;
-import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
+import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
 import static org.onosproject.net.packet.PacketPriority.CONTROL;
 import static org.slf4j.LoggerFactory.getLogger;
 
-
 /**
  * AAA application for ONOS.
  */
 @Component(immediate = true)
 public class AAA {
+
+    // for verbose output
+    private final Logger log = getLogger(getClass());
+
     // a list of our dependencies :
     // to register with ONOS as an application - described next
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -96,8 +92,28 @@ public class AAA {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected VoltTenantService voltTenantService;
 
-    // for verbose output
-    private final Logger log = getLogger(getClass());
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry netCfgService;
+
+    // Parsed RADIUS server addresses
+    protected InetAddress radiusIpAddress;
+    protected String radiusMacAddress;
+
+    // NAS IP address
+    protected InetAddress nasIpAddress;
+    protected String nasMacAddress;
+
+    // RADIUS server secret
+    protected String radiusSecret;
+
+    // ID of RADIUS switch
+    protected String radiusSwitch;
+
+    // RADIUS port number
+    protected long radiusPort;
+
+    // RADIUS server TCP port number
+    protected short radiusServerPort;
 
     // our application-specific event handler
     private ReactivePacketProcessor processor = new ReactivePacketProcessor();
@@ -105,124 +121,80 @@ public class AAA {
     // our unique identifier
     private ApplicationId appId;
 
-    // Map of state machines. Each state machine is represented by an
-    // unique identifier on the switch: dpid + port number
-    Map stateMachineMap = null;
-
-    // RADIUS server IP address
-    private static final String DEFAULT_RADIUS_IP = "192.168.1.10";
-    // NAS IP address
-    private static final String DEFAULT_NAS_IP = "192.168.1.11";
-    // RADIUS uplink port
-    private static final int DEFAULT_RADIUS_UPLINK = 2;
-    // RADIUS server shared secret
-    private static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
-    // RADIUS MAC address
-    private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10";
-    // NAS MAC address
-    private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01";
-    // Radius Switch Id
-    private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
-    // Radius Port Number
-    private static final String DEFAULT_RADIUS_PORT = "129";
-
-    @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP,
-            label = "RADIUS IP Address")
-    private String radiusIpAddress = DEFAULT_RADIUS_IP;
-
-    @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP,
-            label = "NAS IP Address")
-    private String nasIpAddress = DEFAULT_NAS_IP;
-
-    @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS,
-            label = "RADIUS MAC Address")
-    private String radiusMacAddress = RADIUS_MAC_ADDRESS;
-
-    @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS,
-            label = "NAS MAC Address")
-    private String nasMacAddress = NAS_MAC_ADDRESS;
-
-    @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET,
-            label = "RADIUS shared secret")
-    private String radiusSecret = DEFAULT_RADIUS_SECRET;
-
-    @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH,
-            label = "Radius switch")
-    private String radiusSwitch = DEFAULT_RADIUS_SWITCH;
-
-    @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT,
-            label = "Radius port")
-    private String radiusPort = DEFAULT_RADIUS_PORT;
-
-    // Parsed RADIUS server IP address
-    protected InetAddress parsedRadiusIpAddress;
-
-    // Parsed NAS IP address
-    protected InetAddress parsedNasIpAddress;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ComponentConfigService cfgService;
-
-    @Modified
-    public void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-
-        String s = Tools.get(properties, "radiusIpAddress");
-        try {
-            parsedRadiusIpAddress = InetAddress.getByName(s);
-            radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s;
-        } catch (UnknownHostException e) {
-            log.error("Invalid RADIUS IP address specification: {}", s);
-        }
-        try {
-            s = Tools.get(properties, "nasIpAddress");
-            parsedNasIpAddress = InetAddress.getByName(s);
-            nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s;
-        } catch (UnknownHostException e) {
-            log.error("Invalid NAS IP address specification: {}", s);
-        }
+    // Configuration properties factory
+    private final ConfigFactory factory =
+            new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY,
+                                                         AAAConfig.class,
+                                                         "AAA") {
+                @Override
+                public AAAConfig createConfig() {
+                    return new AAAConfig();
+                }
+            };
 
-        s = Tools.get(properties, "radiusMacAddress");
-        radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s;
+    // Listener for config changes
+    private final InternalConfigListener cfgListener = new InternalConfigListener();
 
-        s = Tools.get(properties, "nasMacAddress");
-        nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s;
+    /**
+     * Builds an EAPOL packet based on the given parameters.
+     *
+     * @param dstMac    destination MAC address
+     * @param srcMac    source MAC address
+     * @param vlan      vlan identifier
+     * @param eapolType EAPOL type
+     * @param eap       EAP payload
+     * @return Ethernet frame
+     */
+    private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
+                                               short vlan, byte eapolType, EAP eap) {
 
-        s = Tools.get(properties, "radiusSecret");
-        radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s;
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(dstMac.toBytes());
+        eth.setSourceMACAddress(srcMac.toBytes());
+        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        if (vlan != Ethernet.VLAN_UNTAGGED) {
+            eth.setVlanID(vlan);
+        }
+        //eapol header
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(eapolType);
+        eapol.setPacketLength(eap.getLength());
 
-        s = Tools.get(properties, "radiusSwitchId");
-        radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s;
+        //eap part
+        eapol.setPayload(eap);
 
-        s = Tools.get(properties, "radiusPortNumber");
-        radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s;
+        eth.setPayload(eapol);
+        eth.setPad(true);
+        return eth;
     }
 
     @Activate
-    public void activate(ComponentContext context) {
-        cfgService.registerProperties(getClass());
-        modified(context);
+    public void activate() {
+        netCfgService.addListener(cfgListener);
+        netCfgService.registerConfigFactory(factory);
+
         // "org.onosproject.aaa" is the FQDN of our app
         appId = coreService.registerApplication("org.onosproject.aaa");
+
+        cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AAAConfig.class));
+
         // register our event handler
         packetService.addProcessor(processor, PacketProcessor.director(2));
         requestIntercepts();
-        // Instantiate the map of the state machines
-        stateMachineMap = Collections.synchronizedMap(Maps.newHashMap());
 
-        hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
+        StateMachine.initializeMaps();
 
+        hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
     }
 
     @Deactivate
     public void deactivate() {
-        cfgService.unregisterProperties(getClass(), false);
-
         appId = coreService.registerApplication("org.onosproject.aaa");
         withdrawIntercepts();
         // de-register and null our handler
         packetService.removeProcessor(processor);
         processor = null;
+        StateMachine.destroyMaps();
     }
 
     /**
@@ -237,8 +209,8 @@ public class AAA {
         TrafficSelector radSelector = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
                 .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(1812))
-                .matchUdpSrc(TpPort.tpPort(1812))
+                .matchUdpDst(TpPort.tpPort(radiusServerPort))
+                .matchUdpSrc(TpPort.tpPort(radiusServerPort))
                 .build();
         packetService.requestPackets(radSelector, CONTROL, appId);
     }
@@ -254,45 +226,12 @@ public class AAA {
         TrafficSelector radSelector = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
                 .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(1812))
-                .matchUdpSrc(TpPort.tpPort(1812))
+                .matchUdpDst(TpPort.tpPort(radiusServerPort))
+                .matchUdpSrc(TpPort.tpPort(radiusServerPort))
                 .build();
         packetService.cancelPackets(radSelector, CONTROL, appId);
     }
 
-    /**
-     * Builds an EAPOL packet based on the given parameters.
-     *
-     * @param dstMac    destination MAC address
-     * @param srcMac    source MAC address
-     * @param vlan      vlan identifier
-     * @param eapolType EAPOL type
-     * @param eap       EAP payload
-     * @return Ethernet frame
-     */
-    private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac,
-                                               short vlan, byte eapolType, EAP eap) {
-
-        Ethernet eth = new Ethernet();
-        eth.setDestinationMACAddress(dstMac.toBytes());
-        eth.setSourceMACAddress(srcMac.toBytes());
-        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
-        if (vlan != Ethernet.VLAN_UNTAGGED) {
-            eth.setVlanID(vlan);
-        }
-        //eapol header
-        EAPOL eapol = new EAPOL();
-        eapol.setEapolType(eapolType);
-        eapol.setPacketLength(eap.getLength());
-
-        //eap part
-        eapol.setPayload(eap);
-
-        eth.setPayload(eapol);
-        eth.setPad(true);
-        return eth;
-    }
-
     // our handler defined as a private inner class
 
     /**
@@ -308,42 +247,66 @@ public class AAA {
             if (ethPkt == null) {
                 return;
             }
-            // identify if incoming packet comes from supplicant (EAP) or RADIUS
-            switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
-                case EAPOL:
-                    handleSupplicantPacket(context.inPacket());
-                    break;
-                case IPV4:
-                    IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
-                    Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
-                    Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress);
-                    if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
-                        // TODO: check for port as well when it's configurable
-                        UDP udpPacket = (UDP) ipv4Packet.getPayload();
-
-                        byte[] datagram = udpPacket.getPayload().serialize();
-                        RADIUS radiusPacket;
-                        try {
+            try {
+                // identify if incoming packet comes from supplicant (EAP) or RADIUS
+                switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
+                    case EAPOL:
+                        handleSupplicantPacket(context.inPacket());
+                        break;
+                    case IPV4:
+                        IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
+                        Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
+                        Ip4Address radiusIp4Address = Ip4Address.valueOf(radiusIpAddress);
+                        if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
+                            // TODO: check for port as well when it's configurable
+                            UDP udpPacket = (UDP) ipv4Packet.getPayload();
+
+                            byte[] datagram = udpPacket.getPayload().serialize();
+                            RADIUS radiusPacket;
                             radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
-                        } catch (DeserializationException e) {
-                            log.warn("Unable to deserialize RADIUS packet:", e);
-                            return;
+                            handleRadiusPacket(radiusPacket);
                         }
-                        handleRadiusPacket(radiusPacket);
-                    }
-                    break;
-                default:
-                    return;
+
+                        break;
+                    default:
+                        log.trace("Skipping Ethernet packet type {}",
+                                  EthType.EtherType.lookup(ethPkt.getEtherType()));
+                }
+            } catch (DeserializationException | StateMachineException e) {
+                log.warn("Unable to process RADIUS packet:", e);
             }
         }
 
+        /**
+         * Creates and initializes common fields of a RADIUS packet.
+         *
+         * @param identifier RADIUS identifier
+         * @param eapPacket  EAP packet
+         * @return RADIUS packet
+         */
+        private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) {
+            RADIUS radiusPayload =
+                    new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
+                               eapPacket.getIdentifier());
+            radiusPayload.setIdentifier(identifier);
+            radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
+                                       eapPacket.getData());
+
+            radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
+                                       AAA.this.nasIpAddress.getAddress());
+
+            radiusPayload.encapsulateMessage(eapPacket);
+            radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+
+            return radiusPayload;
+        }
 
         /**
          * Handles PAE packets (supplicant).
          *
          * @param inPacket Ethernet packet coming from the supplicant
          */
-        private void handleSupplicantPacket(InboundPacket inPacket) {
+        private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException {
             Ethernet ethPkt = inPacket.parsed();
             // Where does it come from?
             MacAddress srcMAC = ethPkt.getSourceMAC();
@@ -351,114 +314,83 @@ public class AAA {
             DeviceId deviceId = inPacket.receivedFrom().deviceId();
             PortNumber portNumber = inPacket.receivedFrom().port();
             String sessionId = deviceId.toString() + portNumber.toString();
-            StateMachine stateMachine = getStateMachine(sessionId);
+            StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
+            if (stateMachine == null) {
+                stateMachine = new StateMachine(sessionId, voltTenantService);
+            }
+
 
             EAPOL eapol = (EAPOL) ethPkt.getPayload();
 
             switch (eapol.getEapolType()) {
                 case EAPOL.EAPOL_START:
-                    try {
-                        stateMachine.start();
-                        stateMachine.supplicantConnectpoint = inPacket.receivedFrom();
-
-                        //send an EAP Request/Identify to the supplicant
-                        EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null);
-                        Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
-                                                          ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
-                                                          eapPayload);
-                        stateMachine.supplicantAddress = srcMAC;
-                        stateMachine.vlanId = ethPkt.getVlanID();
-
-                        this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
-                    } catch (StateMachineException e) {
-                        e.printStackTrace();
-                    }
+                    stateMachine.start();
+                    stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom());
+
+                    //send an EAP Request/Identify to the supplicant
+                    EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
+                    Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
+                                                      ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
+                                                      eapPayload);
+                    stateMachine.setSupplicantAddress(srcMAC);
+                    stateMachine.setVlanId(ethPkt.getVlanID());
+
+                    this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
 
                     break;
                 case EAPOL.EAPOL_PACKET:
-                    //check if this is a Response/Identify or  a Response/TLS
+                    RADIUS radiusPayload;
+                    // check if this is a Response/Identify or  a Response/TLS
                     EAP eapPacket = (EAP) eapol.getPayload();
 
                     byte dataType = eapPacket.getDataType();
                     switch (dataType) {
+
                         case EAP.ATTR_IDENTITY:
-                            try {
-                                //request id access to RADIUS
-                                RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
-                                                                  eapPacket.getIdentifier());
-                                radiusPayload.setIdentifier(stateMachine.getIdentifier());
-                                radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
-                                                           eapPacket.getData());
-                                stateMachine.setUsername(eapPacket.getData());
-                                radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
-                                                           AAA.this.parsedNasIpAddress.getAddress());
-
-                                radiusPayload.encapsulateMessage(eapPacket);
-
-                                // set Request Authenticator in StateMachine
-                                stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
-                                radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
-                                sendRadiusMessage(radiusPayload);
+                            // request id access to RADIUS
+                            stateMachine.setUsername(eapPacket.getData());
 
-                                //change the state to "PENDING"
-                                stateMachine.requestAccess();
-                            } catch (StateMachineException e) {
-                                e.printStackTrace();
-                            }
+                            radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
+
+                            // set Request Authenticator in StateMachine
+                            stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
+                            sendRadiusMessage(radiusPayload);
+
+                            // change the state to "PENDING"
+                            stateMachine.requestAccess();
                             break;
                         case EAP.ATTR_MD5:
-                            //verify if the EAP identifier corresponds to the challenge identifier from the client state
-                            //machine.
-                            if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) {
+                            // verify if the EAP identifier corresponds to the
+                            // challenge identifier from the client state
+                            // machine.
+                            if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
                                 //send the RADIUS challenge response
-                                RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
-                                                                  eapPacket.getIdentifier());
-                                radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier());
-                                radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
-                                                           stateMachine.getUsername());
-                                radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
-                                                           AAA.this.parsedNasIpAddress.getAddress());
-
-                                radiusPayload.encapsulateMessage(eapPacket);
+                                radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket);
 
                                 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
-                                                           stateMachine.getChallengeState());
-                                radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+                                                           stateMachine.challengeState());
                                 sendRadiusMessage(radiusPayload);
                             }
                             break;
                         case EAP.ATTR_TLS:
-                            try {
-                                //request id access to RADIUS
-                                RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
-                                                                  eapPacket.getIdentifier());
-                                radiusPayload.setIdentifier(stateMachine.getIdentifier());
-                                radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
-                                                           stateMachine.getUsername());
-                                radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
-                                                           AAA.this.parsedNasIpAddress.getAddress());
-
-                                radiusPayload.encapsulateMessage(eapPacket);
+                            // request id access to RADIUS
+                            radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
 
-                                radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
-                                                           stateMachine.getChallengeState());
-                                stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
+                            radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
+                                                       stateMachine.challengeState());
+                            stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
 
-                                radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+                            sendRadiusMessage(radiusPayload);
+                            // TODO: this gets called on every fragment, should only be called at TLS-Start
+                            stateMachine.requestAccess();
 
-                                sendRadiusMessage(radiusPayload);
-                                // TODO: this gets called on every fragment, should only be called at TLS-Start
-                                stateMachine.requestAccess();
-                            } catch (StateMachineException e) {
-                                e.printStackTrace();
-                            }
                             break;
                         default:
                             return;
                     }
                     break;
                 default:
-                    return;
+                    log.trace("Skipping EAPOL message {}", eapol.getEapolType());
             }
         }
 
@@ -467,99 +399,46 @@ public class AAA {
          *
          * @param radiusPacket RADIUS packet coming from the RADIUS server.
          */
-        private void handleRadiusPacket(RADIUS radiusPacket) {
-            StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier());
+        private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
+            StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
             if (stateMachine == null) {
                 log.error("Invalid session identifier, exiting...");
                 return;
             }
 
-            EAP eapPayload = new EAP();
-            Ethernet eth = null;
+            EAP eapPayload;
+            Ethernet eth;
             switch (radiusPacket.getCode()) {
                 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
                     byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
                     eapPayload = radiusPacket.decapsulateMessage();
                     stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
-                    eth = buildEapolResponse(stateMachine.supplicantAddress,
-                                             MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
+                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                                             MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
                                              eapPayload);
-                    this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
+                    this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
                     break;
                 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
-                    try {
-                        //send an EAPOL - Success to the supplicant.
-                        byte[] eapMessage =
-                                radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
-                        eapPayload = new EAP();
-                        eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
-                        eth = buildEapolResponse(stateMachine.supplicantAddress,
-                                                 MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET,
-                                                 eapPayload);
-                        this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint);
-
-                        stateMachine.authorizeAccess();
-                    } catch (StateMachineException e) {
-                        e.printStackTrace();
-                    }
+                    //send an EAPOL - Success to the supplicant.
+                    byte[] eapMessage =
+                            radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
+                    eapPayload = new EAP();
+                    eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
+                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                                             MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
+                                             eapPayload);
+                    this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+
+                    stateMachine.authorizeAccess();
                     break;
                 case RADIUS.RADIUS_CODE_ACCESS_REJECT:
-                    try {
-                        stateMachine.denyAccess();
-                    } catch (StateMachineException e) {
-                        e.printStackTrace();
-                    }
+                    stateMachine.denyAccess();
                     break;
                 default:
                     log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
             }
         }
 
-        private StateMachine getStateMachineById(byte identifier) {
-            StateMachine stateMachine = null;
-            Set stateMachineSet = stateMachineMap.entrySet();
-
-            synchronized (stateMachineMap) {
-                Iterator itr = stateMachineSet.iterator();
-                while (itr.hasNext()) {
-                    Map.Entry entry = (Map.Entry) itr.next();
-                    stateMachine = (StateMachine) entry.getValue();
-                    if (identifier == stateMachine.getIdentifier()) {
-                        //the state machine has already been created for this session session
-                        stateMachine = (StateMachine) entry.getValue();
-                        break;
-                    }
-                }
-            }
-
-            return stateMachine;
-        }
-
-        private StateMachine getStateMachine(String sessionId) {
-            StateMachine stateMachine = null;
-            Set stateMachineSet = stateMachineMap.entrySet();
-
-            synchronized (stateMachineMap) {
-                Iterator itr = stateMachineSet.iterator();
-                while (itr.hasNext()) {
-
-                    Map.Entry entry = (Map.Entry) itr.next();
-                    if (sessionId.equals(entry.getKey())) {
-                        //the state machine has already been created for this session session
-                        stateMachine = (StateMachine) entry.getValue();
-                        break;
-                    }
-                }
-            }
-
-            if (stateMachine == null) {
-                stateMachine = new StateMachine(sessionId, voltTenantService);
-                stateMachineMap.put(sessionId, stateMachine);
-            }
-
-            return stateMachine;
-        }
-
         private void sendRadiusMessage(RADIUS radiusMessage) {
             Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
             Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
@@ -576,12 +455,12 @@ public class AAA {
             IPv4 ip4Packet = new IPv4();
             Ethernet ethPkt = new Ethernet();
             radiusMessage.setParent(udp);
-            udp.setDestinationPort((short) 1812);
-            udp.setSourcePort((short) 1812); // TODO: make this configurable
+            udp.setDestinationPort(radiusServerPort);
+            udp.setSourcePort(radiusServerPort);
             udp.setPayload(radiusMessage);
             udp.setParent(ip4Packet);
-            ip4Packet.setSourceAddress(AAA.this.nasIpAddress);
-            ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress);
+            ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress());
+            ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress());
             ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
             ip4Packet.setPayload(udp);
             ip4Packet.setParent(ethPkt);
@@ -591,7 +470,7 @@ public class AAA {
             ethPkt.setPayload(ip4Packet);
 
             TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build();
+                    .setOutput(PortNumber.portNumber(radiusPort)).build();
             OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
                                                               treatment, ByteBuffer.wrap(ethPkt.serialize()));
             packetService.emit(packet);
@@ -613,4 +492,59 @@ public class AAA {
 
     }
 
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        /**
+         * Reconfigures the DHCP Server according to the configuration parameters passed.
+         *
+         * @param cfg configuration object
+         */
+        private void reconfigureNetwork(AAAConfig cfg) {
+            AAAConfig newCfg;
+            if (cfg == null) {
+                newCfg = new AAAConfig();
+            } else {
+                newCfg = cfg;
+            }
+            if (newCfg.nasIp() != null) {
+                nasIpAddress = newCfg.nasIp();
+            }
+            if (newCfg.radiusIp() != null) {
+                radiusIpAddress = newCfg.radiusIp();
+            }
+            if (newCfg.radiusMac() != null) {
+                radiusMacAddress = newCfg.radiusMac();
+            }
+            if (newCfg.nasMac() != null) {
+                nasMacAddress = newCfg.nasMac();
+            }
+            if (newCfg.radiusSecret() != null) {
+                radiusSecret = newCfg.radiusSecret();
+            }
+            if (newCfg.radiusSwitch() != null) {
+                radiusSwitch = newCfg.radiusSwitch();
+            }
+            if (newCfg.radiusPort() != -1) {
+                radiusPort = newCfg.radiusPort();
+            }
+            if (newCfg.radiusServerUDPPort() != -1) {
+                radiusServerPort = newCfg.radiusServerUDPPort();
+            }
+        }
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+
+            if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+                    event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+                    event.configClass().equals(AAAConfig.class)) {
+
+                AAAConfig cfg = netCfgService.getConfig(appId, AAAConfig.class);
+                reconfigureNetwork(cfg);
+                log.info("Reconfigured");
+            }
+        }
+    }
+
+
 }
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java
new file mode 100644 (file)
index 0000000..c18d2bf
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.aaa;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.basics.BasicElementConfig;
+
+/**
+ * Network config for the AAA app.
+ */
+public class AAAConfig extends Config<ApplicationId> {
+
+    private static final String RADIUS_IP = "radiusIp";
+    private static final String RADIUS_SERVER_PORT = "1812";
+    private static final String RADIUS_MAC = "radiusMac";
+    private static final String NAS_IP = "nasIp";
+    private static final String NAS_MAC = "nasMac";
+    private static final String RADIUS_SECRET = "radiusSecret";
+    private static final String RADIUS_SWITCH = "radiusSwitch";
+    private static final String RADIUS_PORT = "radiusPort";
+
+    // RADIUS server IP address
+    protected static final String DEFAULT_RADIUS_IP = "192.168.1.10";
+
+    // RADIUS MAC address
+    protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10";
+
+    // NAS IP address
+    protected static final String DEFAULT_NAS_IP = "192.168.1.11";
+
+    // NAS MAC address
+    protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01";
+
+    // RADIUS server shared secret
+    protected static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
+
+    // Radius Switch Id
+    protected static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9";
+
+    // Radius Port Number
+    protected static final String DEFAULT_RADIUS_PORT = "129";
+
+    // Radius Server UDP Port Number
+    protected static final String DEFAULT_RADIUS_SERVER_PORT = "1812";
+
+    /**
+     * Gets the value of a string property, protecting for an empty
+     * JSON object.
+     *
+     * @param name name of the property
+     * @param defaultValue default value if none has been specified
+     * @return String value if one os found, default value otherwise
+     */
+    private String getStringProperty(String name, String defaultValue) {
+        if (object == null) {
+            return defaultValue;
+        }
+        return get(name, defaultValue);
+    }
+
+    /**
+     * Returns the NAS ip.
+     *
+     * @return ip address or null if not set
+     */
+    public InetAddress nasIp() {
+        try {
+            return InetAddress.getByName(getStringProperty(NAS_IP, DEFAULT_NAS_IP));
+        } catch (UnknownHostException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Sets the NAS ip.
+     *
+     * @param ip new ip address; null to clear
+     * @return self
+     */
+    public BasicElementConfig nasIp(String ip) {
+        return (BasicElementConfig) setOrClear(NAS_IP, ip);
+    }
+
+    /**
+     * Returns the RADIUS server ip.
+     *
+     * @return ip address or null if not set
+     */
+    public InetAddress radiusIp() {
+        try {
+            return InetAddress.getByName(getStringProperty(RADIUS_IP, DEFAULT_RADIUS_IP));
+        } catch (UnknownHostException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Sets the RADIUS server ip.
+     *
+     * @param ip new ip address; null to clear
+     * @return self
+     */
+    public BasicElementConfig radiusIp(String ip) {
+        return (BasicElementConfig) setOrClear(RADIUS_IP, ip);
+    }
+
+    /**
+     * Returns the RADIUS MAC address.
+     *
+     * @return mac address or null if not set
+     */
+    public String radiusMac() {
+        return getStringProperty(RADIUS_MAC, DEFAULT_RADIUS_MAC);
+    }
+
+    /**
+     * Sets the RADIUS MAC address.
+     *
+     * @param mac new MAC address; null to clear
+     * @return self
+     */
+    public BasicElementConfig radiusMac(String mac) {
+        return (BasicElementConfig) setOrClear(RADIUS_MAC, mac);
+    }
+
+    /**
+     * Returns the RADIUS MAC address.
+     *
+     * @return mac address or null if not set
+     */
+    public String nasMac() {
+        return getStringProperty(NAS_MAC, DEFAULT_NAS_MAC);
+    }
+
+    /**
+     * Sets the RADIUS MAC address.
+     *
+     * @param mac new MAC address; null to clear
+     * @return self
+     */
+    public BasicElementConfig nasMac(String mac) {
+        return (BasicElementConfig) setOrClear(NAS_MAC, mac);
+    }
+
+    /**
+     * Returns the RADIUS secret.
+     *
+     * @return radius secret or null if not set
+     */
+    public String radiusSecret() {
+        return getStringProperty(RADIUS_SECRET, DEFAULT_RADIUS_SECRET);
+    }
+
+    /**
+     * Sets the RADIUS secret.
+     *
+     * @param secret new MAC address; null to clear
+     * @return self
+     */
+    public BasicElementConfig radiusSecret(String secret) {
+        return (BasicElementConfig) setOrClear(RADIUS_SECRET, secret);
+    }
+
+    /**
+     * Returns the ID of the RADIUS switch.
+     *
+     * @return radius switch ID or null if not set
+     */
+    public String radiusSwitch() {
+        return getStringProperty(RADIUS_SWITCH, DEFAULT_RADIUS_SWITCH);
+    }
+
+    /**
+     * Sets the ID of the RADIUS switch.
+     *
+     * @param switchId new RADIUS switch ID; null to clear
+     * @return self
+     */
+    public BasicElementConfig radiusSwitch(String switchId) {
+        return (BasicElementConfig) setOrClear(RADIUS_SWITCH, switchId);
+    }
+
+    /**
+     * Returns the RADIUS port.
+     *
+     * @return radius port or null if not set
+     */
+    public long radiusPort() {
+        return Integer.parseInt(getStringProperty(RADIUS_PORT, DEFAULT_RADIUS_PORT));
+    }
+
+    /**
+     * Sets the RADIUS port.
+     *
+     * @param port new RADIUS port; null to clear
+     * @return self
+     */
+    public BasicElementConfig radiusPort(long port) {
+        return (BasicElementConfig) setOrClear(RADIUS_PORT, port);
+    }
+
+    /**
+     * Returns the RADIUS server UDP port.
+     *
+     * @return radius server UDP port.
+     */
+    public short radiusServerUDPPort() {
+        return Short.parseShort(getStringProperty(RADIUS_SERVER_PORT,
+                                                  DEFAULT_RADIUS_SERVER_PORT));
+    }
+
+    /**
+     * Sets the RADIUS port.
+     *
+     * @param port new RADIUS UDP port; -1 to clear
+     * @return self
+     */
+    public BasicElementConfig radiusServerUDPPort(short port) {
+        return (BasicElementConfig) setOrClear(RADIUS_SERVER_PORT, (long) port);
+    }
+
+}
index 60959ad..84f6924 100644 (file)
 
 package org.onosproject.aaa;
 
+import java.util.BitSet;
+import java.util.Map;
+
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.xosintegration.VoltTenant;
 import org.onosproject.xosintegration.VoltTenantService;
 import org.slf4j.Logger;
 
-import java.util.BitSet;
+import com.google.common.collect.Maps;
 
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -58,9 +61,9 @@ class StateMachine {
     private byte[] requestAuthenticator;
 
     // Supplicant connectivity info
-    protected ConnectPoint supplicantConnectpoint;
-    protected MacAddress supplicantAddress;
-    protected short vlanId;
+    private ConnectPoint supplicantConnectpoint;
+    private MacAddress supplicantAddress;
+    private short vlanId;
 
     private String sessionId = null;
 
@@ -109,8 +112,28 @@ class StateMachine {
 
     private int currentState = STATE_IDLE;
 
+    // Maps of state machines. Each state machine is represented by an
+    // unique identifier on the switch: dpid + port number
+    private static Map<String, StateMachine> sessionIdMap;
+    private static Map<Integer, StateMachine> identifierMap;
 
-    /**
+    public static void initializeMaps() {
+        sessionIdMap = Maps.newConcurrentMap();
+        identifierMap = Maps.newConcurrentMap();
+    }
+
+    public static void destroyMaps() {
+        sessionIdMap = null;
+        identifierMap = null;
+    }
+
+    public static StateMachine lookupStateMachineById(byte identifier) {
+        return identifierMap.get((int) identifier);
+    }
+
+    public static StateMachine lookupStateMachineBySessionId(String sessionId) {
+        return sessionIdMap.get(sessionId);
+    }    /**
      * State Machine Constructor.
      *
      * @param sessionId   session Id represented by the switch dpid +  port number
@@ -120,15 +143,69 @@ class StateMachine {
         log.info("Creating a new state machine for {}", sessionId);
         this.sessionId = sessionId;
         this.voltService = voltService;
+        sessionIdMap.put(sessionId, this);
+    }
+
+    /**
+     * Gets the connect point for the supplicant side.
+     *
+     * @return supplicant connect point
+     */
+    public ConnectPoint supplicantConnectpoint() {
+        return supplicantConnectpoint;
+    }
+
+    /**
+     * Sets the supplicant side connect point.
+     *
+     * @param supplicantConnectpoint supplicant select point.
+     */
+    public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) {
+        this.supplicantConnectpoint = supplicantConnectpoint;
+    }
+
+    /**
+     * Gets the MAC address of the supplicant.
+     *
+     * @return supplicant MAC address
+     */
+    public MacAddress supplicantAddress() {
+        return supplicantAddress;
+    }
+
+    /**
+     * Sets the supplicant MAC address.
+     *
+     * @param supplicantAddress new supplicant MAC address
+     */
+    public void setSupplicantAddress(MacAddress supplicantAddress) {
+        this.supplicantAddress = supplicantAddress;
+    }
+
+    /**
+     * Gets the client's Vlan ID.
+     *
+     * @return client vlan ID
+     */
+    public short vlanId() {
+        return vlanId;
+    }
 
+    /**
+     * Sets the client's vlan ID.
+     *
+     * @param vlanId new client vlan ID
+     */
+    public void setVlanId(short vlanId) {
+        this.vlanId = vlanId;
     }
 
     /**
-     * Get the client id that is requesting for access.
+     * Gets the client id that is requesting for access.
      *
      * @return The client id.
      */
-    public String getSessionId() {
+    public String sessionId() {
         return this.sessionId;
     }
 
@@ -137,7 +214,7 @@ class StateMachine {
      */
     private void createIdentifier() throws StateMachineException {
         log.debug("Creating Identifier.");
-        int index = -1;
+        int index;
 
         try {
             //find the first available spot for identifier assignment
@@ -178,11 +255,11 @@ class StateMachine {
     }
 
     /**
-     * Get the challenge EAP identifier set by the RADIUS.
+     * Gets the challenge EAP identifier set by the RADIUS.
      *
      * @return The challenge EAP identifier.
      */
-    protected byte getChallengeIdentifier() {
+    protected byte challengeIdentifier() {
         return this.challengeIdentifier;
     }
 
@@ -198,11 +275,11 @@ class StateMachine {
     }
 
     /**
-     * Get the challenge state set by the RADIUS.
+     * Gets the challenge state set by the RADIUS.
      *
      * @return The challenge state.
      */
-    protected byte[] getChallengeState() {
+    protected byte[] challengeState() {
         return this.challengeState;
     }
 
@@ -217,16 +294,16 @@ class StateMachine {
 
 
     /**
-     * Get the username.
+     * Gets the username.
      *
      * @return The requestAuthenticator.
      */
-    protected byte[] getReqeustAuthenticator() {
+    protected byte[] requestAuthenticator() {
         return this.requestAuthenticator;
     }
 
     /**
-     * Set the username.
+     * Sets the authenticator.
      *
      * @param authenticator The username sent to the RADIUS upon access request.
      */
@@ -236,11 +313,11 @@ class StateMachine {
 
 
     /**
-     * Get the username.
+     * Gets the username.
      *
      * @return The username.
      */
-    protected byte[] getUsername() {
+    protected byte[] username() {
         return this.username;
     }
 
@@ -249,7 +326,7 @@ class StateMachine {
      *
      * @return The state machine identifier.
      */
-    public byte getIdentifier() {
+    public byte identifier() {
         return (byte) this.identifier;
     }
 
@@ -267,7 +344,7 @@ class StateMachine {
     /**
      * Move to the next state.
      *
-     * @param msg
+     * @param msg message
      */
     private void next(int msg) {
         currentState = transition[currentState][msg];
@@ -280,14 +357,11 @@ class StateMachine {
      * @throws StateMachineException if authentication protocol is violated
      */
     public void start() throws StateMachineException {
-        try {
-            states[currentState].start();
-            //move to the next state
-            next(TRANSITION_START);
-            createIdentifier();
-        } catch (StateMachineInvalidTransitionException e) {
-            e.printStackTrace();
-        }
+        states[currentState].start();
+        //move to the next state
+        next(TRANSITION_START);
+        createIdentifier();
+        identifierMap.put(identifier, this);
     }
 
     /**
@@ -297,13 +371,9 @@ class StateMachine {
      * @throws StateMachineException if authentication protocol is violated
      */
     public void requestAccess() throws StateMachineException {
-        try {
-            states[currentState].requestAccess();
-            //move to the next state
-            next(TRANSITION_REQUEST_ACCESS);
-        } catch (StateMachineInvalidTransitionException e) {
-            e.printStackTrace();
-        }
+        states[currentState].requestAccess();
+        //move to the next state
+        next(TRANSITION_REQUEST_ACCESS);
     }
 
     /**
@@ -313,27 +383,22 @@ class StateMachine {
      * @throws StateMachineException if authentication protocol is violated
      */
     public void authorizeAccess() throws StateMachineException {
-        try {
-            states[currentState].radiusAccepted();
-            //move to the next state
-            next(TRANSITION_AUTHORIZE_ACCESS);
-
-            if (voltService != null) {
-                voltService.addTenant(
-                        VoltTenant.builder()
-                                .withHumanReadableName("VCPE-" + this.identifier)
-                                .withId(this.identifier)
-                                .withProviderService(1)
-                                .withServiceSpecificId(String.valueOf(this.identifier))
-                                .withPort(this.supplicantConnectpoint)
-                                .withVlanId(String.valueOf(this.vlanId)).build());
-            }
-
-            deleteIdentifier();
-        } catch (StateMachineInvalidTransitionException e) {
-            e.printStackTrace();
+        states[currentState].radiusAccepted();
+        //move to the next state
+        next(TRANSITION_AUTHORIZE_ACCESS);
+
+        if (voltService != null) {
+            voltService.addTenant(
+                    VoltTenant.builder()
+                            .withHumanReadableName("VCPE-" + this.identifier)
+                            .withId(this.identifier)
+                            .withProviderService(1)
+                            .withServiceSpecificId(String.valueOf(this.identifier))
+                            .withPort(this.supplicantConnectpoint)
+                            .withVlanId(String.valueOf(this.vlanId)).build());
         }
 
+        deleteIdentifier();
     }
 
     /**
@@ -343,14 +408,10 @@ class StateMachine {
      * @throws StateMachineException if authentication protocol is violated
      */
     public void denyAccess() throws StateMachineException {
-        try {
-            states[currentState].radiusDenied();
-            //move to the next state
-            next(TRANSITION_DENY_ACCESS);
-            deleteIdentifier();
-        } catch (StateMachineInvalidTransitionException e) {
-            e.printStackTrace();
-        }
+        states[currentState].radiusDenied();
+        //move to the next state
+        next(TRANSITION_DENY_ACCESS);
+        deleteIdentifier();
     }
 
     /**
@@ -360,141 +421,117 @@ class StateMachine {
      * @throws StateMachineException if authentication protocol is violated
      */
     public void logoff() throws StateMachineException {
-        try {
-            states[currentState].logoff();
-            //move to the next state
-            next(TRANSITION_LOGOFF);
-        } catch (StateMachineInvalidTransitionException e) {
-            e.printStackTrace();
-        }
+        states[currentState].logoff();
+        //move to the next state
+        next(TRANSITION_LOGOFF);
     }
 
     /**
-     * Get the current state.
+     * Gets the current state.
      *
      * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
      * STATE_UNAUTHORIZED.
      */
-    public int getState() {
+    public int state() {
         return currentState;
     }
 
-
+    @Override
     public String toString() {
         return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
                 ("state: " + this.currentState);
     }
-}
 
-// FIXME: A source file should contain no more than one top-level entity!
+    abstract class State {
+        private final Logger log = getLogger(getClass());
 
-abstract class State {
-    private final Logger log = getLogger(getClass());
-
-    private String name = "State";
+        private String name = "State";
 
-    public void start() throws StateMachineInvalidTransitionException {
-        log.warn("START transition from this state is not allowed.");
-    }
+        public void start() throws StateMachineInvalidTransitionException {
+            log.warn("START transition from this state is not allowed.");
+        }
 
-    public void requestAccess() throws StateMachineInvalidTransitionException {
-        log.warn("REQUEST ACCESS transition from this state is not allowed.");
-    }
+        public void requestAccess() throws StateMachineInvalidTransitionException {
+            log.warn("REQUEST ACCESS transition from this state is not allowed.");
+        }
 
-    public void radiusAccepted() throws StateMachineInvalidTransitionException {
-        log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
-    }
+        public void radiusAccepted() throws StateMachineInvalidTransitionException {
+            log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
+        }
 
-    public void radiusDenied() throws StateMachineInvalidTransitionException {
-        log.warn("DENY ACCESS transition from this state is not allowed.");
-    }
+        public void radiusDenied() throws StateMachineInvalidTransitionException {
+            log.warn("DENY ACCESS transition from this state is not allowed.");
+        }
 
-    public void logoff() throws StateMachineInvalidTransitionException {
-        log.warn("LOGOFF transition from this state is not allowed.");
+        public void logoff() throws StateMachineInvalidTransitionException {
+            log.warn("LOGOFF transition from this state is not allowed.");
+        }
     }
-}
 
-/**
- * Idle state: supplicant is logged of from the network.
- */
-class Idle extends State {
-    private final Logger log = getLogger(getClass());
-    private String name = "IDLE_STATE";
+    /**
    * Idle state: supplicant is logged of from the network.
    */
+    class Idle extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "IDLE_STATE";
 
-    public void start() {
-        log.info("Moving from IDLE state to STARTED state.");
+        public void start() {
+            log.info("Moving from IDLE state to STARTED state.");
+        }
     }
-}
 
-/**
- * Started state: supplicant has entered the network and informed the authenticator.
- */
-class Started extends State {
-    private final Logger log = getLogger(getClass());
-    private String name = "STARTED_STATE";
+    /**
    * Started state: supplicant has entered the network and informed the authenticator.
    */
+    class Started extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "STARTED_STATE";
 
-    public void requestAccess() {
-        log.info("Moving from STARTED state to PENDING state.");
+        public void requestAccess() {
+            log.info("Moving from STARTED state to PENDING state.");
+        }
     }
-}
 
-/**
- * Pending state: supplicant has been identified by the authenticator but has not access yet.
- */
-class Pending extends State {
-    private final Logger log = getLogger(getClass());
-    private String name = "PENDING_STATE";
+    /**
    * Pending state: supplicant has been identified by the authenticator but has not access yet.
    */
+    class Pending extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "PENDING_STATE";
 
-    public void radiusAccepted() {
-        log.info("Moving from PENDING state to AUTHORIZED state.");
-    }
+        public void radiusAccepted() {
+            log.info("Moving from PENDING state to AUTHORIZED state.");
+        }
 
-    public void radiusDenied() {
-        log.info("Moving from PENDING state to UNAUTHORIZED state.");
+        public void radiusDenied() {
+            log.info("Moving from PENDING state to UNAUTHORIZED state.");
+        }
     }
-}
 
-/**
- * Authorized state: supplicant port has been accepted, access is granted.
- */
-class Authorized extends State {
-    private final Logger log = getLogger(getClass());
-    private String name = "AUTHORIZED_STATE";
+    /**
    * Authorized state: supplicant port has been accepted, access is granted.
    */
+    class Authorized extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "AUTHORIZED_STATE";
 
-    public void logoff() {
+        public void logoff() {
 
-        log.info("Moving from AUTHORIZED state to IDLE state.");
+            log.info("Moving from AUTHORIZED state to IDLE state.");
+        }
     }
-}
 
-/**
- * Unauthorized state: supplicant port has been rejected, access is denied.
- */
-class Unauthorized extends State {
-    private final Logger log = getLogger(getClass());
-    private String name = "UNAUTHORIZED_STATE";
+    /**
    * Unauthorized state: supplicant port has been rejected, access is denied.
    */
+    class Unauthorized extends State {
+        private final Logger log = getLogger(getClass());
+        private String name = "UNAUTHORIZED_STATE";
 
-    public void logoff() {
-        log.info("Moving from UNAUTHORIZED state to IDLE state.");
+        public void logoff() {
+            log.info("Moving from UNAUTHORIZED state to IDLE state.");
+        }
     }
-}
 
 
-/**
- * Exception for the State Machine.
- */
-class StateMachineException extends Exception {
-    public StateMachineException(String message) {
-        super(message);
-
-    }
-}
-
-/**
- * Exception raised when the transition from one state to another is invalid.
- */
-class StateMachineInvalidTransitionException extends StateMachineException {
-    public StateMachineInvalidTransitionException(String message) {
-        super(message);
-    }
 }
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java
new file mode 100644 (file)
index 0000000..d4a4da7
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015 AT&T Foundry
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.onosproject.aaa;
+
+/**
+ * Exception for the State Machine.
+ */
+class StateMachineException extends Exception {
+    public StateMachineException(String message) {
+        super(message);
+
+    }
+}
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java
new file mode 100644 (file)
index 0000000..9f41a34
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright 2015 AT&T Foundry
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.onosproject.aaa;
+
+/**
+ * Exception raised when the transition from one state to another is invalid.
+ */
+class StateMachineInvalidTransitionException extends StateMachineException {
+    public StateMachineInvalidTransitionException(String message) {
+        super(message);
+    }
+}
index 1b581ab..75a6033 100644 (file)
  */
 package org.onosproject.aaa;
 
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.EAP;
+import org.onlab.packet.EAPOL;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.RADIUS;
+import org.onlab.packet.RADIUSAttribute;
+import org.onlab.packet.UDP;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableSet;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.onosproject.net.NetTestTools.connectPoint;
 
 /**
  * Set of tests of the ONOS application component.
  */
 public class AAATest {
 
+    MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a");
+    MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
+
+    PacketProcessor packetProcessor;
     private AAA aaa;
+    List<Ethernet> savedPackets = new LinkedList<>();
+
+    /**
+     * Saves the given packet onto the saved packets list.
+     *
+     * @param eth packet to save
+     */
+    private void savePacket(Ethernet eth) {
+        savedPackets.add(eth);
+    }
+
+    /**
+     * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
+     */
+    private class MockPacketService extends PacketServiceAdapter {
+
+        @Override
+        public void addProcessor(PacketProcessor processor, int priority) {
+            packetProcessor = processor;
+        }
+
+        @Override
+        public void emit(OutboundPacket packet) {
+            try {
+                Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
+                                                                   0, packet.data().array().length);
+                savePacket(eth);
+            } catch (Exception e) {
+                fail(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Mocks the DefaultPacketContext.
+     */
+    private final class TestPacketContext extends DefaultPacketContext {
+
+        private TestPacketContext(long time, InboundPacket inPkt,
+                                  OutboundPacket outPkt, boolean block) {
+            super(time, inPkt, outPkt, block);
+        }
+
+        @Override
+        public void send() {
+            // We don't send anything out.
+        }
+    }
+
+    /**
+     * Mocks a host to allow locating the Radius server.
+     */
+    private static final class MockHost implements Host {
+        @Override
+        public HostId id() {
+            return null;
+        }
+
+        @Override
+        public MacAddress mac() {
+            return null;
+        }
+
+        @Override
+        public VlanId vlan() {
+            return VlanId.vlanId(VlanId.UNTAGGED);
+        }
+
+        @Override
+        public Set<IpAddress> ipAddresses() {
+            return null;
+        }
+
+        @Override
+        public HostLocation location() {
+            return null;
+        }
+
+        @Override
+        public Annotations annotations() {
+            return null;
+        }
+
+        @Override
+        public ProviderId providerId() {
+            return null;
+        }
+    }
+
+    /**
+     * Mocks the Host service.
+     */
+    private static final class MockHostService extends HostServiceAdapter {
+        @Override
+        public Set<Host> getHostsByIp(IpAddress ip) {
+            return ImmutableSet.of(new MockHost());
+        }
+    }
+
+    /**
+     * Mocks the network config registry.
+     */
+    @SuppressWarnings("unchecked")
+    private static final class TestNetworkConfigRegistry
+            extends NetworkConfigRegistryAdapter {
+        @Override
+        public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+            return (C) new AAAConfig();
+        }
+    }
+
+    /**
+     * Sends an Ethernet packet to the process method of the Packet Processor.
+     *
+     * @param reply Ethernet packet
+     */
+    private void sendPacket(Ethernet reply) {
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
+        InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
+                                                          reply,
+                                                          byteBuffer);
+
+        PacketContext context = new TestPacketContext(127L, inPacket, null, false);
+        packetProcessor.process(context);
+    }
+
+    /**
+     * Constructs an Ethernet packet containing a EAPOL_START Payload.
+     *
+     * @return Ethernet packet
+     */
+    private Ethernet constructSupplicantStartPacket() {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(clientMac.toBytes());
+        eth.setSourceMACAddress(serverMac.toBytes());
+        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        eth.setVlanID((short) 2);
+
+        EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 1, EAPOL.EAPOL_START, null);
+
+        //eapol header
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(EAPOL.EAPOL_START);
+        eapol.setPacketLength(eap.getLength());
+
+        //eap part
+        eapol.setPayload(eap);
+
+        eth.setPayload(eapol);
+        eth.setPad(true);
+        return eth;
+    }
+
+    /**
+     * Constructs an Ethernet packet containing identification payload.
+     *
+     * @return Ethernet packet
+     */
+    private Ethernet constructSupplicantIdentifyPacket(byte type) {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(clientMac.toBytes());
+        eth.setSourceMACAddress(serverMac.toBytes());
+        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        eth.setVlanID((short) 2);
+
+        String username = "user";
+        EAP eap = new EAP(EAP.REQUEST, (byte) 1, type,
+                          username.getBytes(Charsets.US_ASCII));
+        eap.setIdentifier((byte) 1);
+
+        // eapol header
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(EAPOL.EAPOL_PACKET);
+        eapol.setPacketLength(eap.getLength());
+
+        // eap part
+        eapol.setPayload(eap);
+
+        eth.setPayload(eapol);
+        eth.setPad(true);
+        return eth;
+    }
+
+    /**
+     * Constructs an Ethernet packet containing a RADIUS challenge
+     * packet.
+     *
+     * @param challengeCode code to use in challenge packet
+     * @param challengeType type to use in challenge packet
+     * @return Ethernet packet
+     */
+    private Ethernet constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(clientMac.toBytes());
+        eth.setSourceMACAddress(serverMac.toBytes());
+        eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort());
+        eth.setVlanID((short) 2);
+
+        IPv4 ipv4 = new IPv4();
+        ipv4.setProtocol(IPv4.PROTOCOL_UDP);
+        ipv4.setSourceAddress(aaa.radiusIpAddress.getHostAddress());
+
+        String challenge = "1234";
+
+        EAP eap = new EAP(challengeType, (byte) 1, challengeType,
+                          challenge.getBytes(Charsets.US_ASCII));
+        eap.setIdentifier((byte) 1);
+
+        RADIUS radius = new RADIUS();
+        radius.setCode(challengeCode);
+
+        radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
+                            challenge.getBytes(Charsets.US_ASCII));
 
+        radius.setPayload(eap);
+        radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
+                            eap.serialize());
+
+        UDP udp = new UDP();
+        udp.setPayload(radius);
+        ipv4.setPayload(udp);
+
+        eth.setPayload(ipv4);
+        eth.setPad(true);
+        return eth;
+    }
+
+    /**
+     * Sets up the services required by the AAA application.
+     */
     @Before
     public void setUp() {
-
+        aaa = new AAA();
+        aaa.netCfgService = new TestNetworkConfigRegistry();
+        aaa.coreService = new CoreServiceAdapter();
+        aaa.packetService = new MockPacketService();
+        aaa.hostService = new MockHostService();
+        aaa.activate();
     }
 
+    /**
+     * Tears down the AAA application.
+     */
     @After
     public void tearDown() {
+        aaa.deactivate();
+    }
+
+    /**
+     * Extracts the RADIUS packet from a packet sent by the supplicant.
+     *
+     * @param supplicantPacket packet sent by the supplicant
+     * @return RADIUS packet
+     * @throws DeserializationException if deserialization of the packet contents
+     *         fails.
+     */
+    private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket)
+            throws DeserializationException {
+        assertThat(supplicantPacket, notNullValue());
+        assertThat(supplicantPacket.getVlanID(), is(VlanId.UNTAGGED));
+        assertThat(supplicantPacket.getSourceMAC().toString(), is(aaa.nasMacAddress));
+        assertThat(supplicantPacket.getDestinationMAC().toString(), is(aaa.radiusMacAddress));
+
+        assertThat(supplicantPacket.getPayload(), instanceOf(IPv4.class));
+        IPv4 ipv4 = (IPv4) supplicantPacket.getPayload();
+        assertThat(ipv4, notNullValue());
+        assertThat(IpAddress.valueOf(ipv4.getSourceAddress()).toString(),
+                   is(aaa.nasIpAddress.getHostAddress()));
+        assertThat(IpAddress.valueOf(ipv4.getDestinationAddress()).toString(),
+                   is(aaa.radiusIpAddress.getHostAddress()));
+
+        assertThat(ipv4.getPayload(), instanceOf(UDP.class));
+        UDP udp = (UDP) ipv4.getPayload();
+        assertThat(udp, notNullValue());
+
+        assertThat(udp.getPayload(), instanceOf(Data.class));
+        Data data = (Data) udp.getPayload();
+        RADIUS radius = RADIUS.deserializer()
+                .deserialize(data.getData(), 0, data.getData().length);
+        assertThat(radius, notNullValue());
+        return radius;
     }
 
+    /**
+     * Checks the contents of a RADIUS packet being sent to the RADIUS server.
+     *
+     * @param radiusPacket packet to check
+     * @param code expected code
+     */
+    private void checkRadiusPacket(Ethernet radiusPacket, byte code) {
+        assertThat(radiusPacket.getVlanID(), is((short) 2));
+
+        // TODO: These address values seem wrong, but are produced by the current AAA implementation
+        assertThat(radiusPacket.getSourceMAC(), is(MacAddress.valueOf(1L)));
+        assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
+
+        assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class));
+        EAPOL eapol = (EAPOL) radiusPacket.getPayload();
+        assertThat(eapol, notNullValue());
+
+        assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
+        assertThat(eapol.getPayload(), instanceOf(EAP.class));
+        EAP eap = (EAP) eapol.getPayload();
+        assertThat(eap, notNullValue());
+        assertThat(eap.getCode(), is(code));
+    }
+
+    /**
+     * Fetches the sent packet at the given index. The requested packet
+     * must be the last packet on the list.
+     *
+     * @param index index into sent packets array
+     * @return packet
+     */
+    private Ethernet fetchPacket(int index) {
+        assertThat(savedPackets.size(), is(index + 1));
+        Ethernet eth = savedPackets.get(index);
+        assertThat(eth, notNullValue());
+        return eth;
+    }
+
+    /**
+     * Tests the authentication path through the AAA application.
+     *
+     * @throws DeserializationException if packed deserialization fails.
+     */
     @Test
-    public void basics() {
+    public void testAuthentication()  throws DeserializationException {
+
+        // Our session id will be the device ID ("of:1") with the port ("1") concatenated
+        String sessionId = "of:11";
+
+        //  (1) Supplicant start up
+
+        Ethernet startPacket = constructSupplicantStartPacket();
+        sendPacket(startPacket);
+
+        Ethernet responsePacket = fetchPacket(0);
+        checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY);
+
+        //  (2) Supplicant identify
+
+        Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY);
+        sendPacket(identifyPacket);
 
+        Ethernet radiusIdentifyPacket = fetchPacket(1);
+
+        RADIUS radiusAccessRequest = checkAndFetchRADIUSPacketFromSupplicant(radiusIdentifyPacket);
+        assertThat(radiusAccessRequest, notNullValue());
+        assertThat(radiusAccessRequest.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+        assertThat(new String(radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
+                   is("user"));
+
+        IpAddress nasIp =
+                IpAddress.valueOf(IpAddress.Version.INET,
+                                  radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP)
+                                          .getValue());
+        assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress()));
+
+        //  State machine should have been created by now
+
+        StateMachine stateMachine =
+                StateMachine.lookupStateMachineBySessionId(sessionId);
+        assertThat(stateMachine, notNullValue());
+        assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
+
+        // (3) RADIUS MD5 challenge
+
+        Ethernet radiusCodeAccessChallengePacket =
+                constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
+        sendPacket(radiusCodeAccessChallengePacket);
+
+        Ethernet radiusChallengeMD5Packet = fetchPacket(2);
+        checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5);
+
+        // (4) Supplicant MD5 response
+
+        Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5);
+        sendPacket(md5RadiusPacket);
+        Ethernet supplicantMD5ResponsePacket = fetchPacket(3);
+        RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket);
+        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1));
+        assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+
+        //  State machine should be in pending state
+
+        assertThat(stateMachine, notNullValue());
+        assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
+
+        // (5) RADIUS TLS Challenge
+
+        Ethernet radiusCodeAccessChallengeTLSPacket =
+                constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_TLS);
+        sendPacket(radiusCodeAccessChallengeTLSPacket);
+
+        Ethernet radiusChallengeTLSPacket = fetchPacket(4);
+        checkRadiusPacket(radiusChallengeTLSPacket, EAP.ATTR_TLS);
+
+        // (6) Supplicant TLS response
+
+        Ethernet tlsRadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_TLS);
+        sendPacket(tlsRadiusPacket);
+        Ethernet supplicantTLSResponsePacket = fetchPacket(5);
+        RADIUS responseTLSRadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantTLSResponsePacket);
+        assertThat(responseTLSRadiusPacket.getIdentifier(), is((byte) 0));
+        assertThat(responseTLSRadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+
+        // (7) RADIUS Success
+
+        Ethernet successPacket =
+                constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
+        sendPacket(successPacket);
+        Ethernet supplicantSuccessPacket = fetchPacket(6);
+
+        checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS);
+
+        //  State machine should be in authorized state
+
+        assertThat(stateMachine, notNullValue());
+        assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
     }
 
+    /**
+     * Tests the default configuration.
+     */
+    @Test
+    public void testConfig() {
+        assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP));
+        assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC));
+        assertThat(aaa.radiusIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_RADIUS_IP));
+        assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC));
+    }
 }
index 2fe44ab..04837e8 100644 (file)
@@ -21,6 +21,7 @@ import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import static org.junit.Assert.*;
 
 
 public class StateMachineTest {
@@ -30,6 +31,7 @@ public class StateMachineTest {
     public void setUp() {
         System.out.println("Set Up.");
         StateMachine.bitSet.clear();
+        StateMachine.initializeMaps();
         stateMachine = new StateMachine("session0", null);
     }
 
@@ -37,6 +39,7 @@ public class StateMachineTest {
     public void tearDown() {
         System.out.println("Tear Down.");
         StateMachine.bitSet.clear();
+        StateMachine.destroyMaps();
         stateMachine = null;
     }
 
@@ -46,19 +49,19 @@ public class StateMachineTest {
      */
     public void basic() throws StateMachineException {
         System.out.println("======= BASIC =======.");
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
 
         stateMachine.start();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
 
         stateMachine.requestAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
 
         stateMachine.authorizeAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
 
         stateMachine.logoff();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
     }
 
     @Test
@@ -68,19 +71,19 @@ public class StateMachineTest {
     public void testIdleState() throws StateMachineException {
         System.out.println("======= IDLE STATE TEST =======.");
         stateMachine.requestAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
 
         stateMachine.authorizeAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
 
         stateMachine.denyAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
 
         stateMachine.logoff();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
 
         stateMachine.start();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
     }
 
     @Test
@@ -92,19 +95,19 @@ public class StateMachineTest {
         stateMachine.start();
 
         stateMachine.authorizeAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
 
         stateMachine.denyAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
 
         stateMachine.logoff();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
 
         stateMachine.start();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED);
 
         stateMachine.requestAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
     }
 
     @Test
@@ -118,19 +121,19 @@ public class StateMachineTest {
         stateMachine.requestAccess();
 
         stateMachine.logoff();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
 
         stateMachine.start();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
 
         stateMachine.requestAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
 
         stateMachine.authorizeAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
 
         stateMachine.denyAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
     }
 
     @Test
@@ -144,19 +147,19 @@ public class StateMachineTest {
         stateMachine.requestAccess();
 
         stateMachine.logoff();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
 
         stateMachine.start();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
 
         stateMachine.requestAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING);
 
         stateMachine.denyAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
 
         stateMachine.authorizeAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
     }
 
     @Test
@@ -170,19 +173,19 @@ public class StateMachineTest {
         stateMachine.authorizeAccess();
 
         stateMachine.start();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
 
         stateMachine.requestAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
 
         stateMachine.authorizeAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
 
         stateMachine.denyAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED);
 
         stateMachine.logoff();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
     }
 
     @Test
@@ -196,27 +199,27 @@ public class StateMachineTest {
         stateMachine.denyAccess();
 
         stateMachine.start();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
 
         stateMachine.requestAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
 
         stateMachine.authorizeAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
 
         stateMachine.denyAccess();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED);
 
         stateMachine.logoff();
-        Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE);
+        Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE);
     }
 
 
     @Test
     public void testIdentifierAvailability() throws StateMachineException {
         System.out.println("======= IDENTIFIER TEST =======.");
-        byte identifier = stateMachine.getIdentifier();
-        System.out.println("State: " + stateMachine.getState());
+        byte identifier = stateMachine.identifier();
+        System.out.println("State: " + stateMachine.state());
         System.out.println("Identifier: " + Byte.toUnsignedInt(identifier));
         Assert.assertEquals(-1, identifier);
         stateMachine.start();
@@ -230,7 +233,7 @@ public class StateMachineTest {
         for (int i = 1; i <= 255; i++) {
                 StateMachine sm = new StateMachine("session" + i, null);
                 sm.start();
-                byte id = sm.getIdentifier();
+                byte id = sm.identifier();
                 Assert.assertEquals(i, Byte.toUnsignedInt(id));
                 if (i == 3) {
                     sm3 = sm;
@@ -244,27 +247,72 @@ public class StateMachineTest {
 
         //simulate the state machine for a specific session and logoff so we can free up a spot for an identifier
         //let's choose identifier 247 then we free up 3
+        Assert.assertNotNull(sm247);
         sm247.requestAccess();
         sm247.authorizeAccess();
         sm247.logoff();
-        sm247 = null;
 
+        Assert.assertNotNull(sm3);
         sm3.requestAccess();
         sm3.authorizeAccess();
         sm3.logoff();
-        sm3 = null;
 
         StateMachine otherSM3 = new StateMachine("session3b", null);
         otherSM3.start();
         otherSM3.requestAccess();
-        byte id3 = otherSM3.getIdentifier();
+        byte id3 = otherSM3.identifier();
         Assert.assertEquals(3, Byte.toUnsignedInt(id3));
 
         StateMachine otherSM247 = new StateMachine("session247b", null);
         otherSM247.start();
         otherSM247.requestAccess();
-        byte id247 = otherSM247.getIdentifier();
+        byte id247 = otherSM247.identifier();
         Assert.assertEquals(247, Byte.toUnsignedInt(id247));
+    }
 
+    @Test
+    public void testSessionIdLookups() {
+        String sessionId1 = "session1";
+        String sessionId2 = "session2";
+        String sessionId3 = "session3";
+
+        StateMachine machine1ShouldBeNull =
+                StateMachine.lookupStateMachineBySessionId(sessionId1);
+        assertNull(machine1ShouldBeNull);
+        StateMachine machine2ShouldBeNull =
+                StateMachine.lookupStateMachineBySessionId(sessionId2);
+        assertNull(machine2ShouldBeNull);
+
+        StateMachine stateMachine1 = new StateMachine(sessionId1, null);
+        StateMachine stateMachine2 = new StateMachine(sessionId2, null);
+
+        assertEquals(stateMachine1,
+                     StateMachine.lookupStateMachineBySessionId(sessionId1));
+        assertEquals(stateMachine2,
+                     StateMachine.lookupStateMachineBySessionId(sessionId2));
+        assertNull(StateMachine.lookupStateMachineBySessionId(sessionId3));
+    }
+
+    @Test
+    public void testIdentifierLookups() throws StateMachineException {
+        String sessionId1 = "session1";
+        String sessionId2 = "session2";
+
+        StateMachine machine1ShouldBeNull =
+                StateMachine.lookupStateMachineById((byte) 1);
+        assertNull(machine1ShouldBeNull);
+        StateMachine machine2ShouldBeNull =
+                StateMachine.lookupStateMachineById((byte) 2);
+        assertNull(machine2ShouldBeNull);
+
+        StateMachine stateMachine1 = new StateMachine(sessionId1, null);
+        stateMachine1.start();
+        StateMachine stateMachine2 = new StateMachine(sessionId2, null);
+        stateMachine2.start();
+
+        assertEquals(stateMachine1,
+                     StateMachine.lookupStateMachineById(stateMachine1.identifier()));
+        assertEquals(stateMachine2,
+                     StateMachine.lookupStateMachineById(stateMachine2.identifier()));
     }
 }
index 54dee43..454ac7e 100644 (file)
@@ -18,7 +18,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
     <url>http://onosproject.org</url>
 
     <properties>
-        <onos.version>1.4.0-SNAPSHOT</onos.version>
         <onos.app.name>org.onosproject.acl</onos.app.name>
         <onos.app.origin>DLUT</onos.app.origin>
-        <web.context>/onos/acl</web.context>
+
+        <web.context>/onos/v1/acl</web.context>
         <api.version>1.0.0</api.version>
         <api.title>ONOS ACL Application REST API</api.title>
         <api.description>
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onlab-junit</artifactId>
-            <version>${onos.version}</version>
         </dependency>
 
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-rest</artifactId>
-            <version>${onos.version}</version>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-rest</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-core-serializers</artifactId>
-            <version>${onos.version}</version>
+            <version>${project.version}</version>
         </dependency>
 
         <dependency>
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onlab-misc</artifactId>
-            <version>${onos.version}</version>
         </dependency>
     </dependencies>
 
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
new file mode 100644 (file)
index 0000000..8c91da4
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Prefix;
+import org.onosproject.core.IdGenerator;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * ACL rule class.
+ */
+public final class AclRule {
+
+    private final RuleId id;
+
+    private final Ip4Prefix srcIp;
+    private final Ip4Prefix dstIp;
+    private final byte ipProto;
+    private final short dstTpPort;
+    private final Action action;
+
+    private static IdGenerator idGenerator;
+
+    /**
+     * Enum type for ACL rule's action.
+     */
+    public enum Action {
+        DENY, ALLOW
+    }
+
+    /**
+     * Constructor for serializer.
+     */
+    private AclRule() {
+        this.id = null;
+        this.srcIp = null;
+        this.dstIp = null;
+        this.ipProto = 0;
+        this.dstTpPort = 0;
+        this.action = null;
+    }
+
+    /**
+     * Create a new ACL rule.
+     *
+     * @param srcIp     source IP address
+     * @param dstIp     destination IP address
+     * @param ipProto   IP protocol
+     * @param dstTpPort destination transport layer port
+     * @param action    ACL rule's action
+     */
+    private AclRule(Ip4Prefix srcIp, Ip4Prefix dstIp, byte ipProto,
+                    short dstTpPort, Action action) {
+        checkState(idGenerator != null, "Id generator is not bound.");
+        this.id = RuleId.valueOf(idGenerator.getNewId());
+        this.srcIp = srcIp;
+        this.dstIp = dstIp;
+        this.ipProto = ipProto;
+        this.dstTpPort = dstTpPort;
+        this.action = action;
+    }
+
+    /**
+     * Check if the first CIDR address is in (or the same as) the second CIDR address.
+     */
+    private boolean checkCIDRinCIDR(Ip4Prefix cidrAddr1, Ip4Prefix cidrAddr2) {
+        if (cidrAddr2 == null) {
+            return true;
+        } else if (cidrAddr1 == null) {
+            return false;
+        }
+        if (cidrAddr1.prefixLength() < cidrAddr2.prefixLength()) {
+            return false;
+        }
+        int offset = 32 - cidrAddr2.prefixLength();
+
+        int cidr1Prefix = cidrAddr1.address().toInt();
+        int cidr2Prefix = cidrAddr2.address().toInt();
+        cidr1Prefix = cidr1Prefix >> offset;
+        cidr2Prefix = cidr2Prefix >> offset;
+        cidr1Prefix = cidr1Prefix << offset;
+        cidr2Prefix = cidr2Prefix << offset;
+
+        return (cidr1Prefix == cidr2Prefix);
+    }
+
+    /**
+     * Check if this ACL rule match the given ACL rule.
+     *
+     * @param r ACL rule to check against
+     * @return true if this ACL rule matches the given ACL ruleule.
+     */
+    public boolean checkMatch(AclRule r) {
+        return (this.dstTpPort == r.dstTpPort || r.dstTpPort == 0)
+                && (this.ipProto == r.ipProto || r.ipProto == 0)
+                && (checkCIDRinCIDR(this.srcIp(), r.srcIp()))
+                && (checkCIDRinCIDR(this.dstIp(), r.dstIp()));
+    }
+
+    /**
+     * Returns a new ACL rule builder.
+     *
+     * @return ACL rule builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of an ACL rule.
+     */
+    public static final class Builder {
+
+        private Ip4Prefix srcIp = null;
+        private Ip4Prefix dstIp = null;
+        private byte ipProto = 0;
+        private short dstTpPort = 0;
+        private Action action = Action.DENY;
+
+        private Builder() {
+            // Hide constructor
+        }
+
+        /**
+         * Sets the source IP address for the ACL rule that will be built.
+         *
+         * @param srcIp source IP address to use for built ACL rule
+         * @return this builder
+         */
+        public Builder srcIp(Ip4Prefix srcIp) {
+            this.srcIp = srcIp;
+            return this;
+        }
+
+        /**
+         * Sets the destination IP address for the ACL rule that will be built.
+         *
+         * @param dstIp destination IP address to use for built ACL rule
+         * @return this builder
+         */
+        public Builder dstIp(Ip4Prefix dstIp) {
+            this.dstIp = dstIp;
+            return this;
+        }
+
+        /**
+         * Sets the IP protocol for the ACL rule that will be built.
+         *
+         * @param ipProto IP protocol to use for built ACL rule
+         * @return this builder
+         */
+        public Builder ipProto(byte ipProto) {
+            this.ipProto = ipProto;
+            return this;
+        }
+
+        /**
+         * Sets the destination transport layer port for the ACL rule that will be built.
+         *
+         * @param dstTpPort destination transport layer port to use for built ACL rule
+         * @return this builder
+         */
+        public Builder dstTpPort(short dstTpPort) {
+            if ((ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP)) {
+                this.dstTpPort = dstTpPort;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the action for the ACL rule that will be built.
+         *
+         * @param action action to use for built ACL rule
+         * @return this builder
+         */
+        public Builder action(Action action) {
+            this.action = action;
+            return this;
+        }
+
+        /**
+         * Builds an ACL rule from the accumulated parameters.
+         *
+         * @return ACL rule instance
+         */
+        public AclRule build() {
+            checkState(srcIp != null && dstIp != null, "Either srcIp or dstIp must be assigned.");
+            checkState(ipProto == 0 || ipProto == IPv4.PROTOCOL_ICMP
+                               || ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP,
+                       "ipProto must be assigned to TCP, UDP, or ICMP.");
+            return new AclRule(srcIp, dstIp, ipProto, dstTpPort, action);
+        }
+
+    }
+
+    /**
+     * Binds an id generator for unique ACL rule id generation.
+     * <p>
+     * Note: A generator cannot be bound if there is already a generator bound.
+     *
+     * @param newIdGenerator id generator
+     */
+    public static void bindIdGenerator(IdGenerator newIdGenerator) {
+        checkState(idGenerator == null, "Id generator is already bound.");
+        idGenerator = checkNotNull(newIdGenerator);
+    }
+
+    public RuleId id() {
+        return id;
+    }
+
+    public Ip4Prefix srcIp() {
+        return srcIp;
+    }
+
+    public Ip4Prefix dstIp() {
+        return this.dstIp;
+    }
+
+    public byte ipProto() {
+        return ipProto;
+    }
+
+    public short dstTpPort() {
+        return dstTpPort;
+    }
+
+    public Action action() {
+        return action;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(action, id.fingerprint(), ipProto, srcIp, dstIp, dstTpPort);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof AclRule) {
+            AclRule that = (AclRule) obj;
+            return Objects.equals(id, that.id) &&
+                    Objects.equals(srcIp, that.srcIp) &&
+                    Objects.equals(dstIp, that.dstIp) &&
+                    Objects.equals(ipProto, that.ipProto) &&
+                    Objects.equals(dstTpPort, that.dstTpPort) &&
+                    Objects.equals(action, that.action);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("id", id)
+                .add("srcIp", srcIp)
+                .add("dstIp", dstIp)
+                .add("ipProto", ipProto)
+                .add("dstTpPort", dstTpPort)
+                .add("action", action)
+                .toString();
+    }
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java
new file mode 100644 (file)
index 0000000..487a676
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import java.util.List;
+
+/**
+ * Service interface exported by ACL application.
+ */
+public interface AclService {
+
+    /**
+     * Gets a list containing all ACL rules.
+     *
+     * @return a list containing all ACL rules
+     */
+    List<AclRule> getAclRules();
+
+    /**
+     * Adds a new ACL rule.
+     *
+     * @param rule ACL rule
+     * @return true if successfully added, otherwise false
+     */
+    boolean addAclRule(AclRule rule);
+
+    /**
+     * Removes an exsiting ACL rule by rule id.
+     *
+     * @param ruleId ACL rule identifier
+     */
+    void removeAclRule(RuleId ruleId);
+
+    /**
+     * Clears ACL and resets all.
+     */
+    void clearAcl();
+
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java
new file mode 100644 (file)
index 0000000..ff9e25f
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.store.Store;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Service interface exported by ACL distributed store.
+ */
+public interface AclStore extends Store {
+
+    /**
+     * Gets a list containing all ACL rules.
+     *
+     * @return a list containing all ACL rules
+     */
+    List<AclRule> getAclRules();
+
+    /**
+     * Adds a new ACL rule.
+     *
+     * @param rule new ACL rule
+     */
+    void addAclRule(AclRule rule);
+
+    /**
+     * Gets an existing ACL rule.
+     *
+     * @param ruleId ACL rule id
+     * @return ACL rule with the given id
+     */
+    AclRule getAclRule(RuleId ruleId);
+
+    /**
+     * Removes an existing ACL rule by rule id.
+     *
+     * @param ruleId ACL rule id
+     */
+    void removeAclRule(RuleId ruleId);
+
+    /**
+     * Clears ACL and reset all.
+     */
+    void clearAcl();
+
+    /**
+     * Gets the current priority for new ACL flow rule by device id.
+     *
+     * @param deviceId device id
+     * @return new ACL flow rule's priority in the given device
+     */
+    int getPriorityByDevice(DeviceId deviceId);
+
+    /**
+     * Gets a set containing all ACL flow rules belonging to a given ACL rule.
+     *
+     * @param ruleId ACL rule id
+     * @return a set containing all ACL flow rules belonging to the given ACL rule
+     */
+    Set<FlowRule> getFlowByRule(RuleId ruleId);
+
+    /**
+     * Adds a new mapping from ACL rule to ACL flow rule.
+     *
+     * @param ruleId   ACL rule id
+     * @param flowRule ACL flow rule
+     */
+    void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule);
+
+    /**
+     * Removes an existing mapping from ACL rule to ACL flow rule.
+     *
+     * @param ruleId ACL rule id
+     */
+    void removeRuleToFlowMapping(RuleId ruleId);
+
+    /**
+     * Gets a list containing all allowing ACL rules matching a given denying ACL rule.
+     *
+     * @param denyingRuleId denying ACL rule id
+     * @return a list containing all allowing ACL rules matching the given denying ACL rule
+     */
+    List<RuleId> getAllowingRuleByDenyingRule(RuleId denyingRuleId);
+
+    /**
+     * Adds a new mapping from denying ACL rule to allowing ACL rule.
+     *
+     * @param denyingRuleId  denying ACL rule id
+     * @param allowingRuleId allowing ACL rule id
+     */
+    void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId);
+
+    /**
+     * Removes an exsiting mapping from denying ACL rule to allowing ACL rule.
+     *
+     * @param denyingRuleId denying ACL rule id
+     */
+    void removeDenyToAllowMapping(RuleId denyingRuleId);
+
+    /**
+     * Checks if an existing ACL rule already works in a given device.
+     *
+     * @param ruleId   ACL rule id
+     * @param deviceId devide id
+     * @return true if the given ACL rule works in the given device
+     */
+    boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId);
+
+    /**
+     * Adds a new mapping from ACL rule to device.
+     *
+     * @param ruleId   ACL rule id
+     * @param deviceId device id
+     */
+    void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId);
+
+    /**
+     * Removes an existing mapping from ACL rule to device.
+     *
+     * @param ruleId ACL rule id
+     */
+    void removeRuleToDeviceMapping(RuleId ruleId);
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java
new file mode 100644 (file)
index 0000000..e792efb
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Prefix;
+import org.onosproject.rest.AbstractWebResource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+/**
+ * Manage ACL rules.
+ */
+@Path("rules")
+public class AclWebResource extends AbstractWebResource {
+
+    /**
+     * Get all ACL rules.
+     * Returns array of all ACL rules.
+     *
+     * @return 200 OK
+     */
+    @GET
+    public Response queryAclRule() {
+        List<AclRule> rules = get(AclService.class).getAclRules();
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode root = mapper.createObjectNode();
+        ArrayNode arrayNode = mapper.createArrayNode();
+        for (AclRule rule : rules) {
+            ObjectNode node = mapper.createObjectNode();
+            node.put("id", rule.id().toString());
+            if (rule.srcIp() != null) {
+                node.put("srcIp", rule.srcIp().toString());
+            }
+            if (rule.dstIp() != null) {
+                node.put("dstIp", rule.dstIp().toString());
+            }
+            if (rule.ipProto() != 0) {
+                switch (rule.ipProto()) {
+                    case IPv4.PROTOCOL_ICMP:
+                        node.put("ipProto", "ICMP");
+                        break;
+                    case IPv4.PROTOCOL_TCP:
+                        node.put("ipProto", "TCP");
+                        break;
+                    case IPv4.PROTOCOL_UDP:
+                        node.put("ipProto", "UDP");
+                        break;
+                    default:
+                        break;
+                }
+            }
+            if (rule.dstTpPort() != 0) {
+                node.put("dstTpPort", rule.dstTpPort());
+            }
+            node.put("action", rule.action().toString());
+            arrayNode.add(node);
+        }
+        root.set("aclRules", arrayNode);
+        return Response.ok(root.toString(), MediaType.APPLICATION_JSON_TYPE).build();
+    }
+
+    /**
+     * Add a new ACL rule.
+     *
+     * @param stream JSON data describing the rule
+     * @return 200 OK
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response addAclRule(InputStream stream) throws URISyntaxException {
+        AclRule newRule = jsonToRule(stream);
+        return get(AclService.class).addAclRule(newRule) ?
+                Response.created(new URI(newRule.id().toString())).build() :
+                Response.serverError().build();
+    }
+
+    /**
+     * Remove ACL rule.
+     *
+     * @param id ACL rule id (in hex string format)
+     * @return 200 OK
+     */
+    @DELETE
+    @Path("{id}")
+    public Response removeAclRule(@PathParam("id") String id) {
+        RuleId ruleId = new RuleId(Long.parseLong(id.substring(2), 16));
+        get(AclService.class).removeAclRule(ruleId);
+        return Response.ok().build();
+    }
+
+    /**
+     * Remove all ACL rules.
+     *
+     * @return 200 OK
+     */
+    @DELETE
+    public Response clearACL() {
+        get(AclService.class).clearAcl();
+        return Response.ok().build();
+    }
+
+    /**
+     * Turns a JSON string into an ACL rule instance.
+     */
+    private AclRule jsonToRule(InputStream stream) {
+        JsonNode node;
+        try {
+            node = mapper().readTree(stream);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Unable to parse ACL request", e);
+        }
+
+        AclRule.Builder rule = AclRule.builder();
+
+        String s = node.path("srcIp").asText(null);
+        if (s != null) {
+            rule.srcIp(Ip4Prefix.valueOf(s));
+        }
+
+        s = node.path("dstIp").asText(null);
+        if (s != null) {
+            rule.dstIp(Ip4Prefix.valueOf(s));
+        }
+
+        s = node.path("ipProto").asText(null);
+        if (s != null) {
+            if ("TCP".equalsIgnoreCase(s)) {
+                rule.ipProto(IPv4.PROTOCOL_TCP);
+            } else if ("UDP".equalsIgnoreCase(s)) {
+                rule.ipProto(IPv4.PROTOCOL_UDP);
+            } else if ("ICMP".equalsIgnoreCase(s)) {
+                rule.ipProto(IPv4.PROTOCOL_ICMP);
+            } else {
+                throw new IllegalArgumentException("ipProto must be assigned to TCP, UDP, or ICMP");
+            }
+        }
+
+        int port = node.path("dstTpPort").asInt(0);
+        if (port > 0) {
+            rule.dstTpPort((short) port);
+        }
+
+        s = node.path("action").asText(null);
+        if (s != null) {
+            if ("allow".equalsIgnoreCase(s)) {
+                rule.action(AclRule.Action.ALLOW);
+            } else if ("deny".equalsIgnoreCase(s)) {
+                rule.action(AclRule.Action.DENY);
+            } else {
+                throw new IllegalArgumentException("action must be ALLOW or DENY");
+            }
+        }
+
+        return rule.build();
+    }
+
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java
new file mode 100644 (file)
index 0000000..468dab5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li and Heng Qi
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl;
+
+/**
+ * ACL rule identifier suitable as an external key.
+ * <p>This class is immutable.</p>
+ */
+public final class RuleId {
+    private final long value;
+
+    /**
+     * Creates an ACL rule identifier from the specified long value.
+     *
+     * @param value long value
+     * @return ACL rule identifier
+     */
+    public static RuleId valueOf(long value) {
+        return new RuleId(value);
+    }
+
+    /**
+     * Constructor for serializer.
+     */
+    RuleId() {
+        this.value = 0;
+    }
+
+    /**
+     * Constructs the ID corresponding to a given long value.
+     *
+     * @param value the underlying value of this ID
+     */
+    RuleId(long value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the backing value.
+     *
+     * @return the value
+     */
+    public long fingerprint() {
+        return value;
+    }
+
+    @Override
+    public int hashCode() {
+        return Long.hashCode(value);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof RuleId)) {
+            return false;
+        }
+        RuleId that = (RuleId) obj;
+        return this.value == that.value;
+    }
+
+    @Override
+    public String toString() {
+        return "0x" + Long.toHexString(value);
+    }
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java
new file mode 100644 (file)
index 0000000..f5c0c20
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl.impl;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.acl.AclRule;
+import org.onosproject.acl.AclService;
+import org.onosproject.acl.AclStore;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.acl.RuleId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the ACL service.
+ */
+@Component(immediate = true)
+@Service
+public class AclManager implements AclService {
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected AclStore aclStore;
+
+    private final Logger log = getLogger(getClass());
+    private ApplicationId appId;
+    private final HostListener hostListener = new InternalHostListener();
+    private IdGenerator idGenerator;
+
+    /**
+     * Checks if the given IP address is in the given CIDR address.
+     */
+    private boolean checkIpInCIDR(Ip4Address ip, Ip4Prefix cidr) {
+        int offset = 32 - cidr.prefixLength();
+        int cidrPrefix = cidr.address().toInt();
+        int ipIntValue = ip.toInt();
+        cidrPrefix = cidrPrefix >> offset;
+        ipIntValue = ipIntValue >> offset;
+        cidrPrefix = cidrPrefix << offset;
+        ipIntValue = ipIntValue << offset;
+
+        return (cidrPrefix == ipIntValue);
+    }
+
+    private class InternalHostListener implements HostListener {
+
+        /**
+         * Generate new ACL flow rules for new host following the given ACL rule.
+         */
+        private void processHostAddedEvent(HostEvent event, AclRule rule) {
+            DeviceId deviceId = event.subject().location().deviceId();
+            for (IpAddress address : event.subject().ipAddresses()) {
+                if ((rule.srcIp() != null) ?
+                        (checkIpInCIDR(address.getIp4Address(), rule.srcIp())) :
+                        (checkIpInCIDR(address.getIp4Address(), rule.dstIp()))) {
+                    if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
+                        List<RuleId> allowingRuleList = aclStore
+                                .getAllowingRuleByDenyingRule(rule.id());
+                        if (allowingRuleList != null) {
+                            for (RuleId allowingRuleId : allowingRuleList) {
+                                generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
+                            }
+                        }
+                        generateACLFlow(rule, deviceId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void event(HostEvent event) {
+            // if a new host appears and an existing rule denies
+            // its traffic, a new ACL flow rule is generated.
+            if (event.type() == HostEvent.Type.HOST_ADDED) {
+                DeviceId deviceId = event.subject().location().deviceId();
+                if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) {
+                    for (AclRule rule : aclStore.getAclRules()) {
+                        if (rule.action() != AclRule.Action.ALLOW) {
+                            processHostAddedEvent(event, rule);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication("org.onos.acl");
+        hostService.addListener(hostListener);
+        idGenerator = coreService.getIdGenerator("acl-ids");
+        AclRule.bindIdGenerator(idGenerator);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        hostService.removeListener(hostListener);
+        flowRuleService.removeFlowRulesById(appId);
+        aclStore.clearAcl();
+        log.info("Stopped");
+    }
+
+    @Override
+    public List<AclRule> getAclRules() {
+        return aclStore.getAclRules();
+    }
+
+    /**
+     * Checks if the new ACL rule matches an existing rule.
+     * If existing allowing rules matches the new denying rule, store the mappings.
+     *
+     * @return true if the new ACL rule matches an existing rule, false otherwise
+     */
+    private boolean matchCheck(AclRule newRule) {
+        for (AclRule existingRule : aclStore.getAclRules()) {
+            if (newRule.checkMatch(existingRule)) {
+                return true;
+            }
+
+            if (existingRule.action() == AclRule.Action.ALLOW
+                    && newRule.action() == AclRule.Action.DENY) {
+                if (existingRule.checkMatch(newRule)) {
+                    aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id());
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean addAclRule(AclRule rule) {
+        if (matchCheck(rule)) {
+            return false;
+        }
+        aclStore.addAclRule(rule);
+        log.info("ACL rule(id:{}) is added.", rule.id());
+        if (rule.action() != AclRule.Action.ALLOW) {
+            enforceRuleAdding(rule);
+        }
+        return true;
+    }
+
+    /**
+     * Gets a set containing all devices connecting with the hosts
+     * whose IP address is in the given CIDR IP address.
+     */
+    private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) {
+        Set<DeviceId> deviceIdSet = new HashSet<>();
+        final Iterable<Host> hosts = hostService.getHosts();
+
+        if (cidrAddr.prefixLength() != 32) {
+            for (Host h : hosts) {
+                for (IpAddress a : h.ipAddresses()) {
+                    if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
+                        deviceIdSet.add(h.location().deviceId());
+                    }
+                }
+            }
+        } else {
+            for (Host h : hosts) {
+                for (IpAddress a : h.ipAddresses()) {
+                    if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) {
+                        deviceIdSet.add(h.location().deviceId());
+                        return deviceIdSet;
+                    }
+                }
+            }
+        }
+        return deviceIdSet;
+    }
+
+    /**
+     * Enforces denying ACL rule by ACL flow rules.
+     */
+    private void enforceRuleAdding(AclRule rule) {
+        Set<DeviceId> dpidSet;
+        if (rule.srcIp() != null) {
+            dpidSet = getDeviceIdSet(rule.srcIp());
+        } else {
+            dpidSet = getDeviceIdSet(rule.dstIp());
+        }
+
+        for (DeviceId deviceId : dpidSet) {
+            List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id());
+            if (allowingRuleList != null) {
+                for (RuleId allowingRuleId : allowingRuleList) {
+                    generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId);
+                }
+            }
+            generateACLFlow(rule, deviceId);
+        }
+    }
+
+    /**
+     * Generates ACL flow rule according to ACL rule
+     * and install it into related device.
+     */
+    private void generateACLFlow(AclRule rule, DeviceId deviceId) {
+        if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) {
+            return;
+        }
+
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        FlowEntry.Builder flowEntry = DefaultFlowEntry.builder();
+
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
+        if (rule.srcIp() != null) {
+            selectorBuilder.matchIPSrc(rule.srcIp());
+            if (rule.dstIp() != null) {
+                selectorBuilder.matchIPDst(rule.dstIp());
+            }
+        } else {
+            selectorBuilder.matchIPDst(rule.dstIp());
+        }
+        if (rule.ipProto() != 0) {
+            selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue());
+        }
+        if (rule.dstTpPort() != 0) {
+            switch (rule.ipProto()) {
+                case IPv4.PROTOCOL_TCP:
+                    selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort()));
+                    break;
+                case IPv4.PROTOCOL_UDP:
+                    selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort()));
+                    break;
+                default:
+                    break;
+            }
+        }
+        if (rule.action() == AclRule.Action.ALLOW) {
+            treatment.add(Instructions.createOutput(PortNumber.CONTROLLER));
+        }
+        flowEntry.forDevice(deviceId);
+        flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId));
+        flowEntry.withSelector(selectorBuilder.build());
+        flowEntry.withTreatment(treatment.build());
+        flowEntry.fromApp(appId);
+        flowEntry.makePermanent();
+        // install flow rule
+        flowRuleService.applyFlowRules(flowEntry.build());
+        log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId);
+        aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build());
+        aclStore.addRuleToDeviceMapping(rule.id(), deviceId);
+    }
+
+    @Override
+    public void removeAclRule(RuleId ruleId) {
+        aclStore.removeAclRule(ruleId);
+        log.info("ACL rule(id:{}) is removed.", ruleId);
+        enforceRuleRemoving(ruleId);
+    }
+
+    /**
+     * Enforces removing an existing ACL rule.
+     */
+    private void enforceRuleRemoving(RuleId ruleId) {
+        Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId);
+        if (flowSet != null) {
+            for (FlowRule flowRule : flowSet) {
+                flowRuleService.removeFlowRules(flowRule);
+                log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString());
+            }
+        }
+        aclStore.removeRuleToFlowMapping(ruleId);
+        aclStore.removeRuleToDeviceMapping(ruleId);
+        aclStore.removeDenyToAllowMapping(ruleId);
+    }
+
+    @Override
+    public void clearAcl() {
+        aclStore.clearAcl();
+        flowRuleService.removeFlowRulesById(appId);
+        log.info("ACL is cleared.");
+    }
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java
new file mode 100644 (file)
index 0000000..a5fcfcc
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li, Heng Qi and Haisheng Yu
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.acl.impl;
+
+import com.google.common.collect.Collections2;
+import org.onosproject.acl.AclRule;
+import org.onosproject.acl.AclStore;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.acl.RuleId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the ACL store service.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedAclStore extends AbstractStore implements AclStore {
+
+    private final Logger log = getLogger(getClass());
+    private final int defaultFlowMaxPriority = 30000;
+
+    private ConsistentMap<RuleId, AclRule> ruleSet;
+    private ConsistentMap<DeviceId, Integer> deviceToPriority;
+    private ConsistentMap<RuleId, Set<DeviceId>> ruleToDevice;
+    private ConsistentMap<RuleId, Set<FlowRule>> ruleToFlow;
+    private ConsistentMap<RuleId, List<RuleId>> denyRuleToAllowRule;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Activate
+    public void activate() {
+        ApplicationId appId = coreService.getAppId("org.onosproject.acl");
+
+        KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
+                .register(KryoNamespaces.API)
+                .register(AclRule.class)
+                .register(AclRule.Action.class)
+                .register(RuleId.class);
+
+        ruleSet = storageService.<RuleId, AclRule>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("acl-rule-set")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        deviceToPriority = storageService.<DeviceId, Integer>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("device-to-priority")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        ruleToFlow = storageService.<RuleId, Set<FlowRule>>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("rule-to-flow")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        denyRuleToAllowRule = storageService.<RuleId, List<RuleId>>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("deny-to-allow")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        ruleToDevice = storageService.<RuleId, Set<DeviceId>>consistentMapBuilder()
+                .withSerializer(Serializer.using(serializer.build()))
+                .withName("rule-to-device")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactive() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public List<AclRule> getAclRules() {
+        List<AclRule> aclRules = new ArrayList<>();
+        aclRules.addAll(Collections2.transform(ruleSet.values(), Versioned::value));
+        return aclRules;
+    }
+
+    @Override
+    public void addAclRule(AclRule rule) {
+        ruleSet.putIfAbsent(rule.id(), rule);
+    }
+
+    @Override
+    public AclRule getAclRule(RuleId ruleId) {
+        Versioned<AclRule> rule = ruleSet.get(ruleId);
+        if (rule != null) {
+            return rule.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void removeAclRule(RuleId ruleId) {
+        ruleSet.remove(ruleId);
+    }
+
+    @Override
+    public void clearAcl() {
+        ruleSet.clear();
+        deviceToPriority.clear();
+        ruleToFlow.clear();
+        denyRuleToAllowRule.clear();
+        ruleToDevice.clear();
+    }
+
+    @Override
+    public int getPriorityByDevice(DeviceId deviceId) {
+        return deviceToPriority.compute(deviceId,
+                                        (id, priority) -> (priority == null) ? defaultFlowMaxPriority : (priority - 1))
+                .value();
+    }
+
+    @Override
+    public Set<FlowRule> getFlowByRule(RuleId ruleId) {
+        Versioned<Set<FlowRule>> flowRuleSet = ruleToFlow.get(ruleId);
+        if (flowRuleSet != null) {
+            return flowRuleSet.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule) {
+        ruleToFlow.computeIf(ruleId,
+                             flowRuleSet -> (flowRuleSet == null || !flowRuleSet.contains(flowRule)),
+                             (id, flowRuleSet) -> {
+                                 Set<FlowRule> newSet = new HashSet<>();
+                                 if (flowRuleSet != null) {
+                                     newSet.addAll(flowRuleSet);
+                                 }
+                                 newSet.add(flowRule);
+                                 return newSet;
+                             });
+    }
+
+    @Override
+    public void removeRuleToFlowMapping(RuleId ruleId) {
+        ruleToFlow.remove(ruleId);
+    }
+
+    @Override
+    public List<RuleId> getAllowingRuleByDenyingRule(RuleId denyingRuleId) {
+        Versioned<List<RuleId>> allowRuleIdSet = denyRuleToAllowRule.get(denyingRuleId);
+        if (allowRuleIdSet != null) {
+            return allowRuleIdSet.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId) {
+        denyRuleToAllowRule.computeIf(denyingRuleId,
+                                      ruleIdList -> (ruleIdList == null || !ruleIdList.contains(allowingRuleId)),
+                                      (id, ruleIdList) -> {
+                                          ArrayList<RuleId> newList = new ArrayList<>();
+                                          if (ruleIdList != null) {
+                                              newList.addAll(ruleIdList);
+                                          }
+                                          newList.add(allowingRuleId);
+                                          return newList;
+                                      });
+    }
+
+    @Override
+    public void removeDenyToAllowMapping(RuleId denyingRuleId) {
+        denyRuleToAllowRule.remove(denyingRuleId);
+    }
+
+    @Override
+    public boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId) {
+        return ruleToDevice.containsKey(ruleId) && ruleToDevice.get(ruleId).value().contains(deviceId);
+    }
+
+    @Override
+    public void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId) {
+        ruleToDevice.computeIf(ruleId,
+                               deviceIdSet -> (deviceIdSet == null || !deviceIdSet.contains(deviceId)),
+                               (id, deviceIdSet) -> {
+                                   Set<DeviceId> newSet = new HashSet<>();
+                                   if (deviceIdSet != null) {
+                                       newSet.addAll(deviceIdSet);
+                                   }
+                                   newSet.add(deviceId);
+                                   return newSet;
+                               });
+    }
+
+    @Override
+    public void removeRuleToDeviceMapping(RuleId ruleId) {
+        ruleToDevice.remove(ruleId);
+    }
+
+}
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java
new file mode 100644 (file)
index 0000000..9da9b3b
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ACL application implementation.
+ */
+package org.onosproject.acl.impl;
diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java
new file mode 100644 (file)
index 0000000..67f755c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ACL application.
+ */
+package org.onosproject.acl;
index 2c2d5cf..fc188b7 100644 (file)
@@ -33,7 +33,7 @@
         </init-param>
         <init-param>
             <param-name>com.sun.jersey.config.property.classnames</param-name>
-            <param-value>org.onos.acl.AclWebResource</param-value>
+            <param-value>org.onosproject.acl.AclWebResource</param-value>
         </init-param>
         <load-on-startup>10</load-on-startup>
     </servlet>
diff --git a/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java
new file mode 100644 (file)
index 0000000..c554db6
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China
+ * Advisers: Keqiu Li and Heng Qi
+ * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002)
+ * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.acl;
+
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.rest.ResourceTest;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test class for ACL application REST resource.
+ */
+public class AclWebResourceTest extends ResourceTest {
+
+    final AclService mockAclService = createMock(AclService.class);
+    final AclStore mockAclStore = createMock(AclStore.class);
+    final List<AclRule> rules = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        expect(mockAclService.getAclRules()).andReturn(rules).anyTimes();
+        ServiceDirectory testDirectory = new TestServiceDirectory().add(AclService.class, mockAclService)
+                .add(AclStore.class, mockAclStore);
+        BaseResource.setServiceDirectory(testDirectory);
+
+        IdGenerator idGenerator = new MockIdGenerator();
+        AclRule.bindIdGenerator(idGenerator);
+    }
+
+    @After
+    public void tearDown() {
+        verify(mockAclService);
+    }
+
+    /**
+     * Mock id generator for testing.
+     */
+    private class MockIdGenerator implements IdGenerator {
+        private AtomicLong nextId = new AtomicLong(0);
+
+        @Override
+        public long getNewId() {
+            return nextId.getAndIncrement();
+        }
+    }
+
+    @Override
+    public AppDescriptor configure() {
+        return new WebAppDescriptor.Builder("org.onosproject.acl").build();
+    }
+
+    @Test
+    @Ignore("FIXME: This needs to get reworked")
+    public void addRule() throws IOException {
+        WebResource.Builder rs = resource().path("rules").header("Content-type", "application/json");
+        String response;
+        String json;
+
+        replay(mockAclService);
+
+        // input a invalid JSON string that contains neither nw_src and nw_dst
+        json = "{\"ipProto\":\"TCP\",\"dstTpPort\":\"80\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("Failed! Either srcIp or dstIp must be assigned."));
+
+        // input a invalid JSON string that doesn't contain CIDR mask bits
+        json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("Malformed IPv4 prefix string: 10.0.0.1. " +
+                                                    "Address must take form \"x.x.x.x/y\""));
+
+        // input a invalid JSON string that contains a invalid IP address
+        json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.256/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("Invalid IP address string: 10.0.0.256"));
+
+        // input a invalid JSON string that contains a invalid IP address
+        json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.01/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("Invalid IP address string: 10.0.01"));
+
+        // input a invalid JSON string that contains a invalid CIDR mask bits
+        json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/a\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("Failed! For input string: \"a\""));
+
+        // input a invalid JSON string that contains a invalid CIDR mask bits
+        json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/33\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("Invalid prefix length 33. The value must be in the interval [0, 32]"));
+
+        // input a invalid JSON string that contains a invalid ipProto value
+        json = "{\"ipProto\":\"ARP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("ipProto must be assigned to TCP, UDP, or ICMP."));
+
+        // input a invalid JSON string that contains a invalid dstTpPort value
+        json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"a\",\"action\":\"DENY\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("dstTpPort must be assigned to a numerical value."));
+
+        // input a invalid JSON string that contains a invalid action value
+        json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"PERMIT\"}";
+        response = rs.post(String.class, json);
+        assertThat(response, containsString("action must be assigned to ALLOW or DENY."));
+    }
+}
index decdf5c..6503ee7 100644 (file)
@@ -24,7 +24,6 @@
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>onos-app-bgprouter</artifactId>
 
     <packaging>bundle</packaging>
index 8826535..6130a2e 100644 (file)
@@ -101,6 +101,7 @@ public class IcmpHandler {
         icmpReplyIpv4.setChecksum((short) 0);
 
         ICMP icmpReply = new ICMP();
+        icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload());
         icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
         icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
         icmpReply.setChecksum((short) 0);
index 1523e9c..fa91686 100644 (file)
@@ -86,7 +86,7 @@ public class CordFabricManager implements FabricService {
 
     private short radiusPort = 1812;
 
-    private short ofPort = 6633;
+    private short ofPort = 6653;
 
     private DeviceId fabricDeviceId = DeviceId.deviceId("of:5e3e486e73000187");
 
index 072254d..cb8acab 100644 (file)
@@ -21,24 +21,11 @@ import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.TpPort;
 import org.onlab.util.KryoNamespace;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.LeadershipEvent;
-import org.onosproject.cluster.LeadershipEventListener;
-import org.onosproject.cluster.LeadershipService;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
-import org.onosproject.net.config.ConfigFactory;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.config.basics.SubjectFactories;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
@@ -57,11 +44,15 @@ import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.cordvtn.OvsdbNode.State;
 import static org.onosproject.cordvtn.OvsdbNode.State.INIT;
+import static org.onosproject.cordvtn.OvsdbNode.State.DISCONNECT;
+import static org.onosproject.net.Device.Type.SWITCH;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * CORD VTN Application that provisions overlay virtual tenant networks.
+ * Provides initial setup or cleanup for provisioning virtual tenant networks
+ * on ovsdb, integration bridge and vm when they are added or deleted.
  */
 @Component(immediate = true)
 @Service
@@ -69,6 +60,11 @@ public class CordVtn implements CordVtnService {
 
     protected final Logger log = getLogger(getClass());
 
+    private static final int NUM_THREADS = 1;
+    private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(OvsdbNode.class);
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
@@ -78,113 +74,82 @@ public class CordVtn implements CordVtnService {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected LogicalClockService clockService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ClusterService clusterService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected LeadershipService leadershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigService configService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
-
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MastershipService mastershipService;
-
-    private static final int DEFAULT_NUM_THREADS = 1;
-    private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
-            .register(KryoNamespaces.API)
-            .register(OvsdbNode.class);
+    private final ExecutorService eventExecutor = Executors
+            .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
 
-    private final ExecutorService eventExecutor = Executors.newFixedThreadPool(
-            DEFAULT_NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler"));
-
-    private final LeadershipEventListener leadershipListener = new InternalLeadershipListener();
     private final DeviceListener deviceListener = new InternalDeviceListener();
     private final HostListener hostListener = new InternalHostListener();
-    private final NodeHandler nodeHandler = new NodeHandler();
+
+    private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
     private final BridgeHandler bridgeHandler = new BridgeHandler();
-    private final VirtualMachineHandler vmHandler = new VirtualMachineHandler();
-
-    private final ConfigFactory configFactory =
-            new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
-                @Override
-                public CordVtnConfig createConfig() {
-                    return new CordVtnConfig();
-                }
-            };
-
-    private ApplicationId appId;
-    private NodeId local;
+    private final VmHandler vmHandler = new VmHandler();
+
     private EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore;
-    private NodeConnectionManager nodeConnectionManager;
 
     @Activate
     protected void activate() {
-        appId = coreService.registerApplication("org.onosproject.cordvtn");
-
-        local = clusterService.getLocalNode().id();
+        coreService.registerApplication("org.onosproject.cordvtn");
         nodeStore = storageService.<DeviceId, OvsdbNode>eventuallyConsistentMapBuilder()
                 .withName("cordvtn-nodestore")
                 .withSerializer(NODE_SERIALIZER)
                 .withTimestampProvider((k, v) -> clockService.getTimestamp())
                 .build();
-        configRegistry.registerConfigFactory(configFactory);
 
         deviceService.addListener(deviceListener);
         hostService.addListener(hostListener);
-        leadershipService.addListener(leadershipListener);
-        leadershipService.runForLeadership(appId.name());
-        nodeConnectionManager = new NodeConnectionManager(appId, local, nodeStore,
-                                            mastershipService, leadershipService);
-        nodeConnectionManager.start();
+
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
-        nodeConnectionManager.stop();
-        leadershipService.removeListener(leadershipListener);
-        leadershipService.withdraw(appId.name());
         deviceService.removeListener(deviceListener);
         hostService.removeListener(hostListener);
+
         eventExecutor.shutdown();
         nodeStore.destroy();
-        configRegistry.unregisterConfigFactory(configFactory);
+
         log.info("Stopped");
     }
 
     @Override
-    public void addNode(String hostname, IpAddress ip, TpPort port) {
-        DefaultOvsdbNode node = new DefaultOvsdbNode(hostname, ip, port, DeviceId.NONE, INIT);
-
-        if (nodeStore.containsKey(node.deviceId())) {
-            log.warn("Node {} with ovsdb-server {}:{} already exists", hostname, ip, port);
+    public void addNode(OvsdbNode ovsdbNode) {
+        if (nodeStore.containsKey(ovsdbNode.deviceId())) {
+            log.warn("Node {} already exists", ovsdbNode.host());
             return;
         }
-        nodeStore.put(node.deviceId(), node);
-        log.info("New node {} with ovsdb-server {}:{} has been added", hostname, ip, port);
+        nodeStore.put(ovsdbNode.deviceId(), ovsdbNode);
+        if (ovsdbNode.state() != INIT) {
+            updateNode(ovsdbNode, INIT);
+        }
     }
 
     @Override
-    public void deleteNode(IpAddress ip, TpPort port) {
-        DeviceId deviceId = DeviceId.deviceId("ovsdb:" + ip + ":" + port);
-        OvsdbNode node = nodeStore.get(deviceId);
+    public void deleteNode(OvsdbNode ovsdbNode) {
+        if (!nodeStore.containsKey(ovsdbNode.deviceId())) {
+            log.warn("Node {} does not exist", ovsdbNode.host());
+            return;
+        }
+        updateNode(ovsdbNode, DISCONNECT);
+    }
 
-        if (node == null) {
-            log.warn("Node with ovsdb-server on {}:{} does not exist", ip, port);
+    @Override
+    public void updateNode(OvsdbNode ovsdbNode, State state) {
+        if (!nodeStore.containsKey(ovsdbNode.deviceId())) {
+            log.warn("Node {} does not exist", ovsdbNode.host());
             return;
         }
-        nodeConnectionManager.disconnectNode(node);
-        nodeStore.remove(node.deviceId());
+        DefaultOvsdbNode updatedNode = new DefaultOvsdbNode(ovsdbNode.host(),
+                                                            ovsdbNode.ip(),
+                                                            ovsdbNode.port(),
+                                                            state);
+        nodeStore.put(ovsdbNode.deviceId(), updatedNode);
     }
 
     @Override
@@ -192,6 +157,11 @@ public class CordVtn implements CordVtnService {
         return nodeStore.size();
     }
 
+    @Override
+    public OvsdbNode getNode(DeviceId deviceId) {
+        return nodeStore.get(deviceId);
+    }
+
     @Override
     public List<OvsdbNode> getNodes() {
         return nodeStore.values()
@@ -199,52 +169,22 @@ public class CordVtn implements CordVtnService {
                 .collect(Collectors.toList());
     }
 
-    private void initialSetup() {
-        // Read ovsdb nodes from network config
-        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
-        if (config == null) {
-            log.warn("No configuration found");
-            return;
-        }
-        config.ovsdbNodes().forEach(
-                node -> addNode(node.hostname(), node.ip(), node.port()));
-    }
-
-    private synchronized void processLeadershipChange(NodeId leader) {
-        // Only the leader performs the initial setup
-        if (leader == null || !leader.equals(local)) {
-            return;
-        }
-        initialSetup();
-    }
-
-    private class InternalLeadershipListener implements LeadershipEventListener {
-
-        @Override
-        public void event(LeadershipEvent event) {
-            if (event.subject().topic().equals(appId.name())) {
-                processLeadershipChange(event.subject().leader());
-            }
-        }
-    }
-
     private class InternalDeviceListener implements DeviceListener {
 
         @Override
         public void event(DeviceEvent event) {
             Device device = event.subject();
-            ConnectionHandler handler =
-                    (device.type() == Device.Type.CONTROLLER ? nodeHandler : bridgeHandler);
+            ConnectionHandler handler = (device.type() == SWITCH ? bridgeHandler : ovsdbHandler);
 
             switch (event.type()) {
-                    case DEVICE_ADDED:
-                        eventExecutor.submit(() -> handler.connected(device));
-                        break;
-                    case DEVICE_AVAILABILITY_CHANGED:
-                        eventExecutor.submit(() -> handler.disconnected(device));
-                        break;
-                    default:
-                        break;
+                case DEVICE_ADDED:
+                    eventExecutor.submit(() -> handler.connected(device));
+                    break;
+                case DEVICE_AVAILABILITY_CHANGED:
+                    eventExecutor.submit(() -> handler.disconnected(device));
+                    break;
+                default:
+                    break;
             }
         }
     }
@@ -268,7 +208,7 @@ public class CordVtn implements CordVtnService {
         }
     }
 
-    private class NodeHandler implements ConnectionHandler<Device> {
+    private class OvsdbHandler implements ConnectionHandler<Device> {
 
         @Override
         public void connected(Device device) {
@@ -296,7 +236,7 @@ public class CordVtn implements CordVtnService {
         }
     }
 
-    private class VirtualMachineHandler implements ConnectionHandler<Host> {
+    private class VmHandler implements ConnectionHandler<Host> {
 
         @Override
         public void connected(Host host) {
index c2c37ab..fdaf752 100644 (file)
@@ -27,12 +27,12 @@ import java.util.Set;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Configuration object for CORD VTN service.
+ * Configuration object for CordVtn service.
  */
 public class CordVtnConfig extends Config<ApplicationId> {
 
     public static final String OVSDB_NODES = "ovsdbNodes";
-    public static final String HOSTNAME = "hostname";
+    public static final String HOST = "host";
     public static final String IP = "ip";
     public static final String PORT = "port";
 
@@ -49,7 +49,7 @@ public class CordVtnConfig extends Config<ApplicationId> {
             return null;
         }
         nodes.forEach(jsonNode -> ovsdbNodes.add(new OvsdbNodeConfig(
-            jsonNode.path(HOSTNAME).asText(),
+            jsonNode.path(HOST).asText(),
             IpAddress.valueOf(jsonNode.path(IP).asText()),
             TpPort.tpPort(jsonNode.path(PORT).asInt()))));
 
@@ -57,27 +57,27 @@ public class CordVtnConfig extends Config<ApplicationId> {
     }
 
     /**
-     * Configuration for an OVSDB node.
+     * Configuration for an ovsdb node.
      */
     public static class OvsdbNodeConfig {
 
-        private final String hostname;
+        private final String host;
         private final IpAddress ip;
         private final TpPort port;
 
-        public OvsdbNodeConfig(String hostname, IpAddress ip, TpPort port) {
-            this.hostname = checkNotNull(hostname);
+        public OvsdbNodeConfig(String host, IpAddress ip, TpPort port) {
+            this.host = checkNotNull(host);
             this.ip = checkNotNull(ip);
             this.port = checkNotNull(port);
         }
 
         /**
-         * Returns hostname of the node.
+         * Returns host information of the node.
          *
-         * @return hostname
+         * @return host
          */
-        public String hostname() {
-            return this.hostname;
+        public String host() {
+            return this.host;
         }
 
         /**
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
new file mode 100644 (file)
index 0000000..043b376
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cordvtn;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.cluster.LeadershipEventListener;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.slf4j.Logger;
+
+import static org.onosproject.cordvtn.OvsdbNode.State.INIT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Reads node information from the network config file and handles the config
+ * update events.
+ * Only a leader controller performs the node addition or deletion.
+ */
+@Component(immediate = true)
+public class CordVtnConfigManager {
+
+    protected final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnService cordVtnService;
+
+    private final ConfigFactory configFactory =
+            new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
+                @Override
+                public CordVtnConfig createConfig() {
+                    return new CordVtnConfig();
+                }
+            };
+
+    private final LeadershipEventListener leadershipListener = new InternalLeadershipListener();
+    private final NetworkConfigListener configListener = new InternalConfigListener();
+
+    private NodeId local;
+    private ApplicationId appId;
+
+    @Activate
+    protected void active() {
+        local = clusterService.getLocalNode().id();
+        appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
+
+        configService.addListener(configListener);
+        configRegistry.registerConfigFactory(configFactory);
+
+        leadershipService.addListener(leadershipListener);
+        leadershipService.runForLeadership(CordVtnService.CORDVTN_APP_ID);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        leadershipService.removeListener(leadershipListener);
+        leadershipService.withdraw(appId.name());
+
+        configRegistry.unregisterConfigFactory(configFactory);
+        configService.removeListener(configListener);
+    }
+
+    private void readConfiguration() {
+        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+
+        if (config == null) {
+            log.warn("No configuration found");
+            return;
+        }
+
+        config.ovsdbNodes().forEach(node -> {
+            DefaultOvsdbNode ovsdbNode =
+                    new DefaultOvsdbNode(node.host(), node.ip(), node.port(), INIT);
+            cordVtnService.addNode(ovsdbNode);
+            log.info("Add new node {}", node.host());
+        });
+    }
+
+    private synchronized void processLeadershipChange(NodeId leader) {
+        if (leader == null || !leader.equals(local)) {
+            return;
+        }
+        readConfiguration();
+    }
+
+    private class InternalLeadershipListener implements LeadershipEventListener {
+
+        @Override
+        public void event(LeadershipEvent event) {
+            if (event.subject().topic().equals(appId.name())) {
+                processLeadershipChange(event.subject().leader());
+            }
+        }
+    }
+
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            // TODO handle update event
+        }
+    }
+}
index d26a10a..1f75dce 100644 (file)
@@ -15,8 +15,8 @@
  */
 package org.onosproject.cordvtn;
 
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.TpPort;
+import org.onosproject.cordvtn.OvsdbNode.State;
+import org.onosproject.net.DeviceId;
 
 import java.util.List;
 
@@ -24,22 +24,30 @@ import java.util.List;
  * Service for provisioning overlay virtual networks on compute nodes.
  */
 public interface CordVtnService {
+
+    String CORDVTN_APP_ID = "org.onosproject.cordvtn";
     /**
      * Adds a new node to the service.
      *
-     * @param hostname hostname of the node
-     * @param ip ip address to access the ovsdb server running on the node
-     * @param port port number to access the ovsdb server running on the node
+     * @param ovsdbNode ovsdb node
+     */
+    void addNode(OvsdbNode ovsdbNode);
+
+    /**
+     * Deletes a node from the service.
+     *
+     * @param ovsdbNode ovsdb node
      */
-    void addNode(String hostname, IpAddress ip, TpPort port);
+    void deleteNode(OvsdbNode ovsdbNode);
 
     /**
-     * Deletes the node from the service.
+     * Updates ovsdb node.
+     * It only used for updating node's connection state.
      *
-     * @param ip ip address to access the ovsdb server running on the node
-     * @param port port number to access the ovsdb server running on the node
+     * @param ovsdbNode ovsdb node
+     * @param state ovsdb connection state
      */
-    void deleteNode(IpAddress ip, TpPort port);
+    void updateNode(OvsdbNode ovsdbNode, State state);
 
     /**
      * Returns the number of the nodes known to the service.
@@ -48,6 +56,14 @@ public interface CordVtnService {
      */
     int getNodeCount();
 
+    /**
+     * Returns OvsdbNode with given device id.
+     *
+     * @param deviceId device id
+     * @return ovsdb node
+     */
+    OvsdbNode getNode(DeviceId deviceId);
+
     /**
      * Returns all nodes known to the service.
      *
index b8cdbe9..ce8b0f1 100644 (file)
@@ -15,6 +15,7 @@
  */
 package org.onosproject.cordvtn;
 
+import com.google.common.base.MoreObjects;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.TpPort;
 import org.onosproject.net.DeviceId;
@@ -26,21 +27,15 @@ import java.util.Objects;
  */
 public class DefaultOvsdbNode implements OvsdbNode {
 
-    private final String hostname;
+    private final String host;
     private final IpAddress ip;
     private final TpPort port;
-    private final DeviceId deviceId;
-    private final DeviceId bridgeId;
     private final State state;
 
-    public DefaultOvsdbNode(String hostname, IpAddress ip, TpPort port,
-                            DeviceId bridgeId, State state) {
-        this.hostname = hostname;
+    public DefaultOvsdbNode(String host, IpAddress ip, TpPort port, State state) {
+        this.host = host;
         this.ip = ip;
         this.port = port;
-        this.deviceId = DeviceId.deviceId(
-                "ovsdb:" + ip.toString() + ":" + port.toString());
-        this.bridgeId = bridgeId;
         this.state = state;
     }
 
@@ -55,8 +50,8 @@ public class DefaultOvsdbNode implements OvsdbNode {
     }
 
     @Override
-    public String hostname() {
-        return this.hostname;
+    public String host() {
+        return this.host;
     }
 
     @Override
@@ -66,12 +61,12 @@ public class DefaultOvsdbNode implements OvsdbNode {
 
     @Override
     public DeviceId deviceId() {
-        return this.deviceId;
+        return DeviceId.deviceId("ovsdb:" + this.ip.toString() + ":" + this.port.toString());
     }
 
     @Override
-    public DeviceId bridgeId() {
-        return this.bridgeId;
+    public DeviceId intBrId() {
+        return DeviceId.deviceId("of:" + this.host);
     }
 
     @Override
@@ -82,8 +77,9 @@ public class DefaultOvsdbNode implements OvsdbNode {
 
         if (o instanceof DefaultOvsdbNode) {
             DefaultOvsdbNode that = (DefaultOvsdbNode) o;
-            // We compare the ip and port only.
-            if (this.ip.equals(that.ip) && this.port.equals(that.port)) {
+            if (this.host.equals(that.host) &&
+                    this.ip.equals(that.ip) &&
+                    this.port.equals(that.port)) {
                 return true;
             }
         }
@@ -92,6 +88,16 @@ public class DefaultOvsdbNode implements OvsdbNode {
 
     @Override
     public int hashCode() {
-        return Objects.hash(ip, port);
+        return Objects.hash(host, ip, port);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("host", host)
+                .add("ip", ip)
+                .add("port", port)
+                .add("state", state)
+                .toString();
     }
 }
index 0b7029e..ebba4cd 100644 (file)
  */
 package org.onosproject.cordvtn;
 
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
 import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
 import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.net.Device;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
 import org.slf4j.Logger;
 
 import java.util.concurrent.Executors;
@@ -28,118 +35,131 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.cordvtn.OvsdbNode.State.CONNECTED;
+import static org.onosproject.cordvtn.OvsdbNode.State.DISCONNECTED;
+import static org.onosproject.cordvtn.OvsdbNode.State.READY;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Node connection manager.
+ * Provides the connection state management of all nodes registered to the service
+ * so that the nodes keep connected unless it is requested to be deleted.
  */
+@Component(immediate = true)
 public class NodeConnectionManager {
     protected final Logger log = getLogger(getClass());
 
-    private final ApplicationId appId;
-    private final NodeId localId;
-    private final EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore;
-    private final MastershipService mastershipService;
-    private final LeadershipService leadershipService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    CordVtnService cordVtnService;
 
     private static final int DELAY_SEC = 5;
-    private ScheduledExecutorService connectionExecutor;
-
-    /**
-     * Creates a new NodeConnectionManager.
-     *
-     * @param localId local id
-     * @param nodeStore node store
-     * @param mastershipService mastership service
-     */
-    public NodeConnectionManager(ApplicationId appId, NodeId localId,
-                                 EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore,
-                                 MastershipService mastershipService,
-                                 LeadershipService leadershipService) {
-        this.appId = appId;
-        this.localId = localId;
-        this.nodeStore = nodeStore;
-        this.mastershipService = mastershipService;
-        this.leadershipService = leadershipService;
-    }
 
-    /**
-     * Starts the node connection manager.
-     */
-    public void start() {
-        connectionExecutor = Executors.newSingleThreadScheduledExecutor(
-                        groupedThreads("onos/cordvtn", "connection-executor"));
-        connectionExecutor.scheduleWithFixedDelay(() -> nodeStore.values()
+    private final DeviceListener deviceListener = new InternalDeviceListener();
+    private final ScheduledExecutorService connectionExecutor = Executors
+            .newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "connection-manager"));
+
+    private NodeId localId;
+
+    @Activate
+    protected void activate() {
+        localId = clusterService.getLocalNode().id();
+        deviceService.addListener(deviceListener);
+
+        connectionExecutor.scheduleWithFixedDelay(() -> cordVtnService.getNodes()
                 .stream()
                 .filter(node -> localId.equals(getMaster(node)))
-                .forEach(node -> connectNode(node)), 0, DELAY_SEC, TimeUnit.SECONDS);
+                .forEach(node -> {
+                    connect(node);
+                    disconnect(node);
+                }), 0, DELAY_SEC, TimeUnit.SECONDS);
     }
 
-    /**
-     * Stops the node connection manager.
-     */
+    @Deactivate
     public void stop() {
         connectionExecutor.shutdown();
+        deviceService.removeListener(deviceListener);
     }
 
-    /**
-     * Adds a new node to the system.
-     *
-     * @param ovsdbNode ovsdb node
-     */
-    public void connectNode(OvsdbNode ovsdbNode) {
+    public void connect(OvsdbNode ovsdbNode) {
         switch (ovsdbNode.state()) {
             case INIT:
             case DISCONNECTED:
-                // TODO: set the node to passive mode
+                setPassiveMode(ovsdbNode);
             case READY:
-                // TODO: initiate connection
-                break;
-            case CONNECTED:
+                setupConnection(ovsdbNode);
                 break;
             default:
+                break;
         }
     }
 
-    /**
-     * Deletes the ovsdb node.
-     *
-     * @param ovsdbNode ovsdb node
-     */
-    public void disconnectNode(OvsdbNode ovsdbNode) {
+    public void disconnect(OvsdbNode ovsdbNode) {
         switch (ovsdbNode.state()) {
-            case CONNECTED:
+            case DISCONNECT:
                 // TODO: disconnect
                 break;
-            case INIT:
-            case READY:
-            case DISCONNECTED:
-                break;
             default:
+                break;
+        }
+    }
+
+    private class InternalDeviceListener implements DeviceListener {
+
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            if (device.type() != Device.Type.CONTROLLER) {
+                return;
+            }
+
+            DefaultOvsdbNode node;
+            switch (event.type()) {
+                case DEVICE_ADDED:
+                    node = (DefaultOvsdbNode) cordVtnService.getNode(device.id());
+                    if (node != null) {
+                        cordVtnService.updateNode(node, CONNECTED);
+                    }
+                    break;
+                case DEVICE_AVAILABILITY_CHANGED:
+                    node = (DefaultOvsdbNode) cordVtnService.getNode(device.id());
+                    if (node != null) {
+                        cordVtnService.updateNode(node, DISCONNECTED);
+                    }
+                    break;
+                default:
+                    break;
+            }
         }
     }
 
     private NodeId getMaster(OvsdbNode ovsdbNode) {
-        // Return the master of the bridge(switch) if it exist or
-        // return the current leader
-        if (ovsdbNode.bridgeId() == DeviceId.NONE) {
-            return leadershipService.getLeader(this.appId.name());
-        } else {
-            return mastershipService.getMasterFor(ovsdbNode.bridgeId());
+        NodeId master = mastershipService.getMasterFor(ovsdbNode.intBrId());
+
+        // master is null if there's no such device
+        if (master == null) {
+            master = leadershipService.getLeader(CordVtnService.CORDVTN_APP_ID);
         }
+        return master;
     }
 
     private void setPassiveMode(OvsdbNode ovsdbNode) {
         // TODO: need ovsdb client implementation first
         // TODO: set the remove ovsdb server passive mode
-        // TODO: set the node state READY if it succeed
-    }
-
-    private void connect(OvsdbNode ovsdbNode) {
-        // TODO: need ovsdb client implementation first
+        cordVtnService.updateNode(ovsdbNode, READY);
     }
 
-    private void disconnect(OvsdbNode ovsdbNode) {
-        // TODO: need ovsdb client implementation first
+    private void setupConnection(OvsdbNode ovsdbNode) {
+        // TODO initiate connection
     }
 }
index bb2a0b7..296bd43 100644 (file)
@@ -24,51 +24,52 @@ import org.onosproject.net.DeviceId;
  */
 public interface OvsdbNode {
     /**
-     * State of the ovsdb node.
+     * Ovsdb connection state.
      */
     enum State {
-        INIT, READY, CONNECTED, DISCONNECTED
+        INIT, READY, CONNECTED, DISCONNECT, DISCONNECTED
     }
 
     /**
-     * Returns the IP address of ovsdb server.
+     * Returns the IP address of the ovsdb server.
      *
      * @return ip address
      */
     IpAddress ip();
 
     /**
-     * Returns the port number of ovsdb server.
+     * Returns the port number of the ovsdb server.
      *
      * @return port number
      */
     TpPort port();
 
     /**
-     * Returns the hostname of the node.
+     * Returns the host information of the ovsdb server.
+     * It could be hostname or ip address.
      *
-     * @return hostname
+     * @return host
      */
-    String hostname();
+    String host();
 
     /**
-     * Returns the state of the node.
+     * Returns the connection state of the ovsdb server.
      *
-     * @return state of the node
+     * @return connection state
      */
     State state();
 
     /**
-     * Returns the device ID of the node.
+     * Returns the device id of the ovsdb server.
      *
      * @return device id
      */
     DeviceId deviceId();
 
     /**
-     * Returns the device ID of the bridge associated with this node.
+     * Returns the device id of the integration bridge associated with the node.
      *
      * @return device id
      */
-    DeviceId bridgeId();
+    DeviceId intBrId();
 }
index c9fade9..5615af1 100644 (file)
@@ -65,7 +65,7 @@ public interface DhcpStore {
      *
      * @param hostId the host ID for which the mapping needs to be changed
      */
-    void releaseIP(HostId hostId);
+    Ip4Address releaseIP(HostId hostId);
 
     /**
      * Returns a collection of all the MacAddress to IPAddress mapping assigned to the hosts.
index 345d5ad..96d94a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -154,6 +154,8 @@ public class DhcpManager implements DhcpService {
 
     private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2");
 
+    private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255");
+
     protected Timeout timeout;
 
     protected static int timerDelay = 2;
@@ -290,12 +292,18 @@ public class DhcpManager implements DhcpService {
             DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
             DHCP dhcpReply = new DHCP();
             dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
-
-            dhcpReply.setYourIPAddress(ipOffered.toInt());
-            dhcpReply.setServerIPAddress(myIP.toInt());
-
-            dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
+            dhcpReply.setFlags(dhcpPacket.getFlags());
+            dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress());
             dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress());
+            dhcpReply.setTransactionId(dhcpPacket.getTransactionId());
+
+            if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
+                dhcpReply.setYourIPAddress(ipOffered.toInt());
+                dhcpReply.setServerIPAddress(myIP.toInt());
+                if (dhcpPacket.getGatewayIPAddress() == 0) {
+                    ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
+                }
+            }
             dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
             dhcpReply.setHardwareAddressLength((byte) 6);
 
@@ -317,54 +325,57 @@ public class DhcpManager implements DhcpService {
             option.setData(myIP.toOctets());
             optionList.add(option);
 
-            // IP Address Lease Time.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
-            option.setLength((byte) 4);
-            option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
-            optionList.add(option);
-
-            // IP Address Renewal Time.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
-            option.setLength((byte) 4);
-            option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
-            optionList.add(option);
-
-            // IP Address Rebinding Time.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
-            option.setLength((byte) 4);
-            option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
-            optionList.add(option);
-
-            // Subnet Mask.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
-            option.setLength((byte) 4);
-            option.setData(subnetMask.toOctets());
-            optionList.add(option);
-
-            // Broadcast Address.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
-            option.setLength((byte) 4);
-            option.setData(broadcastAddress.toOctets());
-            optionList.add(option);
-
-            // Router Address.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
-            option.setLength((byte) 4);
-            option.setData(routerAddress.toOctets());
-            optionList.add(option);
-
-            // DNS Server Address.
-            option = new DHCPOption();
-            option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
-            option.setLength((byte) 4);
-            option.setData(domainServer.toOctets());
-            optionList.add(option);
+            if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
+
+                // IP Address Lease Time.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue());
+                option.setLength((byte) 4);
+                option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array());
+                optionList.add(option);
+
+                // IP Address Renewal Time.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue());
+                option.setLength((byte) 4);
+                option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array());
+                optionList.add(option);
+
+                // IP Address Rebinding Time.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue());
+                option.setLength((byte) 4);
+                option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array());
+                optionList.add(option);
+
+                // Subnet Mask.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
+                option.setLength((byte) 4);
+                option.setData(subnetMask.toOctets());
+                optionList.add(option);
+
+                // Broadcast Address.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue());
+                option.setLength((byte) 4);
+                option.setData(broadcastAddress.toOctets());
+                optionList.add(option);
+
+                // Router Address.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
+                option.setLength((byte) 4);
+                option.setData(routerAddress.toOctets());
+                optionList.add(option);
+
+                // DNS Server Address.
+                option = new DHCPOption();
+                option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
+                option.setLength((byte) 4);
+                option.setData(domainServer.toOctets());
+                optionList.add(option);
+            }
 
             // End Option.
             option = new DHCPOption();
@@ -447,41 +458,51 @@ public class DhcpManager implements DhcpService {
 
                 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) {
 
-                    outgoingPacketType = DHCPPacketType.DHCPACK;
-
                     if (flagIfServerIP && flagIfRequestedIP) {
                         // SELECTING state
-                        if (myIP.equals(serverIP) &&
-                                dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
+                        if (myIP.equals(serverIP)) {
 
-                            Ethernet ethReply = buildReply(packet, requestedIP,
-                                    (byte) outgoingPacketType.getValue());
+                            if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
+                                outgoingPacketType = DHCPPacketType.DHCPACK;
+                                discoverHost(context, requestedIP);
+                            } else {
+                                outgoingPacketType = DHCPPacketType.DHCPNAK;
+                            }
+                            Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
                             sendReply(context, ethReply);
-                            discoverHost(context, requestedIP);
                         }
                     } else if (flagIfRequestedIP) {
                         // INIT-REBOOT state
                         if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
-                            Ethernet ethReply = buildReply(packet, requestedIP,
-                                    (byte) outgoingPacketType.getValue());
+                            outgoingPacketType = DHCPPacketType.DHCPACK;
+                            Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
                             sendReply(context, ethReply);
                             discoverHost(context, requestedIP);
                         }
+
                     } else {
                         // RENEWING and REBINDING state
                         int ciaadr = dhcpPayload.getClientIPAddress();
                         if (ciaadr != 0) {
                             Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
                             if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime)) {
-                                Ethernet ethReply = buildReply(packet, clientIaddr,
-                                        (byte) outgoingPacketType.getValue());
-                                sendReply(context, ethReply);
+                                outgoingPacketType = DHCPPacketType.DHCPACK;
                                 discoverHost(context, clientIaddr);
+                            } else if (packet.getEtherType() == Ethernet.TYPE_IPV4 &&
+                                    ((IPv4) packet.getPayload()).getDestinationAddress() == myIP.toInt()) {
+                                outgoingPacketType = DHCPPacketType.DHCPNAK;
+                            } else {
+                                return;
                             }
+                            Ethernet ethReply = buildReply(packet, clientIaddr, (byte) outgoingPacketType.getValue());
+                            sendReply(context, ethReply);
                         }
                     }
                 } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) {
-                    dhcpStore.releaseIP(hostId);
+                    Ip4Address ip4Address = dhcpStore.releaseIP(hostId);
+                    if (ip4Address != null) {
+                        hostProviderService.removeIpFromHost(hostId, ip4Address);
+                    }
                 }
             }
         }
@@ -666,9 +687,10 @@ public class DhcpManager implements DhcpService {
                 if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) &&
                         (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) {
 
-                    dhcpStore.releaseIP(entry.getKey());
-                    // TODO remove only the IP from the host entry when the API is in place.
-                    hostProviderService.hostVanished(entry.getKey());
+                    Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey());
+                    if (ip4Address != null) {
+                        hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress());
+                    }
                 }
             }
             timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES);
index dbdadb3..63f69d4 100644 (file)
@@ -212,7 +212,7 @@ public class DistributedDhcpStore implements DhcpStore {
     }
 
     @Override
-    public void releaseIP(HostId hostId) {
+    public Ip4Address releaseIP(HostId hostId) {
         if (allocationMap.containsKey(hostId)) {
             IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value())
                                                     .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired)
@@ -222,7 +222,9 @@ public class DistributedDhcpStore implements DhcpStore {
             if (ipWithinRange(freeIP)) {
                 freeIPPool.add(freeIP);
             }
+            return freeIP;
         }
+        return null;
     }
 
     @Override
index 3ea3b1b..fd4701c 100644 (file)
@@ -25,6 +25,7 @@ import org.onlab.packet.DHCPPacketType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.UDP;
 import org.onosproject.core.CoreServiceAdapter;
@@ -234,7 +235,8 @@ public class DhcpManagerTest {
         public void setDefaultTimeoutForPurge(int timeInSeconds) {
         }
 
-        public void releaseIP(HostId hostId) {
+        public Ip4Address releaseIP(HostId hostId) {
+            return null;
         }
 
         public Map<HostId, IpAssignment> listAssignedMapping() {
@@ -331,12 +333,18 @@ public class DhcpManagerTest {
 
         @Override
         public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
+
         }
 
         @Override
         public void hostVanished(HostId hostId) {
         }
 
+        @Override
+        public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
+
+        }
+
     }
 
     /**
index f5dfcf2..b092041 100644 (file)
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.compendium</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java
new file mode 100644 (file)
index 0000000..2c61949
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.flowanalyzer;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+
+/**
+ * Analyzes flows for cycles and black holes.
+ */
+@Command(scope = "onos", name = "flow-analysis",
+         description = "Analyzes flows for cycles and black holes")
+public class FlowAnalysisCommand extends AbstractShellCommand {
+
+    @Override
+    protected void execute() {
+        FlowAnalyzer service = get(FlowAnalyzer.class);
+        print(service.analyze());
+    }
+}
index 5d99d74..6aaaaee 100644 (file)
@@ -21,12 +21,31 @@ import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.host.HostService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.net.topology.TopologyGraph;
 import org.onosproject.net.link.LinkService;
+import org.onosproject.net.Link;
+import org.onosproject.net.topology.TopologyVertex;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -42,11 +61,10 @@ public class FlowAnalyzer {
     protected FlowRuleService flowRuleService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected LinkService linkService;
+    protected TopologyService topologyService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
+    protected LinkService linkService;
 
     @Activate
     public void activate(ComponentContext context) {
@@ -58,12 +76,193 @@ public class FlowAnalyzer {
         log.info("Stopped");
     }
 
+    TopologyGraph graph;
+    Map<FlowEntry, String> label = new HashMap<>();
+    Set<FlowEntry> ignoredFlows = new HashSet<>();
 
     /**
-     * ...
+     * Analyzes and prints out a report on the status of every flow entry inside
+     * the network. The possible states are: Cleared (implying that the entry leads to
+     * a host), Cycle (implying that it is part of cycle), and Black Hole (implying
+     * that the entry does not lead to a single host).
      */
-    public void analyze() {
-        // TODO: implement this
+    public String analyze() {
+        graph = topologyService.getGraph(topologyService.currentTopology());
+        for (TopologyVertex v: graph.getVertexes()) {
+            DeviceId srcDevice = v.deviceId();
+            Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice);
+            for (FlowEntry flow: flowTable) {
+                dfs(flow);
+            }
+        }
+
+        //analyze the cycles to look for "critical flows" that can be removed
+        //to break the cycle
+        Set<FlowEntry> critpts = new HashSet<>();
+        for (FlowEntry flow: label.keySet()) {
+            if ("Cycle".equals(label.get(flow))) {
+                Map<FlowEntry, String> labelSaved = label;
+                label = new HashMap<FlowEntry, String>();
+                ignoredFlows.add(flow);
+                for (TopologyVertex v: graph.getVertexes()) {
+                    DeviceId srcDevice = v.deviceId();
+                    Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice);
+                    for (FlowEntry flow1: flowTable) {
+                        dfs(flow1);
+                    }
+                }
+
+                boolean replacable = true;
+                for (FlowEntry flow2: label.keySet()) {
+                    if ("Cleared".equals(labelSaved.get(flow2)) && !("Cleared".equals(label.get(flow2)))) {
+                        replacable = false;
+                    }
+                }
+                if (replacable) {
+                    critpts.add(flow);
+                }
+                label = labelSaved;
+            }
+        }
+
+        for (FlowEntry flow: critpts) {
+            label.put(flow, "Cycle Critical Point");
+        }
+
+        String s = "\n";
+        for (FlowEntry flow: label.keySet()) {
+            s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n");
+            s += ("Analysis: " + label.get(flow) + "!\n\n");
+        }
+        s += ("Analyzed " + label.keySet().size() + " flows.");
+        //log.info(s);
+        return s;
+    }
+
+    public Map<FlowEntry, String> calcLabels() {
+        analyze();
+        return label;
+    }
+    public String analysisOutput()   {
+        analyze();
+        String s = "\n";
+        for (FlowEntry flow: label.keySet()) {
+            s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n");
+            s += ("Analysis: " + label.get(flow) + "!\n\n");
+        }
+        return s;
+    }
+
+    private boolean dfs(FlowEntry flow) {
+        if (ignoredFlows.contains(flow)) {
+            return false;
+        }
+        if ("Cycle".equals(label.get(flow)) ||
+                "Black Hole".equals(label.get(flow)) ||
+                "Cleared".equals(label.get(flow)) ||
+                 "NA".equals(label.get(flow)) ||
+                "Cycle Critical Point".equals(label.get(flow))) {
+
+            // This flow has already been analyzed and there is no need to analyze it further
+            return !"Black Hole".equals(label.get(flow));
+        }
+
+        if ("Visiting".equals(label.get(flow))) {
+            //you've detected a cycle because you reached the same entry again during your dfs
+            //let it continue so you can label the whole cycle
+            label.put(flow, "Cycle");
+        } else {
+            //otherwise, mark off the current flow entry as currently being visited
+            label.put(flow, "Visiting");
+        }
+
+        boolean pointsToLiveEntry = false;
+
+        List<Instruction> instructions = flow.treatment().allInstructions();
+        for (Instruction i: instructions) {
+            if (i instanceof Instructions.OutputInstruction) {
+                pointsToLiveEntry |= analyzeInstruction(i, flow);
+            }
+            if ("NA".equals(label.get(flow))) {
+                return pointsToLiveEntry;
+            }
+        }
+
+        if (!pointsToLiveEntry) {
+            //this entry does not point to any "live" entries thus must be a black hole
+            label.put(flow, "Black Hole");
+        } else if ("Visiting".equals(label.get(flow))) {
+            //the flow is not in a cycle or in a black hole
+            label.put(flow, "Cleared");
+        }
+        return pointsToLiveEntry;
     }
 
+    private boolean analyzeInstruction(Instruction i, FlowEntry flow) {
+        boolean pointsToLiveEntry = false;
+        Instructions.OutputInstruction output = (Instructions.OutputInstruction) i;
+        PortNumber port = output.port();
+        PortNumber outPort = null;
+
+        DeviceId egress = null;
+        boolean hasHost = false;
+
+        ConnectPoint portPt = new ConnectPoint(flow.deviceId(), port);
+        for (Link l: linkService.getEgressLinks(portPt)) {
+            if (l.dst().elementId() instanceof DeviceId) {
+                egress = l.dst().deviceId();
+                outPort = l.dst().port();
+            } else if (l.dst().elementId() instanceof HostId) {
+                //the port leads to a host: therefore it is not a dead link
+                pointsToLiveEntry = true;
+                hasHost = true;
+            }
+        }
+        if (!topologyService.isInfrastructure(topologyService.currentTopology(), portPt) && egress == null) {
+            pointsToLiveEntry = true;
+            hasHost = true;
+        }
+        if (hasHost) {
+            return pointsToLiveEntry;
+        }
+        if (egress == null) {
+            //the port that the flow instructions tells you to send the packet
+            //to doesn't exist or is a controller port
+            label.put(flow, "NA");
+            return pointsToLiveEntry;
+        }
+
+        Iterable<FlowEntry> dstFlowTable = flowRuleService.getFlowEntries(egress);
+
+        Set<Criterion> flowCriteria = flow.selector().criteria();
+
+        //filter the criteria in order to remove port dependency
+        Set<Criterion> filteredCriteria = new HashSet<>();
+        for (Criterion criterion : flowCriteria) {
+            if (!(criterion instanceof PortCriterion)) {
+                filteredCriteria.add(criterion);
+            }
+        }
+
+        //ensure that the in port is equal to the port that it is coming in from
+        filteredCriteria.add(Criteria.matchInPort(outPort));
+
+        for (FlowEntry entry: dstFlowTable) {
+            if (ignoredFlows.contains(entry)) {
+                continue;
+            }
+            if (filteredCriteria.containsAll(entry.selector().criteria())) {
+                dfs(entry);
+
+                if (!"Black Hole".equals(label.get(entry))) {
+                    //this entry is "live" i.e not a black hole
+                    pointsToLiveEntry = true;
+                }
+            }
+        }
+        return pointsToLiveEntry;
+    }
+    public String flowEntryRepresentation(FlowEntry flow) {
+        return "Device: " + flow.deviceId() + ", " + flow.selector().criteria() + ", " + flow.treatment().immediate();
+    }
 }
diff --git a/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644 (file)
index 0000000..93cb27e
--- /dev/null
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.onosproject.flowanalyzer.FlowAnalysisCommand"/>
+        </command>
+
+    </command-bundle>
+</blueprint>
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java
new file mode 100644 (file)
index 0000000..4ea3aa4
--- /dev/null
@@ -0,0 +1,28 @@
+package org.onosproject.flowanalyzer;
+
+import org.onlab.graph.MutableAdjacencyListsGraph;
+import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.net.topology.TopologyGraph;
+import org.onosproject.net.topology.TopologyVertex;
+
+import java.util.Set;
+
+/**
+ * Default implementation of an immutable topology graph based on a generic
+ * implementation of adjacency lists graph.
+ */
+public class DefaultMutableTopologyGraph
+        extends MutableAdjacencyListsGraph<TopologyVertex, TopologyEdge>
+        implements TopologyGraph {
+
+    /**
+     * Creates a topology graph comprising of the specified vertexes and edges.
+     *
+     * @param vertexes set of graph vertexes
+     * @param edges    set of graph edges
+     */
+    public DefaultMutableTopologyGraph(Set<TopologyVertex> vertexes, Set<TopologyEdge> edges) {
+        super(vertexes, edges);
+    }
+
+}
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java
new file mode 100644 (file)
index 0000000..faa2f5f
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.flowanalyzer;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleExtPayLoad;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.topology.TopologyService;
+
+import java.util.Arrays;
+import java.util.TreeSet;
+
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Created by nikcheerla on 7/20/15.
+ */
+public class FlowAnalyzerTest {
+
+    FlowRuleService flowRuleService = new MockFlowRuleService();
+    TopologyService topologyService;
+    MockLinkService linkService = new MockLinkService();
+
+    @Test
+    @Ignore("This needs to be reworked to be more robust")
+    public void basic() {
+        flowRuleService = new MockFlowRuleService();
+        flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 90));
+        flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 100));
+        flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 150));
+        flowRuleService.applyFlowRules(genFlow("ATL-002", 80, 70));
+        flowRuleService.applyFlowRules(genFlow("ATL-003", 120, 130));
+        flowRuleService.applyFlowRules(genFlow("ATL-004", 50));
+        flowRuleService.applyFlowRules(genFlow("ATL-005", 140, 10));
+
+        linkService.addLink("H00:00:00:00:00:0660", 160, "ATL-005", 140);
+        linkService.addLink("ATL-005", 10, "ATL-004", 40);
+        linkService.addLink("ATL-004", 50, "ATL-002", 80);
+        linkService.addLink("ATL-002", 70, "ATL-001", 110);
+        linkService.addLink("ATL-001", 150, "H00:00:00:00:00:0770", 170);
+        linkService.addLink("ATL-001", 90, "ATL-004", 30);
+        linkService.addLink("ATL-001", 100, "ATL-003", 120);
+        linkService.addLink("ATL-003", 130, "ATL-005", 20);
+
+        topologyService = new MockTopologyService(linkService.createdGraph);
+
+        FlowAnalyzer flowAnalyzer = new FlowAnalyzer();
+        flowAnalyzer.flowRuleService = flowRuleService;
+        flowAnalyzer.linkService = linkService;
+        flowAnalyzer.topologyService = topologyService;
+
+        String labels = flowAnalyzer.analysisOutput();
+        String correctOutput = "Flow Rule: Device: atl-005, [IN_PORT{port=140}], [OUTPUT{port=10}]\n" +
+                "Analysis: Cleared!\n" +
+                "\n" +
+                "Flow Rule: Device: atl-003, [IN_PORT{port=120}], [OUTPUT{port=130}]\n" +
+                "Analysis: Black Hole!\n" +
+                "\n" +
+                "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=90}]\n" +
+                "Analysis: Cycle Critical Point!\n" +
+                "\n" +
+                "Flow Rule: Device: atl-004, [], [OUTPUT{port=50}]\n" +
+                "Analysis: Cycle!\n" +
+                "\n" +
+                "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=150}]\n" +
+                "Analysis: Cleared!\n" +
+                "\n" +
+                "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=100}]\n" +
+                "Analysis: Black Hole!\n" +
+                "\n" +
+                "Flow Rule: Device: atl-002, [IN_PORT{port=80}], [OUTPUT{port=70}]\n" +
+                "Analysis: Cycle!\n";
+        assertEquals("Wrong labels", new TreeSet(Arrays.asList(labels.replaceAll("\\s+", "").split("!"))),
+                     new TreeSet(Arrays.asList(correctOutput.replaceAll("\\s+", "").split("!"))));
+    }
+
+    public FlowRule genFlow(String d, long inPort, long outPort) {
+        DeviceId device = DeviceId.deviceId(d);
+        TrafficSelector ts = DefaultTrafficSelector.builder().matchInPort(PortNumber.portNumber(inPort)).build();
+        TrafficTreatment tt = DefaultTrafficTreatment.builder()
+                .add(Instructions.createOutput(PortNumber.portNumber(outPort))).build();
+        return new DefaultFlowRule(device, ts, tt, 1, new DefaultApplicationId(5000, "of"),
+                                   50000, true, FlowRuleExtPayLoad.flowRuleExtPayLoad(new byte[5]));
+    }
+    public FlowRule genFlow(String d, long outPort) {
+        DeviceId device = DeviceId.deviceId(d);
+        TrafficSelector ts = DefaultTrafficSelector.builder().build();
+        TrafficTreatment tt = DefaultTrafficTreatment.builder()
+                .add(Instructions.createOutput(PortNumber.portNumber(outPort))).build();
+        return new DefaultFlowRule(device, ts, tt, 1, new DefaultApplicationId(5000, "of"),
+                                   50000, true, FlowRuleExtPayLoad.flowRuleExtPayLoad(new byte[5]));
+    }
+
+}
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java
new file mode 100644 (file)
index 0000000..40bb004
--- /dev/null
@@ -0,0 +1,103 @@
+package org.onosproject.flowanalyzer;
+
+import com.google.common.collect.Sets;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleServiceAdapter;
+
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+/**
+ * Created by nikcheerla on 7/20/15.
+ */
+
+public class MockFlowRuleService extends FlowRuleServiceAdapter {
+
+    final Set<FlowRule> flows = Sets.newHashSet();
+    boolean success;
+
+    int errorFlow = -1;
+    public void setErrorFlow(int errorFlow) {
+        this.errorFlow = errorFlow;
+    }
+
+    public void setFuture(boolean success) {
+        this.success = success;
+    }
+
+    @Override
+    public void apply(FlowRuleOperations ops) {
+        AtomicBoolean thisSuccess = new AtomicBoolean(success);
+        ops.stages().forEach(stage -> stage.forEach(flow -> {
+            if (errorFlow == flow.rule().id().value()) {
+                thisSuccess.set(false);
+            } else {
+                switch (flow.type()) {
+                    case ADD:
+                    case MODIFY: //TODO is this the right behavior for modify?
+                        flows.add(flow.rule());
+                        break;
+                    case REMOVE:
+                        flows.remove(flow.rule());
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }));
+        if (thisSuccess.get()) {
+            ops.callback().onSuccess(ops);
+        } else {
+            ops.callback().onError(ops);
+        }
+    }
+
+    @Override
+    public int getFlowRuleCount() {
+        return flows.size();
+    }
+
+    @Override
+    public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+        return flows.stream()
+                .filter(flow -> flow.deviceId().equals(deviceId))
+                .map(DefaultFlowEntry::new)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public void applyFlowRules(FlowRule... flowRules) {
+        for (FlowRule flow : flowRules) {
+            flows.add(flow);
+        }
+    }
+
+    @Override
+    public void removeFlowRules(FlowRule... flowRules) {
+        for (FlowRule flow : flowRules) {
+            flows.remove(flow);
+        }
+    }
+
+    @Override
+    public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
+        return flows.stream()
+                .filter(flow -> flow.appId() == id.id())
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Iterable<FlowRule> getFlowRulesByGroupId(ApplicationId appId, short groupId) {
+        return flows.stream()
+                .filter(flow -> flow.appId() == appId.id() && flow.groupId().id() == groupId)
+                .collect(Collectors.toList());
+    }
+}
+
+
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java
new file mode 100644 (file)
index 0000000..2171c6f
--- /dev/null
@@ -0,0 +1,183 @@
+package org.onosproject.flowanalyzer;
+
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.link.LinkServiceAdapter;
+import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.TopologyVertex;
+
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashSet;
+
+import static org.onosproject.net.Link.State.ACTIVE;
+
+
+/**
+ * Created by nikcheerla on 7/21/15.
+ */
+public class MockLinkService extends LinkServiceAdapter {
+    DefaultMutableTopologyGraph createdGraph = new DefaultMutableTopologyGraph(new HashSet<>(), new HashSet<>());
+    List<Link> links = new ArrayList<>();
+
+    @Override
+    public int getLinkCount() {
+        return links.size();
+    }
+
+    @Override
+    public Iterable<Link> getLinks() {
+        return links;
+    }
+
+    @Override
+    public Set<Link> getDeviceLinks(DeviceId deviceId) {
+        Set<Link> egress = getDeviceEgressLinks(deviceId);
+        egress.addAll(getDeviceIngressLinks(deviceId));
+        return egress;
+    }
+
+    @Override
+    public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
+        Set<Link> setL = new HashSet<>();
+        for (Link l: links) {
+            if (l.src().elementId() instanceof DeviceId && l.src().deviceId().equals(deviceId)) {
+                setL.add(l);
+            }
+        }
+        return setL;
+    }
+
+    @Override
+    public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
+        Set<Link> setL = new HashSet<>();
+        for (Link l: links) {
+            if (l.dst().elementId() instanceof DeviceId && l.dst().deviceId().equals(deviceId)) {
+                setL.add(l);
+            }
+        }
+        return setL;
+    }
+
+
+    @Override
+    public Set<Link> getEgressLinks(ConnectPoint pt) {
+        Set<Link> setL = new HashSet<>();
+        for (Link l: links) {
+            if (l.src().equals(pt)) {
+                setL.add(l);
+            }
+        }
+        return setL;
+    }
+
+    @Override
+    public Set<Link> getIngressLinks(ConnectPoint pt) {
+        Set<Link> setL = new HashSet<>();
+        for (Link l: links) {
+            if (l.dst().equals(pt)) {
+                setL.add(l);
+            }
+        }
+        return setL;
+    }
+
+    @Override
+    public Set<Link> getLinks(ConnectPoint pt) {
+        Set<Link> setL = new HashSet<>();
+        for (Link l: links) {
+            if (l.src().equals(pt) || l.dst().equals(pt)) {
+                setL.add(l);
+            }
+        }
+        return setL;
+    }
+
+    public void addLink(String device, long port, String device2, long port2) {
+        ElementId d1;
+        if (device.charAt(0) == 'H') {
+            device = device.substring(1, device.length());
+            d1 = HostId.hostId(device);
+        } else {
+            d1 = DeviceId.deviceId(device);
+        }
+
+        ElementId d2;
+        if (device2.charAt(0) == 'H') {
+            d2 = HostId.hostId(device2.substring(1, device2.length()));
+        } else {
+            d2 = DeviceId.deviceId(device2);
+        }
+
+        ConnectPoint src = new ConnectPoint(d1, PortNumber.portNumber(port));
+        ConnectPoint dst = new ConnectPoint(d2, PortNumber.portNumber(port2));
+        Link curLink;
+        curLink = new Link() {
+            @Override
+            public ConnectPoint src() {
+                return src;
+            }
+
+            @Override
+            public ConnectPoint dst() {
+                return dst;
+            }
+
+            @Override
+            public boolean isDurable() {
+                return true;
+            }
+
+            @Override
+            public Annotations annotations() {
+                return null;
+            }
+
+            @Override
+            public Type type() {
+                return null;
+            }
+
+            @Override
+            public ProviderId providerId() {
+                return null;
+            }
+
+            @Override
+            public State state() {
+                return ACTIVE;
+            }
+        };
+        links.add(curLink);
+        if (d1 instanceof DeviceId && d2 instanceof DeviceId) {
+            TopologyVertex v1 = () -> (DeviceId) d1, v2 = () -> (DeviceId) d2;
+            createdGraph.addVertex(v1);
+            createdGraph.addVertex(v2);
+            createdGraph.addEdge(new TopologyEdge() {
+                @Override
+                public Link link() {
+                    return curLink;
+                }
+
+                @Override
+                public TopologyVertex src() {
+                    return v1;
+                }
+
+                @Override
+                public TopologyVertex dst() {
+                    return v2;
+                }
+            });
+        }
+    }
+
+
+}
diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java
new file mode 100644 (file)
index 0000000..0d25c97
--- /dev/null
@@ -0,0 +1,21 @@
+package org.onosproject.flowanalyzer;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyGraph;
+import org.onosproject.net.topology.TopologyServiceAdapter;
+
+
+/**
+ * Created by nikcheerla on 7/20/15.
+ */
+public class MockTopologyService extends TopologyServiceAdapter {
+    TopologyGraph cur;
+
+    public MockTopologyService(TopologyGraph g) {
+        cur = g;
+    }
+
+    @Override
+    public TopologyGraph getGraph(Topology topology) {
+        return cur;
+    }
+}
diff --git a/framework/src/onos/apps/igmp/pom.xml b/framework/src/onos/apps/igmp/pom.xml
new file mode 100644 (file)
index 0000000..7980d2c
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-igmp</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Internet Group Message Protocol</description>
+
+    <properties>
+        <onos.app.name>org.onosproject.igmp</onos.app.name>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- This is needed by ComponentContext, used for tunable configuration -->
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <version>1.9.8</version>
+            <scope>provided</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>
+                            ${project.groupId}.${project.artifactId}
+                        </Bundle-SymbolicName>
+                        <Import-Package>
+                            org.slf4j,
+                            org.osgi.framework,
+                            org.apache.commons.lang.math.*,
+                            com.google.common.*,
+                            org.onlab.packet.*,
+                            org.onlab.rest.*,
+                            org.onosproject.*,
+                            org.onosproject.mfwd.impl.*;
+                            org.onlab.util.*,
+                            org.jboss.netty.util.*
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.5.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java
new file mode 100644 (file)
index 0000000..ae539c6
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.igmp.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IGMP;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+/**
+ * Internet Group Management Protocol.
+ */
+@Component(immediate = true)
+public class IGMPComponent {
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    private IGMPPacketProcessor processor = new IGMPPacketProcessor();
+    private static ApplicationId appId;
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication("org.onosproject.igmp");
+
+        packetService.addProcessor(processor, PacketProcessor.director(1));
+
+        // Build a traffic selector for all multicast traffic
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+        selector.matchIPProtocol(IPv4.PROTOCOL_IGMP);
+        packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        packetService.removeProcessor(processor);
+        processor = null;
+        log.info("Stopped");
+    }
+
+    /**
+     * Packet processor responsible for handling IGMP packets.
+     */
+    private class IGMPPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            // Stop processing if the packet has been handled, since we
+            // can't do any more to it.
+            if (context.isHandled()) {
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            Ethernet ethPkt = pkt.parsed();
+            if (ethPkt == null) {
+                return;
+            }
+
+            /*
+             * IPv6 MLD packets are handled by ICMP6. We'll only deal
+             * with IPv4.
+             */
+            if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
+                return;
+            }
+
+            IPv4 ip = (IPv4) ethPkt.getPayload();
+            IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
+            IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
+            log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() +
+                    "\tingress port: " + context.inPacket().receivedFrom().toString());
+
+            if (ip.getProtocol() != IPv4.PROTOCOL_IGMP) {
+                log.error("IGMP Picked up a non IGMP packet.");
+                return;
+            }
+
+            IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4");
+            if (!mcast.contains(gaddr)) {
+                log.error("IGMP Picked up a non multicast packet.");
+                return;
+            }
+
+            if (mcast.contains(saddr)) {
+                log.error("IGMP Picked up a packet with a multicast source address.");
+                return;
+            }
+            IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
+            IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
+
+            IGMP igmp = (IGMP) ip.getPayload();
+            switch (igmp.getIgmpType()) {
+
+                case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
+                    IGMPProcessMembership.processMembership(igmp, pkt.receivedFrom());
+                    break;
+
+                case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
+                    IGMPProcessQuery.processQuery(igmp, pkt.receivedFrom());
+                    break;
+
+                case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
+                case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
+                case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
+                    log.debug("IGMP version 1 & 2 message types are not currently supported. Message type: " +
+                            igmp.getIgmpType());
+                    break;
+
+                default:
+                    log.debug("Unkown IGMP message type: " + igmp.getIgmpType());
+                    break;
+            }
+        }
+    }
+}
diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java
new file mode 100644 (file)
index 0000000..3d7d603
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.igmp.impl;
+
+import org.onlab.packet.IGMP;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Process an IGMP Membership Report.
+ */
+public final class IGMPProcessMembership {
+
+    // Hide the default constructor.
+    private IGMPProcessMembership() {
+    }
+
+    /**
+     * Process the IGMP Membership report.
+     *
+     * @param igmp the deserialized IGMP message.
+     * @param receivedFrom the ConnectPoint this message came from.
+     */
+    public static void processMembership(IGMP igmp, ConnectPoint receivedFrom) {
+    }
+
+}
diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java
new file mode 100644 (file)
index 0000000..eb25679
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.igmp.impl;
+
+import org.onlab.packet.IGMP;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Process IGMP Query messages.
+ */
+public final class IGMPProcessQuery {
+
+    // Hide the default constructor.
+    private IGMPProcessQuery() {
+    }
+
+    /**
+     * Process the IGMP Membership Query message.
+     *
+     * @param igmp The deserialzed IGMP message
+     * @param receivedFrom the ConnectPoint this message came from.
+     */
+    public static void processQuery(IGMP igmp, ConnectPoint receivedFrom) {
+    }
+
+}
diff --git a/framework/src/onos/apps/mfwd/pom.xml b/framework/src/onos/apps/mfwd/pom.xml
new file mode 100644 (file)
index 0000000..835de83
--- /dev/null
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-mfwd</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Multicast forwarding application</description>
+
+    <properties>
+        <onos.app.name>org.onosproject.mfwd</onos.app.name>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty</artifactId>
+            <version>3.9.0.Final</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-rest</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-rest</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <_wab>src/main/webapp/</_wab>
+                        <Bundle-SymbolicName>
+                            ${project.groupId}.${project.artifactId}
+                        </Bundle-SymbolicName>
+                        <Import-Package>
+                            org.slf4j,
+                            org.osgi.framework,
+                            javax.ws.rs,javax.ws.rs.core,
+                            com.sun.jersey.api.core,
+                            com.sun.jersey.spi.container.servlet,
+                            com.sun.jersey.server.impl.container.servlet,
+                            com.fasterxml.jackson.databind,
+                            com.fasterxml.jackson.databind.node,
+                            org.apache.commons.lang.math.*,
+                            org.apache.karaf.shell.commands,
+                            org.apache.karaf.shell.console,
+                            com.google.common.*,
+                            org.onlab.packet.*,
+                            org.onlab.rest.*,
+                            org.onosproject.*,
+                            org.onlab.util.*,
+                            org.jboss.netty.util.*
+                        </Import-Package>
+                        <Web-ContextPath>${web.context}</Web-ContextPath>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
+
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java
new file mode 100644 (file)
index 0000000..ae5d9e9
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.mfwd.impl.McastRouteTable;
+
+/**
+ * Deletes a multicast route.
+ */
+@Command(scope = "onos", name = "mcast-delete",
+        description = "Delete a multicast route flow")
+public class McastDeleteCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "sAddr",
+            description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry",
+            required = true, multiValued = false)
+    String sAddr = null;
+
+    @Argument(index = 1, name = "gAddr",
+            description = "IP Address of the multicast group",
+            required = true, multiValued = false)
+    String gAddr = null;
+
+    @Override
+    protected void execute() {
+        McastRouteTable mrib = McastRouteTable.getInstance();
+        mrib.removeRoute(sAddr, gAddr);
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java
new file mode 100644 (file)
index 0000000..7260fde
--- /dev/null
@@ -0,0 +1,72 @@
+/*\r
+ * Copyright 2014-2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.onosproject.mfwd.cli;\r
+\r
+import org.apache.karaf.shell.commands.Argument;\r
+import org.apache.karaf.shell.commands.Command;\r
+import org.onosproject.cli.AbstractShellCommand;\r
+\r
+import org.onosproject.mfwd.impl.McastConnectPoint;\r
+import org.onosproject.mfwd.impl.McastRouteBase;\r
+import org.onosproject.mfwd.impl.McastRouteTable;\r
+\r
+/**\r
+ * Installs a source, multicast group flow.\r
+ */\r
+@Command(scope = "onos", name = "mcast-join",\r
+         description = "Installs a source, multicast group flow")\r
+public class McastJoinCommand extends AbstractShellCommand {\r
+\r
+    @Argument(index = 0, name = "sAddr",\r
+              description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry",\r
+              required = true, multiValued = false)\r
+    String sAddr = null;\r
+\r
+    @Argument(index = 1, name = "gAddr",\r
+              description = "IP Address of the multicast group",\r
+              required = true, multiValued = false)\r
+    String gAddr = null;\r
+\r
+    @Argument(index = 2, name = "ingressPort",\r
+            description = "Ingress port and Egress ports",\r
+            required = false, multiValued = false)\r
+    String ingressPort = null;\r
+\r
+    @Argument(index = 3, name = "ports",\r
+              description = "Ingress port and Egress ports",\r
+              required = false, multiValued = true)\r
+    String[] ports = null;\r
+\r
+    @Override\r
+    protected void execute() {\r
+        McastRouteTable mrib = McastRouteTable.getInstance();\r
+        McastRouteBase mr = mrib.addRoute(sAddr, gAddr);\r
+\r
+        // Port format "of:0000000000000023/4"\r
+        if (ingressPort != null) {\r
+            String inCP = ingressPort;\r
+            log.debug("Ingress port provided: " + inCP);\r
+            mr.addIngressPoint(inCP);\r
+        }\r
+\r
+        for (int i = 0; i < ports.length; i++) {\r
+            String egCP = ports[i];\r
+            log.debug("Egress port provided: " + egCP);\r
+            mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);\r
+        }\r
+        print("Added the mcast route");\r
+    }\r
+}\r
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java
new file mode 100644 (file)
index 0000000..7fa3a13
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.cli;
+
+import org.apache.karaf.shell.commands.Command;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.mfwd.impl.McastRouteTable;
+import org.onosproject.mfwd.impl.MRibCodec;
+
+import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Displays the source, multicast group flows entries.
+ */
+@Command(scope = "onos", name = "mcast-show", description = "Displays the source, multicast group flows")
+public class McastShowCommand extends AbstractShellCommand {
+
+    private final Logger log = getLogger(getClass());
+    private static final String MCAST_GROUP = "mcastgroup";
+
+    @Override
+    protected void execute() {
+        McastRouteTable mrt = McastRouteTable.getInstance();
+        if (outputJson()) {
+            print("%s", json(mrt));
+        } else {
+            printMrib4(mrt);
+        }
+    }
+
+    public JsonNode json(McastRouteTable mrt) {
+        ObjectNode pushContent = new MRibCodec().encode(mrt , this);
+        return pushContent;
+    }
+
+    /**
+     * Displays multicast route table entries.
+     *
+     * @param mrt Mutlicast Route Table
+     */
+    protected void printMrib4(McastRouteTable mrt) {
+        print(mrt.printMcastRouteTable());
+    }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java
new file mode 100644 (file)
index 0000000..7b5ed39
--- /dev/null
@@ -0,0 +1,5 @@
+/**\r
+ * Sample Multicast forwarding framework using intents.\r
+ */\r
+package org.onosproject.mfwd.cli;\r
+\r
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java
new file mode 100644 (file)
index 0000000..c4f1852
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+
+import org.onlab.packet.IpPrefix;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+
+import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Encode and Decode the Multicast Route Table in JSON for CLI and REST commands.
+ */
+public class MRibCodec extends JsonCodec<McastRouteTable> {
+
+    private final Logger log = getLogger(getClass());
+    private static final String SOURCE_ADDRESS = "sourceAddress";
+    private static final String GROUP_ADDRESS = "groupAddress";
+    private static final String INGRESS_POINT = "ingressPoint";
+    private static final String EGRESS_POINT = "egressPoint";
+    private static final String MCASTCONNECTPOINT = "McastConnectPoint";
+    private static final String ELEMENTID = "elementId";
+    private static final String PORTNUMBER = "portNumber";
+    private static final String MCAST_GROUP = "mcastGroup";
+
+    /**
+     * Encode the MRIB into json format.
+     *
+     * @param mcastRouteTable McastRouteTable
+     * @param context CodecContext
+     * @return result ObjectNode
+     */
+    @Override
+    public ObjectNode encode(McastRouteTable mcastRouteTable, CodecContext context) {
+
+        final JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
+        final ObjectNode macastRouteTabNode = nodeFactory.objectNode();
+        ArrayNode mcastGroupNode = context.mapper().createArrayNode();
+        Optional<McastRouteTable> mcastRouteTabOpt = Optional.ofNullable(mcastRouteTable);
+
+        //checking whether the McastRouteTable is present.
+        if (mcastRouteTabOpt.isPresent()) {
+            Map<IpPrefix, McastRouteGroup> mrib4 = mcastRouteTabOpt.get().getMrib4();
+            Optional<Map<IpPrefix, McastRouteGroup>> mrib4Opt = Optional.ofNullable(mrib4);
+
+            //checking whether the mrib4 is present.
+            if (mrib4Opt.isPresent()) {
+
+                for (McastRouteGroup mg : mrib4Opt.get().values()) {
+                    Collection<McastRouteSource> mcastRoute = mg.getSources().values();
+                    Optional<Collection<McastRouteSource>> mcastRouteOpt = Optional.ofNullable(mcastRoute);
+
+                    //checking whether the McastRouteSource List is present.
+                    if (mcastRouteOpt.isPresent()) {
+                        for (McastRouteSource mcastRouteSource : mcastRouteOpt.get()) {
+                            mcastGroupNode.add(createMcastGroupNode(mcastRouteSource, context));
+                        }
+                        macastRouteTabNode.put(MCAST_GROUP, mcastGroupNode);
+                    }
+                }
+            }
+        }
+        return macastRouteTabNode;
+    }
+    /**
+     * Method for creating the McastGroup object node.
+     *
+     * @param mcastRouteSource McastRouteSource
+     */
+    private ObjectNode createMcastGroupNode(McastRouteSource mcastRouteSource, CodecContext context) {
+
+        final ObjectNode mcastGroupNode = context.mapper().createObjectNode();
+        final ObjectNode ingressNode = context.mapper().createObjectNode();
+        final ObjectNode egressNode = context.mapper().createObjectNode();
+        final ArrayNode jsonLabelIds = context.mapper().createArrayNode();
+        final String sAddr = mcastRouteSource.getSaddr().toString();
+        final String gAddr = mcastRouteSource.getGaddr().toString();
+
+        Optional<String> saddrOpt = Optional.ofNullable(sAddr);
+        Optional<String> gaddrOpt = Optional.ofNullable(gAddr);
+
+        //checking source address and group address are present.
+        if (saddrOpt.isPresent() && gaddrOpt.isPresent()) {
+            mcastGroupNode.put(SOURCE_ADDRESS, saddrOpt.get().toString());
+            mcastGroupNode.put(GROUP_ADDRESS, gaddrOpt.get().toString());
+            McastConnectPoint mcastIngCP = mcastRouteSource.getIngressPoint();
+            Optional<McastConnectPoint> mcastIngCPOpt = Optional.ofNullable(mcastIngCP);
+
+            //checking whether the ingress connection point is present.
+            if (mcastIngCPOpt.isPresent()) {
+                ingressNode.put(MCASTCONNECTPOINT, mcastConnectPoint(mcastIngCPOpt.get(), context));
+            }
+
+            mcastGroupNode.put(INGRESS_POINT , ingressNode);
+            Set<McastConnectPoint> mcastEgCPSet = mcastRouteSource.getEgressPoints();
+            Optional<Set<McastConnectPoint>> mcastEgCPOpt = Optional.ofNullable(mcastEgCPSet);
+
+            //checking whether the egress connection points are present.
+            if (mcastEgCPOpt.isPresent()) {
+                for (final McastConnectPoint mcastConnectPoint : mcastEgCPOpt.get()) {
+                    jsonLabelIds.add(mcastConnectPoint(mcastConnectPoint, context));
+                }
+            }
+
+            egressNode.put(MCASTCONNECTPOINT , jsonLabelIds);
+            mcastGroupNode.put(EGRESS_POINT , egressNode);
+        }
+        return mcastGroupNode;
+    }
+
+    /**
+     * Method for creating the McastConnectPoint object node.
+     *
+     * @param mcastConnectPoint McastConnectPoint
+     * @param context CodecContext
+     * @return mcastCpNode ObjectNode
+     */
+    private ObjectNode mcastConnectPoint(McastConnectPoint mcastConnectPoint, CodecContext context) {
+        final ObjectNode mcastCpNode = context.mapper().createObjectNode();
+        mcastCpNode.put(ELEMENTID , mcastConnectPoint.getConnectPoint().elementId().toString());
+        mcastCpNode.put(PORTNUMBER , mcastConnectPoint.getConnectPoint().port().toLong());
+        return mcastCpNode;
+    }
+
+    /**
+     * Decode json format and insert into the flow table.
+     *
+     * @param json ObjectNode
+     * @param context CodecContext
+     * @return mr McastRouteBase
+     */
+    @Override
+    public McastRouteTable decode(ObjectNode json, CodecContext context) {
+
+        String macAddr = null;
+        String portNo = null;
+        String sAddr = json.path(SOURCE_ADDRESS).asText();
+        String gAddr = json.path(GROUP_ADDRESS).asText();
+        JsonNode inPntObjNode = (JsonNode) json.path(INGRESS_POINT);
+        JsonNode egPntArrNode = (JsonNode) json.path(EGRESS_POINT);
+
+        log.debug("sAddr :" + sAddr + " gAddr :" + gAddr + " inPntObjNode :" + inPntObjNode);
+        log.debug("egPntArrNode :" + egPntArrNode.toString());
+
+        McastRouteTable mrib = McastRouteTable.getInstance();
+        McastRouteBase mr = mrib.addRoute(sAddr, gAddr);
+        Optional<JsonNode> inPntOpt = Optional.ofNullable(inPntObjNode);
+
+        if (inPntOpt.isPresent()) {
+
+            JsonNode inMcastCP = inPntOpt.get().path(MCASTCONNECTPOINT);
+            Optional<JsonNode> inCpOpt = Optional.ofNullable(inMcastCP);
+
+            if (inCpOpt.isPresent()) {
+                macAddr = inCpOpt.get().path(ELEMENTID).asText();
+                portNo = inCpOpt.get().path(PORTNUMBER).asText();
+                mr.addIngressPoint(macAddr + "/" + Long.parseLong(portNo));
+            }
+        }
+
+        Optional<JsonNode> egPntOpt = Optional.ofNullable(egPntArrNode);
+
+        if (egPntOpt.isPresent()) {
+            JsonNode egMcastCP = egPntOpt.get().path(MCASTCONNECTPOINT);
+            Optional<JsonNode> egMcCpOpt = Optional.ofNullable(egMcastCP);
+
+            if (egMcCpOpt.isPresent()) {
+                Iterator<JsonNode> egCpIt = egMcCpOpt.get().elements();
+
+                while (egCpIt.hasNext()) {
+
+                    JsonNode egMcastCPObj = egCpIt.next();
+                    Optional<JsonNode> egMcCpObOpt = Optional.ofNullable(egMcastCPObj);
+                    if (egMcCpObOpt.isPresent()) {
+                        macAddr = egMcCpObOpt.get().path(ELEMENTID).asText();
+                        portNo = egMcCpObOpt.get().path(PORTNUMBER).asText();
+                        log.debug("macAddr egPort : " + macAddr + " portNo egPort :" + portNo);
+                        mr.addEgressPoint(macAddr + "/" + Long.parseLong(portNo), McastConnectPoint.JoinSource.STATIC);
+                    }
+                }
+            }
+        }
+       return mrib;
+    }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java
new file mode 100644 (file)
index 0000000..e2a6ff0
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.onosproject.net.ConnectPoint;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Mulitcast ConnectPoint adds a variable to track the usage
+ * of these multicast endpoints.
+ */
+public class McastConnectPoint {
+
+    private ConnectPoint connectPoint;
+
+    public enum JoinSource {
+        STATIC, IGMP, PIM;
+    }
+
+    public EnumSet<JoinSource> interest = EnumSet.noneOf(JoinSource.class);
+
+    public McastConnectPoint(ConnectPoint cp) {
+        this.connectPoint = cp;
+    }
+
+    public McastConnectPoint(ConnectPoint cp, JoinSource src) {
+        this.connectPoint = cp;
+        interest.add(src);
+    }
+
+    public McastConnectPoint(String connectPoint, JoinSource src) {
+        ConnectPoint cp = ConnectPoint.deviceConnectPoint(connectPoint);
+        this.connectPoint = cp;
+        this.interest.add(src);
+    }
+
+    /**
+     * Get the connect point.
+     *
+     * @return connectPoint
+     */
+    public ConnectPoint getConnectPoint() {
+        return connectPoint;
+    }
+
+    /**
+     * Get the sources of interest for this egressPoint.
+     *
+     * @return interest flags
+     */
+    public Set<JoinSource> getInterest() {
+        return interest;
+    }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java
new file mode 100644 (file)
index 0000000..f5bd1e0
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IPv4;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+/**
+ * WORK-IN-PROGRESS: The multicast forwarding application using intent framework.
+ */
+@Component(immediate = true)
+public class McastForwarding {
+
+    private final Logger log = getLogger(getClass());
+    private final IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4");
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    private ReactivePacketProcessor processor = new ReactivePacketProcessor();
+    private McastRouteTable mrib;
+    private static ApplicationId appId;
+
+    /**
+     * Active MulticastForwardingIntent.
+     */
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication("org.onosproject.mfwd");
+
+        packetService.addProcessor(processor, PacketProcessor.director(2));
+
+        // Build a traffic selector for all multicast traffic
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+        selector.matchIPDst(mcast);
+        packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+
+        mrib = McastRouteTable.getInstance();
+        log.info("Started");
+    }
+
+    /**
+     * Deactivate Multicast Forwarding Intent.
+     */
+    @Deactivate
+    public void deactivate() {
+        packetService.removeProcessor(processor);
+        processor = null;
+        log.info("Stopped");
+    }
+
+    /**
+     * Get the application ID, used by the McastIntentManager.
+     *
+     * @return the application ID
+     */
+    public static ApplicationId getAppId() {
+        return appId;
+    }
+
+    /**
+     * Packet processor responsible for forwarding packets along their paths.
+     */
+    private class ReactivePacketProcessor implements PacketProcessor {
+
+        /**
+         * Process incoming packets.
+         *
+         * @param context packet processing context
+         */
+        @Override
+        public void process(PacketContext context) {
+            // Stop processing if the packet has been handled, since we
+            // can't do any more to it.
+            if (context.isHandled()) {
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            Ethernet ethPkt = pkt.parsed();
+
+            if (ethPkt == null) {
+                return;
+            }
+
+            if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4 &&
+                    ethPkt.getEtherType() != Ethernet.TYPE_IPV6) {
+                return;
+            }
+
+            if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
+                // Ignore ipv6 at the moment.
+                return;
+            }
+
+            IPv4 ip = (IPv4) ethPkt.getPayload();
+            IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
+            IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
+
+            log.debug("Packet ({}, {}) has been punted\n" +
+                            "\tingress port: {}\n",
+                    saddr.toString(),
+                    gaddr.toString(),
+                    context.inPacket().receivedFrom().toString());
+
+            if (!mcast.contains(gaddr)) {
+                // Yikes, this is a bad group address
+                return;
+            }
+
+            if (mcast.contains(saddr)) {
+                // Yikes, the source address is multicast
+                return;
+            }
+
+            IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
+            IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
+
+            /*
+             * Do a best match lookup on the (s, g) of the packet. If an entry does
+             * not exist create one and store it's incoming connect point.
+             *
+             * The connect point is deviceId / portId that the packet entered
+             * the SDN network.  This differs from traditional mcast where the
+             * ingress port would be a specific device.
+             */
+            McastRoute entry = mrib.findBestMatch(spfx, gpfx);
+            if (entry == null || entry.getSaddr().equals(IPv4.fromIPv4Address(0))) {
+
+                /*
+                 * Create an entry that we can fast drop.
+                 */
+                entry = mrib.addRoute(spfx, gpfx);
+                entry.addIngressPoint(context.inPacket().receivedFrom());
+            }
+
+            /*
+             * TODO: If we do not have an ingress or any egress connect points we
+             * should set up a fast drop entry.
+             */
+            if (entry.getIngressPoint() == null) {
+                return;
+            }
+
+            if (entry.getEgressPoints().isEmpty()) {
+                return;
+            }
+
+            /*
+             * This is odd, we should not have received a punted packet if an
+             * intent was installed unless the intent was not installed
+             * correctly.  However, we are seeing packets get punted after
+             * the intent has been installed.
+             *
+             * Therefore we are going to forward the packets even if they
+             * should have already been forwarded by the intent fabric.
+             */
+            if (entry.getIntentKey() != null) {
+                return;
+            }
+
+            entry.setIntent();
+            McastIntentManager im = McastIntentManager.getInstance();
+            im.setIntent(entry);
+
+            entry.incrementPuntCount();
+
+            // Send the pack out each of the egress devices & port
+            forwardPacketToDst(context, entry);
+        }
+    }
+
+    /**
+     * Forward the packet to it's multicast destinations.
+     *
+     * @param context The packet context
+     * @param entry The multicast route entry matching this packet
+     */
+    private void forwardPacketToDst(PacketContext context, McastRoute entry) {
+
+        // Send the pack out each of the respective egress ports
+        for (ConnectPoint egress : entry.getEgressConnectPoints()) {
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(egress.port()).build();
+
+            OutboundPacket packet = new DefaultOutboundPacket(
+                    egress.deviceId(),
+                    treatment,
+                    context.inPacket().unparsed());
+
+            packetService.emit(packet);
+        }
+    }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java
new file mode 100644 (file)
index 0000000..90f65c9
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+
+@Component(immediate = true)
+@Service(value = org.onosproject.mfwd.impl.McastIntentManager.class)
+public class McastIntentManager {
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentService intentService;
+
+    private static McastIntentManager instance;
+
+    public McastIntentManager() {
+        instance = this;
+    }
+
+    /**
+     * Active this component.
+     */
+    @Activate
+    public void activate() { }
+
+    /**
+     * Deactivate this component.
+     */
+    @Deactivate
+    public void deactivate() {
+        withdrawAllIntents();
+    }
+
+    /**
+     * Get instance of this intentManager.
+     *
+     * @return the instance of this intent manager.
+     */
+    public static McastIntentManager getInstance() {
+        if (instance == null) {
+            instance = new McastIntentManager();
+        }
+        return instance;
+    }
+
+    /**
+     * Install the PointToMultipoint forwarding intent.
+     *
+     * @param mroute multicast route entry
+     * @return the intent that has been set or null otherwise
+     */
+    public SinglePointToMultiPointIntent setIntent(McastRoute mroute) {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+        if (mroute.getIngressPoint() == null ||
+                mroute.getEgressPoints().isEmpty()) {
+            return null;
+        }
+
+        /*
+         * Match the group AND source addresses.  We will also check ether type to
+         * determine if we are doing ipv4 or ipv6.
+         *
+         * If we really wanted to be pendantic we could put in a
+         * condition to make sure the ethernet MAC address was also
+         * mcast.
+         */
+        selector.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(mroute.getGaddr())
+                .matchIPSrc(mroute.getSaddr());
+
+        SinglePointToMultiPointIntent intent =
+                SinglePointToMultiPointIntent.builder()
+                        .appId(McastForwarding.getAppId())
+                        .selector(selector.build())
+                        .treatment(treatment)
+                        .ingressPoint(mroute.getIngressPoint().getConnectPoint())
+                        .egressPoints(mroute.getEgressConnectPoints()).
+                        build();
+
+        intentService.submit(intent);
+        return intent;
+    }
+
+    /**
+     * Withdraw the intent represented by this route.
+     *
+     * @param mroute the mcast route whose intent we want to remove
+     */
+    public void withdrawIntent(McastRouteBase mroute) {
+        Intent intent = intentService.getIntent(mroute.getIntentKey());
+        intentService.withdraw(intent);
+    }
+
+    /**
+     * Withdraw all intents.
+     *
+     * This will be called from the deactivate method so we don't leave
+     * a mess behind us after we leave.
+     */
+    public void withdrawAllIntents() {
+        for (Intent intent : intentService.getIntents()) {
+            intentService.withdraw(intent);
+        }
+    }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java
new file mode 100644 (file)
index 0000000..12b7e6d
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+
+import java.util.Set;
+
+/**
+ * This McastRouteBase interface is implemented by the McastRouteBase class which
+ * in turn acts as the base class for both the McastRouteGroup and McastRouteSource.
+ */
+interface McastRoute {
+
+    /**
+     * Gets the group addresses.
+     *
+     * @return group address
+     */
+    public IpPrefix getGaddr();
+
+    /**
+     * Gets the source address.
+     *
+     * @return the source address
+     */
+    public IpPrefix getSaddr();
+
+    /**
+     * Determines if this is an IPv4 multicast route.
+     *
+     * @return true if it is an IPv4 route
+     */
+    public boolean isIp4();
+
+    /**
+     * Determines if this is an IPv6 multicast route.
+     *
+     * @return true if it is an IPv6 route
+     */
+    public boolean isIp6();
+
+    /**
+     * Add the ingress ConnectPoint.
+     *
+     * @param cpstr string representing a ConnectPoint
+     * @return whether ingress has been added, only add if ingressPoint is null
+     */
+    public boolean addIngressPoint(String cpstr);
+
+    /**
+     * Add the ingress ConnectPoint.
+     *
+     * @param cp the ConnectPoint of incoming traffic.
+     * @return whether ingress has been added, only add if ingressPoint is null
+     */
+    public boolean addIngressPoint(ConnectPoint cp);
+
+    /**
+     * Get the ingress connect point.
+     *
+     * @return the ingress connect point
+     */
+    public McastConnectPoint getIngressPoint();
+
+    /**
+     * Add an egress connect point.
+     *
+     * @param cp the egress McastConnectPoint to be added
+     * @return return the McastConnectPoint
+     */
+    public McastConnectPoint addEgressPoint(ConnectPoint cp);
+
+    /**
+     * Add an egress connect point.
+     *
+     * @param connectPoint deviceId/portNum
+     * @return return the McastConnectPoint
+     */
+    public McastConnectPoint addEgressPoint(String connectPoint);
+
+    /**
+     * Add an egress connect point.
+     *
+     * @param cp the egress McastConnectPoint to be added
+     * @param interest the protocol that has shown interest in this route
+     * @return return the McastConnectPoint
+     */
+    public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest);
+
+    /**
+     * Add an egress connect point.
+     *
+     * @param connectPoint deviceId/portNum
+     * @param interest the protocol that has shown interest in this route
+     * @return return the McastConnectPoint
+     */
+    public McastConnectPoint addEgressPoint(String connectPoint, McastConnectPoint.JoinSource interest);
+
+    /**
+     * Get the egress connect points.
+     *
+     * @return a set of egress connect points
+     */
+    public Set<McastConnectPoint> getEgressPoints();
+
+    /**
+     * Get the egress connect points.
+     *
+     * @return a set of egress connect points
+     */
+    public Set<ConnectPoint> getEgressConnectPoints();
+
+    /**
+     * Find the egress connect point if it exists.
+     *
+     * @param cp ConnectPoint to search for
+     * @return the connect point when found, null otherwise.
+     */
+    public McastConnectPoint findEgressConnectPoint(ConnectPoint cp);
+
+    /**
+     * remove Interest from a McastConnectPoint.
+     *
+     * @param mcp connect point.
+     * @param interest the protocol interested in this multicast stream
+     * @return whether or not interest was removed
+     */
+    public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest);
+
+    /**
+     * Increment the punt count.
+     */
+    public void incrementPuntCount();
+
+    /**
+     * Get the punt count.
+     *
+     * @return the punt count
+     */
+    public int getPuntCount();
+
+    /**
+     * Have the McastIntentManager create an intent, attempt to
+     * install the intent and then save the key.
+     */
+    public void setIntent();
+
+    /**
+     * Set the Intent key.
+     *
+     * @param intent intent
+     */
+    public void setIntent(SinglePointToMultiPointIntent intent);
+
+    /**
+     * Withdraw the intent if it has been installed.
+     */
+    public void withdrawIntent();
+
+    /**
+     * Get the intent key.
+     *
+     * @return the intentKey
+     */
+    public Key getIntentKey();
+
+    /**
+     * Pretty print the the route.
+     *
+     * @return a pretty string
+     */
+    public String toString();
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
new file mode 100644 (file)
index 0000000..730acfa
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.commons.collections.set.ListOrderedSet;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.Key;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * McastRouteBase base class for McastRouteGroup and McastRouteSource.
+ */
+public class McastRouteBase implements McastRoute {
+    protected final IpPrefix gaddr;
+    protected final IpPrefix saddr;
+
+    protected McastConnectPoint ingressPoint;
+    protected Set<McastConnectPoint> egressPoints;
+
+    protected boolean isGroup = false;
+
+    protected boolean dirty = false;
+
+    /**
+     * How may times has this packet been punted.
+     */
+    private int puntCount = 0;
+
+    /**
+     * If the intentKey is null that means no intent has
+     * been installed.
+     */
+    protected Key intentKey = null;
+
+    /**
+     * Create a multicast route. This is the parent class for both the Group
+     * and the source.
+     *
+     * @param saddr source address
+     * @param gaddr multicast group address
+     */
+    public McastRouteBase(String saddr, String gaddr) {
+        this.gaddr = IpPrefix.valueOf(checkNotNull(gaddr));
+        if (saddr == null || saddr.equals("*")) {
+            this.saddr = IpPrefix.valueOf(0, 0);
+        } else {
+            this.saddr = IpPrefix.valueOf(checkNotNull(gaddr));
+        }
+        this.init();
+    }
+
+    /**
+     * Create a multicast group table entry.
+     * @param gaddr multicast group address
+     */
+    public McastRouteBase(String gaddr) {
+        this("*", gaddr);
+    }
+
+    /**
+     * Set the source and group address value of a (*, G) group.
+     *
+     * @param gpfx the group prefix address
+     */
+    public McastRouteBase(IpPrefix gpfx) {
+        this(IpPrefix.valueOf(0, 0), gpfx);
+    }
+
+    /**
+     * Create a multicast route constructor.
+     *
+     * @param saddr source address
+     * @param gaddr group address
+     */
+    public McastRouteBase(IpPrefix saddr, IpPrefix gaddr) {
+        this.saddr = checkNotNull(saddr);
+        this.gaddr = checkNotNull(gaddr);
+
+        this.init();
+    }
+
+    private void init() {
+        this.isGroup = (this.saddr.prefixLength() == 0);
+        this.ingressPoint = null;
+        this.egressPoints = new HashSet();
+    }
+
+    /**
+     * Get the multicast group address.
+     *
+     * @return the multicast group address
+     */
+    @Override
+    public IpPrefix getGaddr() {
+        return gaddr;
+    }
+
+    /**
+     * Get the multicast source address.
+     *
+     * @return the multicast source address
+     */
+    @Override
+    public IpPrefix getSaddr() {
+        return saddr;
+    }
+
+    /**
+     * Is this an IPv4 multicast route.
+     *
+     * @return true if it is an IPv4 route
+     */
+    @Override
+    public boolean isIp4() {
+        return gaddr.isIp4();
+    }
+
+    /**
+     * Is this an IPv6 multicast route.
+     *
+     * @return true if it is an IPv6 route
+     */
+    @Override
+    public boolean isIp6() {
+        return gaddr.isIp6();
+    }
+
+    /**
+     * Is this a multicast group route?
+     *
+     * @return true if it is a multicast group route.
+     */
+    public boolean isGroup() {
+        return isGroup;
+    }
+
+    /**
+     * @return true if this is (S, G) false if it (*, G).
+     */
+    public boolean isSource() {
+        return (!isGroup);
+    }
+
+    /**
+     * Get the dirty state.
+     *
+     * @return whether this route is dirty or not.
+     */
+    public boolean getDirty() {
+        return this.dirty;
+    }
+
+    /**
+     * Set the dirty state to indicate that something changed.
+     * This may require an update to the flow tables (intents).
+     *
+     * @param dirty set the dirty bit
+     */
+    public void setDirty(boolean dirty) {
+        this.dirty = dirty;
+    }
+
+    /**
+     * Add an ingress point to this route.
+     *
+     * @param ingress incoming connect point
+     * @return whether ingress has been added, only add if ingressPoint is null
+     */
+    public boolean addIngressPoint(ConnectPoint ingress) {
+
+        // Do NOT add the ingressPoint if it is not null.
+        if (this.ingressPoint != null) {
+            // TODO: Log an warning.
+            return false;
+        }
+        this.ingressPoint = new McastConnectPoint(checkNotNull(ingress));
+        setDirty(true);
+        return true;
+    }
+
+    /**
+     * Add or modify the ingress connect point.
+     *
+     * @param connectPoint string switch device Id
+     * @return whether ingress has been added, only add if ingressPoint is null
+     */
+    public boolean addIngressPoint(String connectPoint) {
+
+        if (this.ingressPoint != null) {
+            // TODO: log a warning.
+            return false;
+        }
+        ConnectPoint cp = ConnectPoint.deviceConnectPoint(checkNotNull(connectPoint));
+        return this.addIngressPoint(cp);
+    }
+
+    /**
+     * Get the ingress McastConnectPoint.
+     *
+     * @return the ingress McastConnectPoint
+     */
+    public McastConnectPoint getIngressPoint() {
+        return this.ingressPoint;
+    }
+
+    /**
+     * Add an egress McastConnectPoint.
+     *
+     * @param cp egress connect point
+     * @return return the McastConnectPoint
+     */
+    public McastConnectPoint addEgressPoint(ConnectPoint cp) {
+        McastConnectPoint mcp = this.findEgressConnectPoint(cp);
+        if (mcp == null) {
+            mcp = new McastConnectPoint(checkNotNull(cp));
+            egressPoints.add(mcp);
+            setDirty(true);
+        }
+        return mcp;
+    }
+
+    /**
+     * Add an egress connect point from a string.
+     *
+     * @param connectPoint string representing a connect point
+     * @return the MulticastConnectPoint
+     */
+    public McastConnectPoint addEgressPoint(String connectPoint) {
+        checkNotNull(connectPoint);
+        return this.addEgressPoint(ConnectPoint.deviceConnectPoint(connectPoint));
+    }
+
+    /**
+     * Add an egress McastConnectPoint.
+     *
+     * @param cp the egress connect point
+     * @param interest the source of interest for mcast traffic
+     */
+    public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest) {
+        checkNotNull(cp);
+        checkNotNull(interest);
+        McastConnectPoint mcp = this.addEgressPoint(cp);
+        if (mcp != null) {
+            mcp.interest.add(interest);
+            setDirty(true);
+        }
+        return mcp;
+    }
+
+    /**
+     * Add an egress McastConnectPoint.
+     *
+     * @param cpstr deviceId/port of the connect point
+     */
+    public McastConnectPoint addEgressPoint(String cpstr, McastConnectPoint.JoinSource interest) {
+        checkNotNull(cpstr);
+        checkNotNull(interest);
+        return this.addEgressPoint(ConnectPoint.deviceConnectPoint(cpstr), interest);
+    }
+
+    /**
+     * Get egress connect points for the route.
+     *
+     * @return Set of egress connect points
+     */
+    public Set<McastConnectPoint> getEgressPoints() {
+        return egressPoints;
+    }
+
+    /**
+     * Get egress McastConnectPoints points as ConnectPoints for intent system.
+     *
+     * @return Set of egress ConnectPoints
+     */
+    public Set<ConnectPoint> getEgressConnectPoints() {
+        Set<ConnectPoint> cps = new ListOrderedSet();
+
+        for (McastConnectPoint mcp : egressPoints) {
+            cps.add(mcp.getConnectPoint());
+        }
+        return cps;
+    }
+
+    /**
+     * Find the Multicast Connect Point that contains the ConnectPoint.
+     *
+     * @param cp the regular ConnectPoint to match
+     * @return the McastConnectPoint that contains cp or null if not found.
+     */
+    public McastConnectPoint findEgressConnectPoint(ConnectPoint cp) {
+        for (McastConnectPoint mcp : this.egressPoints) {
+            if (mcp.getConnectPoint().equals(cp)) {
+                return mcp;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Remove specified interest from the given ConnectPoint.
+     *
+     * @param mcp connect point.
+     * @param interest the protocol interested in this multicast stream
+     * @return true if removed, false otherwise
+     */
+    public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest) {
+        checkNotNull(mcp);
+        if (mcp.interest.contains(interest)) {
+            mcp.interest.remove(interest);
+            setDirty(true);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get the number of times the packet has been punted.
+     *
+     * @return the punt count
+     */
+    @Override
+    public int getPuntCount() {
+        return puntCount;
+    }
+
+    /**
+     * Increment the punt count.
+     *
+     * TODO: we need to handle wrapping.
+     */
+    @Override
+    public void incrementPuntCount() {
+        puntCount++;
+    }
+
+    /**
+     * Have the McastIntentManager create and set the intent, then save the intent key.
+     *
+     * If we already have an intent, we will first withdraw the existing intent and
+     * replace it with a new one.  This will support the case where the ingress connectPoint
+     * or group of egress connectPoints change.
+     */
+    @Override
+    public void setIntent() {
+        if (this.intentKey != null) {
+            this.withdrawIntent();
+        }
+        McastIntentManager im = McastIntentManager.getInstance();
+        SinglePointToMultiPointIntent intent = im.setIntent(this);
+        this.intentKey = intent.key();
+    }
+
+    /**
+     * Set the Intent key.
+     *
+     * @param intent the multicast intent
+     */
+    @Override
+    public void setIntent(SinglePointToMultiPointIntent intent) {
+        intentKey = intent.key();
+    }
+
+    /**
+     * Get the intent key represented by this route.
+     *
+     * @return intentKey
+     */
+    @Override
+    public Key getIntentKey() {
+        return this.intentKey;
+    }
+
+
+    /**
+     * Withdraw the intent and set the key to null.
+     */
+    @Override
+    public void withdrawIntent() {
+        if (intentKey == null) {
+            // nothing to withdraw
+            return;
+        }
+        McastIntentManager im = McastIntentManager.getInstance();
+        im.withdrawIntent(this);
+        this.intentKey = null;
+    }
+
+    /**
+     * Pretty Print this Multicast Route.  Works for McastRouteSource and McastRouteGroup.
+     *
+     * @return pretty string of the multicast route
+     */
+    @Override
+    public String toString() {
+        String out = String.format("(%s, %s)\n\t",
+                saddr.toString(), gaddr.toString());
+
+        out += "intent: ";
+        out += (intentKey == null) ? "not installed" : this.intentKey.toString();
+        out += "\n\tingress: ";
+        out += (ingressPoint == null) ? "NULL" : ingressPoint.toString();
+        out += "\n\tegress: {\n";
+        if (egressPoints != null && !egressPoints.isEmpty()) {
+            for (McastConnectPoint eg : egressPoints) {
+                out += "\t\t" + eg.getConnectPoint().toString() + "\n";
+            }
+        }
+        out += ("\t}\n");
+        out += ("\tpunted: " + this.getPuntCount() + "\n");
+        return out;
+    }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java
new file mode 100644 (file)
index 0000000..4a58e1b
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.HashMap;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * The McastRouteGroup extends the McastRouteBase class and serves two purposes:
+ * first it represents a (*, G) multicast route entry.  Second it serves
+ * as a container for all (S, G) multicast route entries that belong
+ * to the same group address.
+ */
+public class McastRouteGroup extends McastRouteBase {
+    private HashMap<IpPrefix, McastRouteSource> sources;
+
+    /**
+     * Class constructor.
+     *
+     * @param gaddr - String representation of group address.
+     */
+    public McastRouteGroup(String gaddr) {
+        super(checkNotNull(gaddr));
+        this.init();
+    }
+
+    /**
+     * Create a multicast group.
+     *
+     * @param gpfx - Group address
+     */
+    public McastRouteGroup(IpPrefix gpfx) {
+        super(checkNotNull(gpfx));
+        this.init();
+    }
+
+    /**
+     * Common initialization used by constructors.
+     */
+    private void init() {
+        this.sources = new HashMap();
+        super.isGroup = true;
+    }
+
+    /**
+     * Find a specific multicast source address for this group.
+     *
+     * @param saddr the source address
+     * @return the multicast source route or null if it does not exist
+     */
+    public McastRouteSource findSource(IpPrefix saddr) {
+        return this.sources.get(checkNotNull(saddr));
+    }
+
+    /**
+     * Return the entire set of multicast sources for this group.
+     *
+     * @return the set of multicast sources
+     */
+    public HashMap<IpPrefix, McastRouteSource> getSources() {
+        return this.sources;
+    }
+
+    /**
+     * Add a new McastRouteSource to this group.
+     *
+     * @param src the multicast source
+     */
+    public void addSource(McastRouteSource src) {
+        checkNotNull(src);
+        this.sources.put(src.getSaddr(), src);
+    }
+
+    /**
+     * Remove the source with this specific IpPrefix from this group entry.
+     *
+     * @param spfx IP Prefix of the source to be removed
+     * @return the source route that was just removed
+     */
+    public McastRouteSource removeSource(IpPrefix spfx) {
+        McastRouteSource src = this.sources.remove(spfx);
+        src.withdrawIntent();
+        return src;
+    }
+
+    /**
+     * Remove all sources from this.
+     */
+    public void removeSources() {
+        for (McastRouteSource src : this.sources.values()) {
+            src.withdrawIntent();
+            this.sources.remove(src.getSaddr());
+        }
+    }
+
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java
new file mode 100644 (file)
index 0000000..68edc2e
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * This class represents and specific multicast senders source address.  Objects from
+ * this class will belong to the sources collection of the multicast group.
+ */
+public class McastRouteSource extends McastRouteBase {
+
+    // A reference to our parent group
+    private McastRouteGroup group;
+
+    /**
+     * Create a multicast source with IpPrefixes.
+     *
+     * @param source the source address
+     * @param group the group address
+     */
+    public McastRouteSource(IpPrefix source, IpPrefix group) {
+        super(checkNotNull(source), checkNotNull(group));
+    }
+
+    /**
+     * Set our parent multicast group.
+     *
+     * @param group the group this source belongs to
+     */
+    public void setGroup(McastRouteGroup group) {
+        this.group = group;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java
new file mode 100644 (file)
index 0000000..5a07bec
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.impl;
+
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpPrefix;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * The Mcast Route Table holds all multicast state for the controller.
+ *
+ * State for IPv4 and IPv6 are maintained.  The tables are sets of McastRouteGroup
+ * structures that represent (*, G) state with a series of egress ConnectPoints.
+ * Each (*, G) may also have a set of (S, G) that may have there own set of
+ * ingress and egress ConnectPoints.
+ *
+ * TODO: perhaps should probably create two separate singleton for IPv4 and IPv6 respectively.
+ */
+@Service(value = org.onosproject.mfwd.impl.McastRouteTable.class)
+public final class McastRouteTable {
+
+    /*
+     * Create a map of the McastGroups indexed by the multicast group prefix.
+     * We may choose to change the map data structure in to some form a radix trie
+     * depending on the type of real world usage we see.
+     */
+    private final Map<IpPrefix, McastRouteGroup> mrib4;
+    private final Map<IpPrefix, McastRouteGroup> mrib6;
+    private static McastRouteTable instance = null;
+
+    private Boolean ipv6Enabled = false;
+
+    /**
+     * Create the two v4 & v6 tables.
+     */
+    private McastRouteTable() {
+        mrib4 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
+        if (ipv6Enabled) {
+            mrib6 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>();
+        } else {
+            mrib6 = null;
+        }
+    }
+
+    /**
+     * Get the single instance of this multicast group address.
+     *
+     * @return the multicast route table
+     */
+    public static McastRouteTable getInstance() {
+        if (instance == null) {
+            instance = new McastRouteTable();
+        }
+        return instance;
+    }
+
+    /**
+     * Get the IPv4 MRIB.
+     *
+     * @return the IPv4 MRIB
+     */
+    public Map<IpPrefix, McastRouteGroup> getMrib4() {
+        return mrib4;
+    }
+
+    /**
+     * Get the IPv6 MRIB.
+     *
+     * @return Return the set of prefix keyed McastGroups
+     */
+    public Map<IpPrefix, McastRouteGroup> getMrib6() {
+        return mrib6;
+    }
+
+    /**
+     * Save the McastRouteGroup in the address family appropriate mrib.
+     *
+     * @param group The McastRouteGroup to save
+     */
+    private void storeGroup(McastRouteGroup group) {
+        if (group.isIp4()) {
+            mrib4.put(group.getGaddr(), group);
+        } else if (group.isIp6() && ipv6Enabled) {
+            mrib6.put(group.getGaddr(), group);
+        }
+    }
+
+    /**
+     * Remove the group.
+     *
+     * @param group the group to be removed
+     */
+    private void removeGroup(McastRouteGroup group) {
+        IpPrefix gpfx = group.getGaddr();
+        if (gpfx.isIp4()) {
+            mrib4.remove(gpfx);
+        } else if (gpfx.isIp6() && ipv6Enabled) {
+            mrib6.remove(gpfx);
+        }
+    }
+
+    /**
+     * Add a multicast route to the MRIB.  This function will.
+     *
+     * @param saddr source address * or x.x.x.x or x.x.x.x/y
+     * @param gaddr group address x.x.x.x or x.x.x.x/y
+     * @return the multicast route
+     */
+    public McastRouteBase addRoute(String saddr, String gaddr) {
+        IpPrefix gpfx = IpPrefix.valueOf(gaddr);
+        IpPrefix spfx = IpPrefix.valueOf(0, 0);
+        if (saddr != null && !saddr.equals("*")) {
+            spfx = IpPrefix.valueOf(saddr);
+        }
+        return addRoute(spfx, gpfx);
+    }
+
+    /**
+     * Add a multicast route to the MRIB.  This function will store either
+     * (S, G) or (*, G) in the mrib if an entry does not already exist. If
+     * an entry does exist it is returned to the caller.
+     *
+     * Every (S, G) is stored as part of it's parent group entry which also represents
+     * (*, G) routes.  In the case of a (S, G) we will also create the (*, G) entry if needed
+     * then save the (S, G) to the (*, G).
+     *
+     * @param spfx the source prefix
+     * @param gpfx the group prefix
+     * @return the resulting McastRouteSource or McastRouteGroup accordingly.
+     */
+    public McastRouteBase addRoute(IpPrefix spfx, IpPrefix gpfx) {
+
+        /**
+         * If a group route (*, g) does not exist we will need to make so we
+         * can start attaching our sources to the group entry.
+         */
+        McastRouteGroup group = findMcastGroup(gpfx);
+        if (group == null) {
+            group = new McastRouteGroup(gpfx);
+
+            // Save it for later
+            if (gpfx.isIp4()) {
+                this.mrib4.put(gpfx, group);
+            } else if (gpfx.isIp6() && ipv6Enabled) {
+                this.mrib6.put(gpfx, group);
+            }
+        }
+
+        /**
+         * If the source prefix length is 0 then we have our (*, g) entry, we can
+         * just return now.
+         */
+        if (spfx.prefixLength() == 0) {
+            return group;
+        }
+
+        // See if the source already exists.  If so just return it.
+        McastRouteSource source = group.findSource(spfx);
+        if (source != null) {
+            return source;
+        }
+
+        /**
+         * We have the group but no source.  We need to create the source then add it
+         * to the group.
+         */
+        source = new McastRouteSource(spfx, gpfx);
+
+        // Have the source save it's parent
+        source.setGroup(group);
+
+        // Save this source as part of this group
+        group.addSource(source);
+
+        return source;
+    }
+
+    /**
+     * Delete a multicast route from the MRIB.
+     *
+     * @param saddr source address * or x.x.x.x or x.x.x.x/y
+     * @param gaddr group address x.x.x.x or x.x.x.x/y
+     */
+    public void removeRoute(String saddr, String gaddr) {
+        IpPrefix gpfx = IpPrefix.valueOf(gaddr);
+        IpPrefix spfx = IpPrefix.valueOf(0, 0);
+        if (saddr != null && !saddr.equals("*")) {
+            spfx = IpPrefix.valueOf(saddr);
+        }
+        removeRoute(spfx, gpfx);
+    }
+
+    /**
+     * Remove a multicast route.
+     *
+     * @param spfx the source prefix
+     * @param gpfx the group prefix
+     */
+    public void removeRoute(IpPrefix spfx, IpPrefix gpfx) {
+
+        /**
+         * If a group route (*, g) does not exist we will need to make so we
+         * can start attaching our sources to the group entry.
+         */
+        McastRouteGroup group = findMcastGroup(gpfx);
+        if (group == null) {
+            // The group does not exist, we can't remove it.
+            return;
+        }
+
+        /**
+         * If the source prefix length is 0 then we have a (*, g) entry, which
+         * means we will remove this group and all of it's sources. We will
+         * also withdraw it's intent if need be.
+         */
+        if (spfx.prefixLength() > 0) {
+            group.removeSource(spfx);
+
+            /*
+             * Now a little house keeping. If this group has no more sources
+             * nor egress connectPoints git rid of it.
+             */
+            if (group.getSources().size() == 0 &&
+                    group.getEgressPoints().size() == 0) {
+                removeGroup(group);
+            }
+
+        } else {
+            // Group remove has been explicitly requested.
+            group.removeSources();
+            group.withdrawIntent();
+            removeGroup(group);
+        }
+    }
+
+    /**
+     * Find the specific multicast group entry.
+     *
+     * @param group the group address
+     * @return McastRouteGroup the multicast (*, G) group route
+     */
+    public McastRouteGroup findMcastGroup(IpPrefix group) {
+        McastRouteGroup g = null;
+        if (group.isIp4()) {
+            g = mrib4.get(group);
+        } else if (group.isIp6() && ipv6Enabled) {
+            g = mrib6.get(group);
+        }
+        return g;
+    }
+
+    /**
+     * Find the multicast (S, G) entry if it exists.
+     *
+     * @param saddr the source address
+     * @param gaddr the group address
+     * @return The multicast source route entry if it exists, null if it does not.
+     */
+    public McastRouteSource findMcastSource(IpPrefix saddr, IpPrefix gaddr) {
+        McastRouteGroup grp = findMcastGroup(checkNotNull(gaddr));
+        if (grp == null) {
+            return null;
+        }
+        return grp.findSource(saddr);
+    }
+
+    /**
+     * This will first look up a Group entry. If no group entry was found null will
+     * be returned. If the group entry has been found we will then look up the (s, g) entry.
+     * If the (s, g) entry has been found, that will be returned.  If no (s, g) was found
+     * the (*, g) group entry will be returned.
+     *
+     * @param saddr the source address
+     * @param gaddr the group address
+     * @return return the best matching McastRouteSource or McastRouteGroup
+     */
+    public McastRoute findBestMatch(IpPrefix saddr, IpPrefix gaddr) {
+        McastRouteGroup grp = this.findMcastGroup(checkNotNull(gaddr));
+        if (grp == null) {
+            return null;
+        }
+
+        // Found a group now look for a source
+        McastRouteSource src = grp.findSource(checkNotNull(saddr));
+        if (src == null) {
+            return grp;
+        }
+
+        return src;
+    }
+
+    /**
+     * Print out the multicast route table in it's entirety.
+     *
+     * TODO: Eventually we will have to implement paging and how to handle large tables.
+     * @return String
+     */
+    public String printMcastRouteTable() {
+        String out = this.toString() + "\n";
+
+        for (McastRouteGroup grp : mrib4.values()) {
+            out += grp.toString() + "\n";
+            for (McastRouteSource src : grp.getSources().values()) {
+                out += src.toString() + "\n";
+            }
+        }
+        return out;
+    }
+
+    /**
+     * Print out a summary of groups in the MRIB.
+     *
+     * @return String
+     */
+    public String toString() {
+        String out = "Mcast Route Table: ";
+        out += mrib4.size() + " IPv4 Multicast Groups\n";
+        if (ipv6Enabled) {
+            out += mrib6.size() + " IPv6 Multicast Groups\n";
+        }
+        return out;
+    }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java
new file mode 100644 (file)
index 0000000..eaef5fc
--- /dev/null
@@ -0,0 +1,4 @@
+/**
+ * Sample Multicast forwarding framework using intents.
+ */
+package org.onosproject.mfwd.impl;
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java
new file mode 100644 (file)
index 0000000..608e044
--- /dev/null
@@ -0,0 +1,149 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.onosproject.mfwd.rest;\r
+\r
+import java.io.IOException;\r
+import com.fasterxml.jackson.databind.ObjectMapper;\r
+import com.fasterxml.jackson.databind.node.ObjectNode;\r
+\r
+import javax.ws.rs.DefaultValue;\r
+import javax.ws.rs.QueryParam;\r
+import javax.ws.rs.Consumes;\r
+import javax.ws.rs.GET;\r
+import javax.ws.rs.POST;\r
+import javax.ws.rs.DELETE;\r
+import javax.ws.rs.Path;\r
+import javax.ws.rs.Produces;\r
+import javax.ws.rs.core.MediaType;\r
+import javax.ws.rs.core.Response;\r
+\r
+import org.onosproject.mfwd.impl.McastConnectPoint;\r
+import org.onosproject.mfwd.impl.McastRouteTable;\r
+import org.onosproject.mfwd.impl.McastRouteBase;\r
+import org.onosproject.mfwd.impl.MRibCodec;\r
+import org.onosproject.rest.AbstractWebResource;\r
+\r
+import org.slf4j.Logger;\r
+import static org.slf4j.LoggerFactory.getLogger;\r
+\r
+/**\r
+ * Rest API for Multicast Forwarding.\r
+ */\r
+@Path("mcast")\r
+public class McastResource extends AbstractWebResource  {\r
+\r
+    private final Logger log = getLogger(getClass());\r
+    private static final String SOURCE_ADDRESS = "sourceAddress";\r
+    private static final String GROUP_ADDRESS = "groupAddress";\r
+    private static final String INGRESS_POINT = "ingressPoint";\r
+    private static final String EGRESS_POINT = "egressPoint";\r
+    private static final String MCAST_GROUP = "mcastGroup";\r
+\r
+    /**\r
+     * Retrieve the multicast route table.\r
+     *\r
+     * @return the multicast route table.\r
+     * @throws IOException if an error occurs\r
+     */\r
+    @Path("show")\r
+    @GET\r
+    @Produces(MediaType.APPLICATION_JSON)\r
+    public Response showAll() throws IOException {\r
+        McastRouteTable mrt = McastRouteTable.getInstance();\r
+        ObjectNode pushContent = new MRibCodec().encode(mrt , this);\r
+        return ok(pushContent.toString()).build();\r
+    }\r
+\r
+    /**\r
+     * Static join a multicast flow.\r
+     *\r
+     * @param sAddr source address to join\r
+     * @param gAddr group address to join\r
+     * @param ports ingress and egress ConnectPoints to join\r
+     * @return the Result of the join\r
+     * @throws IOException if something failed with the join command\r
+     */\r
+    @Path("/join")\r
+    @POST\r
+    @Consumes(MediaType.APPLICATION_JSON)\r
+    @Produces(MediaType.TEXT_PLAIN)\r
+    public Response join(@QueryParam("src") String sAddr,\r
+                    @QueryParam("grp") String gAddr,\r
+                    @DefaultValue("") @QueryParam("ports") String ports)\r
+                    throws IOException {\r
+\r
+        ObjectMapper mapper = new ObjectMapper();\r
+        log.debug("Source IP Address: " + sAddr);\r
+        log.debug("Destination IP Address: " + gAddr);\r
+        log.debug("Ingress and Egress ports: " + ports);\r
+\r
+        String output = "Insertion Faild";\r
+        if (sAddr != null && gAddr != null && ports != null) {\r
+\r
+            String[] portArr = ports.split(",");\r
+            log.debug("Port Array Length: " + portArr.length);\r
+            McastRouteTable mrt = McastRouteTable.getInstance();\r
+            McastRouteBase mr = mrt.addRoute(sAddr, gAddr);\r
+\r
+            // Port format "of:0000000000000023/4"\r
+            log.debug("checking inside outer if: " + portArr.length);\r
+\r
+            if (mr != null && portArr != null && portArr.length > 0) {\r
+\r
+                String inCP = portArr[0];\r
+                log.debug("Ingress port provided: " + inCP);\r
+                mr.addIngressPoint(inCP);\r
+\r
+                for (int i = 1; i < portArr.length; i++) {\r
+                    String egCP = portArr[i];\r
+                    log.debug("Egress port provided: " + egCP);\r
+                    mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);\r
+                }\r
+                mrt.printMcastRouteTable();\r
+                output = "Successfully Inserted";\r
+            }\r
+        } else {\r
+            output = "Please Insert the rest uri correctly";\r
+        }\r
+        return Response.ok(output).build();\r
+    }\r
+\r
+    /**\r
+     * Delete multicast state.\r
+     *\r
+     * @param src address to be deleted\r
+     * @param grp address to be deleted\r
+     * @return status of delete if successful\r
+     */\r
+    @Path("/delete")\r
+    @DELETE\r
+    @Consumes(MediaType.TEXT_PLAIN)\r
+    @Produces(MediaType.TEXT_PLAIN)\r
+    public Response removeMcastFlow(@QueryParam("src") String src,\r
+                    @QueryParam("grp") String grp) {\r
+\r
+        String resp = "Failed to delete";\r
+        log.info("Source IP Address to delete: " + src);\r
+        log.info("Destination IP Address to delete: " + grp);\r
+        McastRouteTable mrt = McastRouteTable.getInstance();\r
+        if (src != null && grp != null) {\r
+            mrt.removeRoute(src, grp);\r
+            resp = "Deleted flow for src " + src + " and grp " + grp;\r
+        }\r
+\r
+        return Response.ok(resp).build();\r
+    }\r
+}\r
diff --git a/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644 (file)
index 0000000..966cb4f
--- /dev/null
@@ -0,0 +1,30 @@
+<!--\r
+  ~ Copyright 2014 Open Networking Laboratory\r
+  ~\r
+  ~ Licensed under the Apache License, Version 2.0 (the "License");\r
+  ~ you may not use this file except in compliance with the License.\r
+  ~ You may obtain a copy of the License at\r
+  ~\r
+  ~     http://www.apache.org/licenses/LICENSE-2.0\r
+  ~\r
+  ~ Unless required by applicable law or agreed to in writing, software\r
+  ~ distributed under the License is distributed on an "AS IS" BASIS,\r
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+  ~ See the License for the specific language governing permissions and\r
+  ~ limitations under the License.\r
+  -->\r
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">\r
+\r
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">\r
+        <command>\r
+            <action class="org.onosproject.mfwd.cli.McastJoinCommand"/>\r
+        </command>\r
+        <command>\r
+            <action class="org.onosproject.mfwd.cli.McastDeleteCommand"/>\r
+        </command>\r
+        <command>\r
+            <action class="org.onosproject.mfwd.cli.McastShowCommand"/>\r
+        </command>\r
+    </command-bundle>\r
+\r
+</blueprint>\r
diff --git a/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..c4c4f45
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+~ Copyright 2014 Open Networking Laboratory
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="ONOS" version="2.5">
+    <display-name>ONOS APP MFWD</display-name>
+
+    <servlet>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
+            <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
+        </init-param>
+        <init-param>
+            <param-name>com.sun.jersey.config.property.classnames</param-name>
+            <param-value>
+                org.onosproject.mfwd.rest.McastResource
+            </param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
index 85b5de2..8466b95 100644 (file)
@@ -45,7 +45,8 @@ import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.OpticalCircuitIntent;
 import org.onosproject.net.intent.OpticalConnectivityIntent;
 import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.net.resource.device.DeviceResourceService;
+import org.onosproject.net.newresource.ResourceService;
+import org.onosproject.net.resource.device.IntentSetMultimap;
 import org.onosproject.net.resource.link.LinkResourceAllocations;
 import org.onosproject.net.resource.link.LinkResourceService;
 import org.onosproject.net.topology.LinkWeight;
@@ -97,11 +98,14 @@ public class OpticalPathProvisioner {
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceResourceService deviceResourceService;
+    protected IntentSetMultimap intentSetMultimap;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected LinkResourceService linkResourceService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ResourceService resourceService;
+
     private ApplicationId appId;
 
     private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
@@ -292,7 +296,6 @@ public class OpticalPathProvisioner {
                             .bidirectional(true)
                             .build();
                     intents.add(circuitIntent);
-                    continue;
                 } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
                     // Create lightpath
                     // FIXME: hardcoded ODU signal type
@@ -304,7 +307,6 @@ public class OpticalPathProvisioner {
                             .bidirectional(true)
                             .build();
                     intents.add(opticalIntent);
-                    continue;
                 } else {
                     log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
                     return Collections.emptyList();
@@ -377,13 +379,13 @@ public class OpticalPathProvisioner {
         private void releaseResources(Intent intent) {
             LinkResourceAllocations lra = linkResourceService.getAllocations(intent.id());
             if (intent instanceof OpticalConnectivityIntent) {
-                deviceResourceService.releasePorts(intent.id());
+                resourceService.release(intent.id());
                 if (lra != null) {
                     linkResourceService.releaseResources(lra);
                 }
             } else if (intent instanceof OpticalCircuitIntent) {
-                deviceResourceService.releasePorts(intent.id());
-                deviceResourceService.releaseMapping(intent.id());
+                resourceService.release(intent.id());
+                intentSetMultimap.releaseMapping(intent.id());
                 if (lra != null) {
                     linkResourceService.releaseResources(lra);
                 }
diff --git a/framework/src/onos/apps/pim/pom.xml b/framework/src/onos/apps/pim/pom.xml
new file mode 100644 (file)
index 0000000..83a366b
--- /dev/null
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-apps</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-pim</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>Protocol Independent Multicast Emulation</description>
+
+    <properties>
+        <onos.app.name>org.onosproject.pim</onos.app.name>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- This is needed by ComponentContext, used for tunable configuration -->
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <version>1.9.8</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>
+                            ${project.groupId}.${project.artifactId}
+                        </Bundle-SymbolicName>
+                        <Import-Package>
+                            org.slf4j,
+                            org.osgi.framework,
+                            org.apache.commons.lang.math.*,
+                            com.google.common.*,
+                            org.apache.karaf.shell.commands,
+                            org.apache.karaf.shell.console,
+                            org.onlab.packet.*,
+                            org.onlab.rest.*,
+                            org.onosproject.*,
+                            org.onosproject.mfwd.impl.*;
+                            org.onlab.util.*,
+                            org.jboss.netty.util.*
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.5.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java
new file mode 100644 (file)
index 0000000..0ef7e38
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.pim.impl.PIMNeighbors;
+import org.onosproject.pim.impl.PIMNeighborsCodec;
+
+import java.util.HashMap;
+
+@Command(scope = "onos", name = "pim-neighbors", description = "Displays the pim neighbors")
+public class PIMShowCommand extends AbstractShellCommand {
+
+    // prints either the json or cli version of the hash map connect point
+    // neighbors from the PIMNeighbors class.
+    @Override
+    protected  void execute() {
+        // grab connect point neighbors hash map to send in to json encoder.
+        HashMap<ConnectPoint, PIMNeighbors> pimNbrs = PIMNeighbors.getConnectPointNeighbors();
+        if (outputJson()) {
+            print("%s", json(pimNbrs));
+        } else {
+            print(PIMNeighbors.printPimNeighbors());
+        }
+    }
+
+    private JsonNode json(HashMap<ConnectPoint, PIMNeighbors> pimNbrs) {
+        return new PIMNeighborsCodec().encode(pimNbrs, this);
+    }
+
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java
new file mode 100644 (file)
index 0000000..954dacb
--- /dev/null
@@ -0,0 +1,4 @@
+/**
+ * PIM Multicast forwarding framework using intents.
+ */
+package org.onosproject.pim.cli;
\ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java
new file mode 100644 (file)
index 0000000..bd5e148
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.PIM;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+/**
+ * Protocol Independent Multicast Emulation.
+ */
+@Component(immediate = true)
+public class PIMComponent {
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    private PIMPacketProcessor processor = new PIMPacketProcessor();
+    private static ApplicationId appId;
+
+    @Activate
+    public void activate() {
+        appId = coreService.registerApplication("org.onosproject.pim");
+
+        packetService.addProcessor(processor, PacketProcessor.director(1));
+
+        // Build a traffic selector for all multicast traffic
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.matchEthType(Ethernet.TYPE_IPV4);
+        selector.matchIPProtocol(IPv4.PROTOCOL_PIM);
+        packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        packetService.removeProcessor(processor);
+        processor = null;
+        log.info("Stopped");
+    }
+
+    /**
+     * Packet processor responsible for handling IGMP packets.
+     */
+    private class PIMPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            // Stop processing if the packet has been handled, since we
+            // can't do any more to it.
+            if (context.isHandled()) {
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            if (pkt == null) {
+                return;
+            }
+
+            Ethernet ethPkt = pkt.parsed();
+            if (ethPkt == null) {
+                return;
+            }
+
+            /*
+             * IPv6 MLD packets are handled by ICMP6. We'll only deal
+             * with IPv4.
+             */
+            if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
+                return;
+            }
+
+            IPv4 ip = (IPv4) ethPkt.getPayload();
+            IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress());
+            IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress());
+            log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() +
+                    "\tingress port: " + context.inPacket().receivedFrom().toString());
+
+            if (ip.getProtocol() != IPv4.PROTOCOL_PIM) {
+                log.debug("PIM Picked up a non PIM packet: IP protocol: " + ip.getProtocol());
+                return;
+            }
+
+            // TODO: check incoming to be PIM.PIM_ADDRESS or "Our" address.
+            IpPrefix spfx = IpPrefix.valueOf(saddr, 32);
+            IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32);
+
+            PIM pim = (PIM) ip.getPayload();
+            switch (pim.getPimMsgType()) {
+
+                case PIM.TYPE_HELLO:
+                    PIMNeighbors.processHello(ethPkt, context.inPacket().receivedFrom());
+                    break;
+
+                case PIM.TYPE_JOIN_PRUNE_REQUEST:
+                    // Create the function
+                    break;
+
+                case PIM.TYPE_ASSERT:
+                case PIM.TYPE_BOOTSTRAP:
+                case PIM.TYPE_CANDIDATE_RP_ADV:
+                case PIM.TYPE_GRAFT:
+                case PIM.TYPE_GRAFT_ACK:
+                case PIM.TYPE_REGISTER:
+                case PIM.TYPE_REGISTER_STOP:
+                    log.debug("Unsupported PIM message type: " + pim.getPimMsgType());
+                    break;
+
+                default:
+                    log.debug("Unkown PIM message type: " + pim.getPimMsgType());
+                    break;
+            }
+        }
+    }
+}
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java
new file mode 100644 (file)
index 0000000..1a96138
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in reliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.pim.PIMHello;
+import org.onlab.packet.pim.PIMHelloOption;
+import org.onosproject.net.ConnectPoint;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * PIMNeighbor represents all the PIM routers that have sent us
+ * hello messages, or that possibly have been statically configured.
+ */
+public class PIMNeighbor {
+    private final Logger log = getLogger(getClass());
+
+    // The primary address of this PIM neighbor
+    private IpAddress primaryAddr;
+
+    // The MacAddress of this neighbor
+    private MacAddress macAddress;
+
+    // The ConnectPoint this PIM neighbor is connected to.
+    private ConnectPoint connectPoint;
+
+    // Is this neighbor us?
+    private boolean isThisUs = false;
+
+    // The option values this neighbor has sent us.
+    private int priority = 0;
+    private int genId = 0;
+    private short holdtime = 0;
+
+    // Is this pim neighbor the DR?
+    private boolean isDr = false;
+
+    // Timeout for this neighbor
+    private volatile Timeout timeout;
+
+    private boolean reelect = false;
+
+    // A back pointer the neighbors list this neighbor belongs to.
+    private PIMNeighbors neighbors;
+
+    /**
+     * Construct this neighbor from the address and connect point.
+     *
+     * @param ipaddr IP Address of neighbor
+     * @param macaddr MAC Address of the neighbor
+     * @param cp The ConnectPoint of this neighbor
+     */
+    public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, ConnectPoint cp) {
+        this.macAddress = macaddr;
+        this.primaryAddr = ipaddr;
+        this.connectPoint = cp;
+        this.resetTimeout();
+    }
+
+    /**
+     * Get the primary address of this neighbor.
+     *
+     * @return the primary IP address.
+     */
+    public IpAddress getPrimaryAddr() {
+        return primaryAddr;
+    }
+
+    /**
+     * Set the primary address of this neighbor.
+     *
+     * @param primaryAddr the address we'll use when sending hello messages
+     */
+    public void setPrimaryAddr(IpAddress primaryAddr) {
+        this.primaryAddr = primaryAddr;
+    }
+
+    /**
+     * Get the priority this neighbor has advertised to us.
+     *
+     * @return the priority
+     */
+    public int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Set the priority for this neighbor.
+     *
+     * @param priority This neighbors priority.
+     */
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    /**
+     * Get the generation ID.
+     *
+     * @return the generation ID.
+     */
+    public int getGenId() {
+        return genId;
+    }
+
+    /**
+     * Set the generation ID.
+     *
+     * @param genId the generation ID.
+     */
+    public void setGenId(int genId) {
+        this.genId = genId;
+    }
+
+    /**
+     * Get the holdtime for this neighbor.
+     *
+     * @return the holdtime
+     */
+    public short getHoldtime() {
+        return holdtime;
+    }
+
+    /**
+     * Set the holdtime for this neighbor.
+     *
+     * @param holdtime the holdtime.
+     */
+    public void setholdtime(short holdtime) {
+        this.holdtime = holdtime;
+    }
+
+    /**
+     * Is this neighbor the designated router on this connect point?
+     *
+     * @return true if so, false if not.
+     */
+    public boolean isDr() {
+        return isDr;
+    }
+
+    /**
+     * Set this router as the designated router on this connect point.
+     *
+     * @param isDr True is this neighbor is the DR false otherwise
+     */
+    public void setIsDr(boolean isDr) {
+        this.isDr = isDr;
+    }
+
+    /**
+     * The ConnectPoint this neighbor is connected to.
+     *
+     * @return the ConnectPoint
+     */
+    public ConnectPoint getConnectPoint() {
+        return connectPoint;
+    }
+
+    /**
+     * Set the ConnectPoint this router is connected to.
+     *
+     * @param connectPoint the ConnectPoint this router is connected to.
+     */
+    public void setConnectPoint(ConnectPoint connectPoint) {
+        this.connectPoint = connectPoint;
+    }
+
+    /**
+     * Set a back pointer to the neighbors list this neighbor is a member of.
+     *
+     * @param neighbors the neighbor list this neighbor belongs to
+     */
+    public void setNeighbors(PIMNeighbors neighbors) {
+        this.neighbors = neighbors;
+    }
+
+    /**
+     * We have received a fresh hello from a neighbor, now we need to process it.
+     * Depending on the values received in the the hello options may force a
+     * re-election process.
+     *
+     * We will also refresh the timeout for this neighbor.
+     *
+     * @param hello copy of the hello we'll be able to extract options from.
+     */
+    public void refresh(PIMHello hello) {
+        checkNotNull(hello);
+
+        for (PIMHelloOption opt : hello.getOptions().values()) {
+
+            int len = opt.getOptLength();
+            byte [] value = new byte[len];
+            ByteBuffer bb = ByteBuffer.wrap(value);
+
+            switch (opt.getOptType()) {
+                case PIMHelloOption.OPT_GENID:
+                    int newid = bb.getInt();
+                    if (this.genId != newid) {
+                        // TODO: we have a newly rebooted neighbor.  Send them our joins.
+                        this.genId = newid;
+                    }
+                    break;
+
+                case PIMHelloOption.OPT_PRIORITY:
+                    int newpri = bb.getInt();
+                    if (this.priority != newpri) {
+
+                        // The priorities have changed.  We may need to re-elect a new DR?
+                        if (this.isDr || this.neighbors.getDesignatedRouter().getPriority() < priority) {
+                            reelect = true;
+                        }
+                        this.priority = newpri;
+                    }
+                    break;
+
+                case PIMHelloOption.OPT_HOLDTIME:
+                    short holdtime = bb.getShort();
+                    if (this.holdtime != holdtime) {
+                        this.holdtime = holdtime;
+                        if (holdtime == 0) {
+                            // We have a neighbor going down.  We can remove all joins
+                            // we have learned from them.
+                            // TODO: What else do we need to do when a neighbor goes down?
+
+                            log.debug("PIM Neighbor has timed out: {}", this.primaryAddr.toString());
+                            return;
+                        }
+                    }
+                    break;
+
+                case PIMHelloOption.OPT_PRUNEDELAY:
+                case PIMHelloOption.OPT_ADDRLIST:
+                    // TODO: implement prune delay and addr list.  Fall through for now.
+
+                default:
+                    log.debug("PIM Hello option type: {} not yet supported or unknown.", opt.getOptType());
+                    break;
+            }
+        }
+
+        if (reelect) {
+            this.neighbors.electDR(this);
+        }
+
+        // Reset the next timeout timer
+        this.resetTimeout();
+    }
+
+    /* --------------------------------------- Timer functions -------------------------- */
+
+    /**
+     * Restart the timeout task for this neighbor.
+     */
+    private void resetTimeout() {
+
+        if (this.holdtime == 0) {
+
+            // Prepare to die.
+            log.debug("shutting down timer for nbr {}", this.primaryAddr.toString());
+            if (this.timeout != null) {
+                this.timeout.cancel();
+                this.timeout = null;
+            }
+            return;
+        }
+
+        // Cancel the existing timeout and start a fresh new one.
+        if (this.timeout != null) {
+            this.timeout.cancel();
+        }
+
+        this.timeout = PIMTimer.getTimer().newTimeout(new NeighborTimeoutTask(this), holdtime, TimeUnit.SECONDS);
+    }
+
+    /**
+     * The task to run when a neighbor timeout expires.
+     */
+    private final class NeighborTimeoutTask implements TimerTask {
+        PIMNeighbor nbr;
+
+        NeighborTimeoutTask(PIMNeighbor nbr) {
+            this.nbr = nbr;
+        }
+
+        @Override
+        public void run(Timeout timeout) throws Exception {
+
+            // TODO: log.debug;
+            PIMNeighbors neighbors = nbr.neighbors;
+            neighbors.removeNeighbor(nbr.getPrimaryAddr());
+        }
+    }
+
+    /**
+     * Stop the timeout timer.
+     *
+     * This happens when we remove the neighbor.
+     */
+    private final void stopTimeout() {
+        this.timeout.cancel();
+        this.timeout = null;
+    }
+
+    @Override
+    public String toString() {
+        String out = "";
+        if (this.isDr) {
+            out += "*NBR:";
+        } else {
+            out += "NBR:";
+        }
+        out += "\tIP: " + this.primaryAddr.toString();
+        out += "\tPr: " + String.valueOf(this.priority);
+        out += "\tHoldTime: " + String.valueOf(this.holdtime);
+        out += "\tGenID: " + String.valueOf(this.genId) + "\n";
+        return out;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java
new file mode 100644 (file)
index 0000000..cad9076
--- /dev/null
@@ -0,0 +1,395 @@
+
+package org.onosproject.pim.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.PIM;
+import org.onlab.packet.pim.PIMHello;
+import org.onosproject.net.ConnectPoint;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * PIMNeighbors is a collection of all neighbors we have received
+ * PIM hello messages from.  The main structure is a HashMap indexed
+ * by ConnectPoint with another HashMap indexed on the PIM neighbors
+ * IPAddress, it contains all PIM neighbors attached on that ConnectPoint.
+ */
+public final class PIMNeighbors {
+
+    private static Logger log = LoggerFactory.getLogger("PIMNeighbors");
+
+    /**
+     * This is the global container for all PIM neighbors indexed by ConnectPoints.
+     *
+     * NOTE: We'll have a problem if the same neighbor can show up on two interfaces
+     * but that should never happen.
+     */
+    private static HashMap<ConnectPoint, PIMNeighbors> connectPointNeighbors = new HashMap<>();
+
+    // The connect point these neighbors are connected to.
+    private ConnectPoint connectPoint;
+
+    // Pointer to the current designated router on this ConnectPoint.
+    private PIMNeighbor designatedRouter;
+
+    // The list of neighbors we have learned on this ConnectPoint.
+    private HashMap<IpAddress, PIMNeighbor> neighbors = new HashMap<>();
+
+    /*
+     * TODO: turn ourIpAddress, ourPriority and OurHoldTime into config options.
+     */
+    // The IP address we are using to source our PIM hello messages on this connect Point.
+    private IpAddress ourIpAddress;
+
+    // The priority we use on this ConnectPoint.
+    private int ourPriority = 1;
+
+    // The holdtime we are sending out.
+    private int ourHoldtime = 105;
+
+    // Then generation ID we are sending out. 0 means we need to generate a new random ID
+    private int ourGenid = 0;
+
+    // Hello Timer for sending hello messages per ConnectPoint with neighbors.
+    private volatile Timeout helloTimer;
+
+    // The period of which we will be sending out PIM hello messages.
+    private final int defaultPimHelloInterval = 30; // seconds
+
+    /**
+     * Create PIMNeighbors object per ConnectPoint.
+     *
+     * @param cp the ConnectPoint.
+     * @return PIMNeighbors structure
+     */
+    public static PIMNeighbors getConnectPointNeighbors(ConnectPoint cp) {
+        return connectPointNeighbors.get(cp);
+    }
+
+    /**
+     * Process incoming hello message, we will need the Macaddress and IP address of the sender.
+     *
+     * @param ethPkt the ethernet header
+     * @param receivedFrom the connect point we recieved this message from
+     */
+    public static void processHello(Ethernet ethPkt, ConnectPoint receivedFrom) {
+        checkNotNull(ethPkt);
+        checkNotNull(ethPkt);
+
+        MacAddress srcmac = ethPkt.getSourceMAC();
+        IPv4 ip = (IPv4) ethPkt.getPayload();
+        Ip4Address srcip = Ip4Address.valueOf(ip.getSourceAddress());
+
+        PIM pim = (PIM) ip.getPayload();
+        checkNotNull(pim);
+
+        PIMHello hello = (PIMHello) pim.getPayload();
+        checkNotNull(hello);
+
+        PIMNeighbor nbr = PIMNeighbors.findOrCreate(srcip, srcmac, receivedFrom);
+        if (nbr == null) {
+            log.error("Could not create a neighbor for: {1}", srcip.toString());
+            return;
+        }
+
+        nbr.setConnectPoint(receivedFrom);
+        nbr.refresh(hello);
+    }
+
+    /**
+     * Create a PIM Neighbor.
+     *
+     * @param cp The ConnectPoint this neighbor was found on
+     */
+    public PIMNeighbors(ConnectPoint cp) {
+        this.connectPoint = cp;
+
+        // TODO: use network config to assign address.
+        this.ourIpAddress = IpAddress.valueOf("10.2.2.2");
+        this.addIpAddress(this.ourIpAddress);
+    }
+
+    /**
+     * Create a PIM neighbor.
+     *
+     * @param cp the ConnectPoint this neighbor was found on
+     * @param ourIp the IP address of this neighbor
+     */
+    public PIMNeighbors(ConnectPoint cp, IpAddress ourIp) {
+        this.connectPoint = cp;
+        this.addIpAddress(ourIp);
+    }
+
+    /**
+     * Start the hello timer when we have been given an IP address.
+     *
+     * @param ourIp our IP address.
+     */
+    public void addIpAddress(IpAddress ourIp) {
+        this.startHelloTimer();
+
+        // Kick off the first pim hello packet
+        this.sendHelloPacket();
+    }
+
+    /**
+     * Getter for our IP address.
+     *
+     * @return our IP address.
+     */
+    public IpAddress getOurIpAddress() {
+        return this.ourIpAddress;
+    }
+
+    /**
+     * Get our priority.
+     *
+     * @return our priority.
+     */
+    public int getOurPriority() {
+        return this.ourPriority;
+    }
+
+    /**
+     * Get the neighbor list for this specific connectPoint.
+     *
+     * @return PIM neighbors on this ConnectPoint
+     */
+    public HashMap<IpAddress, PIMNeighbor> getOurNeighborsList() {
+        return this.neighbors;
+    }
+
+    /**
+     * Get the designated router on this connection.
+     *
+     * @return the PIMNeighbor representing the DR
+     */
+    public PIMNeighbor getDesignatedRouter() {
+        return designatedRouter;
+    }
+
+    /**
+     * Are we the DR on this CP?
+     *
+     * @return true if we are, false if not
+     */
+    public boolean weAreTheDr() {
+        return (designatedRouter != null &&
+                designatedRouter.getPrimaryAddr().equals(ourIpAddress));
+    }
+
+    /**
+     * Find the neighbor with the given IP address on this CP.
+     *
+     * @param ipaddr the IP address of the neighbor we are interested in
+     * @return the pim neighbor if it exists
+     */
+    public PIMNeighbor findNeighbor(IpAddress ipaddr) {
+        PIMNeighbor nbr = neighbors.get(ipaddr);
+        return nbr;
+    }
+
+    /**
+     * Add a new PIM neighbor to this list.
+     *
+     * @param nbr the neighbor to be added.
+     */
+    public void addNeighbor(PIMNeighbor nbr) {
+        if (neighbors.containsKey(nbr.getPrimaryAddr())) {
+
+            // TODO: Hmmm, how should this be handled?
+            log.debug("We are adding a neighbor that already exists: {}", nbr.toString());
+            neighbors.remove(nbr.getPrimaryAddr(), nbr);
+        }
+        nbr.setNeighbors(this);
+        neighbors.put(nbr.getPrimaryAddr(), nbr);
+    }
+
+    /**
+     * Remove the neighbor from our neighbor list.
+     *
+     * @param ipaddr the IP address of the neighbor to remove
+     */
+    public void removeNeighbor(IpAddress ipaddr) {
+
+        boolean reelect = (designatedRouter == null || designatedRouter.getPrimaryAddr().equals(ipaddr));
+        if (neighbors.containsKey(ipaddr)) {
+            neighbors.remove(ipaddr);
+        }
+        this.electDR();
+    }
+
+    /**
+     * Remove the given neighbor from the neighbor list.
+     *
+     * @param nbr the nbr to be removed.
+     */
+    public void removeNeighbor(PIMNeighbor nbr) {
+
+        boolean reelect = (designatedRouter == null || nbr.isDr());
+        neighbors.remove(nbr.getPrimaryAddr(), nbr);
+        this.electDR();
+    }
+
+    /**
+     * Elect a new DR on this ConnectPoint.
+     *
+     * @return the PIM Neighbor that wins
+     */
+    public PIMNeighbor electDR() {
+
+        for (PIMNeighbor nbr : this.neighbors.values()) {
+            if (this.designatedRouter == null) {
+                this.designatedRouter = nbr;
+                continue;
+            }
+
+            if (nbr.getPriority() > this.designatedRouter.getPriority()) {
+                this.designatedRouter = nbr;
+                continue;
+            }
+
+            // We could sort in ascending order
+            if (this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) {
+                this.designatedRouter = nbr;
+                continue;
+            }
+        }
+
+        return this.designatedRouter;
+    }
+
+    /**
+     * Elect a new DR given the new neighbor.
+     *
+     * @param nbr the new neighbor to use in DR election.
+     * @return the PIM Neighbor that wins DR election
+     */
+    public PIMNeighbor electDR(PIMNeighbor nbr) {
+
+        // Make sure I have
+        if (this.designatedRouter == null ||
+                this.designatedRouter.getPriority() < nbr.getPriority() ||
+                this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) {
+            this.designatedRouter = nbr;
+        }
+        return this.designatedRouter;
+    }
+
+    /**
+     * Find or create a pim neighbor with a given ip address and connect point.
+     *
+     * @param ipaddr of the pim neighbor
+     * @param mac The mac address of our sending neighbor
+     * @param cp the connect point the neighbor was learned from
+     * @return an existing or new PIM neighbor
+     */
+    public static PIMNeighbor findOrCreate(IpAddress ipaddr, MacAddress mac, ConnectPoint cp) {
+        PIMNeighbors neighbors = connectPointNeighbors.get(cp);
+        if (neighbors == null) {
+            neighbors = new PIMNeighbors(cp);
+            connectPointNeighbors.put(cp, neighbors);
+        }
+
+        PIMNeighbor nbr = neighbors.findNeighbor(ipaddr);
+        if (nbr == null) {
+            nbr = new PIMNeighbor(ipaddr, mac, cp);
+            neighbors.addNeighbor(nbr);
+            neighbors.electDR(nbr);
+        }
+        return nbr;
+    }
+
+    // Returns the connect point neighbors hash map
+    public static HashMap<ConnectPoint, PIMNeighbors> getConnectPointNeighbors() {
+        return connectPointNeighbors;
+    }
+
+    /* ---------------------------------- PIM Hello Timer ----------------------------------- */
+
+    /**
+     * Start a new hello timer for this ConnectPoint.
+     */
+    private void startHelloTimer() {
+        this.helloTimer = PIMTimer.getTimer().newTimeout(
+                new HelloTimer(this),
+                this.defaultPimHelloInterval,
+                TimeUnit.SECONDS);
+
+        log.trace("Started Hello Timer: " + this.ourIpAddress.toString());
+    }
+
+    /**
+     * This inner class handles transmitting a PIM hello message on this ConnectPoint.
+     */
+    private final class HelloTimer implements TimerTask {
+        PIMNeighbors neighbors;
+
+        HelloTimer(PIMNeighbors neighbors) {
+            this.neighbors = neighbors;
+        }
+
+        @Override
+        public void run(Timeout timeout) throws Exception {
+
+            // Send off a hello packet
+            sendHelloPacket();
+
+            // restart the hello timer
+            neighbors.startHelloTimer();
+        }
+    }
+
+    private void sendHelloPacket() {
+        PIMHello hello = new PIMHello();
+
+        // TODO: we will need to implement the network config service to assign ip addresses & options
+        /*
+        hello.createDefaultOptions();
+
+        Ethernet eth = hello.createPIMHello(this.ourIpAddress);
+        hello.sendPacket(this.connectPoint);
+        */
+    }
+
+    /**
+     * prints the connectPointNeighbors list with each neighbor list.
+     *
+     * @return string of neighbors.
+     */
+    public static String printPimNeighbors() {
+        String out = "PIM Neighbors Table: \n";
+
+        for (PIMNeighbors pn: connectPointNeighbors.values()) {
+
+            out += "CP:\n " + pn.toString();
+            for (PIMNeighbor nbr : pn.neighbors.values()) {
+                out += "\t" + nbr.toString();
+            }
+        }
+        return out;
+    }
+
+    @Override
+    public String toString() {
+        String out = "PIM Neighbors: ";
+        if (this.ourIpAddress != null) {
+            out += "IP: " + this.ourIpAddress.toString();
+        } else {
+            out += "IP: *Null*";
+        }
+        out += "\tPR: " + String.valueOf(this.ourPriority) + "\n";
+        return out;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java
new file mode 100644 (file)
index 0000000..ee62eb7
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.HashMap;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * PIM neighbors Codec.
+ */
+public class PIMNeighborsCodec extends JsonCodec<HashMap<ConnectPoint, PIMNeighbors>> {
+    // JSON field names
+    //Return Name
+    private static final String CPNBRLIST = "connect_point_list";
+
+    // PIM Neightbors Fields
+    private static final String IP = "ip";
+    private static final String PRIORITY = "priority";
+    private static final String NBRLIST = "neighbor_list";
+
+    // PIM neighbor Files
+    private static final String DR = "designated";
+    private static final String NBR_IP = "ip";
+    private static final String PR = "priority";
+    private static final String HOLDTIME = "hold_time";
+
+    /**
+     * Encode the PIM Neighbors.
+     *
+     * @param cpn ConnectPoint neighbors
+     * @param context encoding context
+     *
+     * @return Encoded neighbors used by CLI and REST
+     */
+    @Override
+    public ObjectNode encode(HashMap<ConnectPoint, PIMNeighbors> cpn, CodecContext context) {
+        checkNotNull(cpn, "Pim Neighbors cannot be null");
+
+        ObjectNode pimNbrJsonCodec = context.mapper().createObjectNode();
+        ArrayNode cpnList = context.mapper().createArrayNode();
+
+        for (PIMNeighbors pn: cpn.values()) {
+            // get the PimNeighbors Obj, contains Neighbors list
+            // create the json object for a single Entry in the Neighbors list
+            ObjectNode cp = context.mapper().createObjectNode();
+            cp.put(IP, pn.getOurIpAddress().toString());
+            cp.put(PRIORITY, String.valueOf(pn.getOurPriority()));
+
+            // create the array for the neighbors list
+            ArrayNode nbrsList = context.mapper().createArrayNode();
+            for (PIMNeighbor nbr : pn.getOurNeighborsList().values()) {
+                nbrsList.add(neighbor(nbr, context));
+            }
+            // adds pim neighbor to list
+            cp.set(NBRLIST, nbrsList);
+            // adds to arraynode which will represent the connect point neighbors hash map.
+            cpnList.add(cp);
+        }
+        pimNbrJsonCodec.set(CPNBRLIST, cpnList);
+        return pimNbrJsonCodec;
+    }
+
+    /**
+     * Encode a single PIM Neighbor.
+     *
+     * @param nbr the neighbor to be encoded
+     * @param context encoding context
+     * @return the encoded neighbor
+     */
+    private ObjectNode neighbor(PIMNeighbor nbr, CodecContext context) {
+        return context.mapper().createObjectNode()
+                .put(DR, Boolean.toString(nbr.isDr()))
+                .put(NBR_IP, nbr.getPrimaryAddr().toString())
+                .put(PR, String.valueOf(nbr.getPriority()))
+                .put(HOLDTIME, String.valueOf(nbr.getHoldtime()));
+    }
+}
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java
new file mode 100644 (file)
index 0000000..c131a53
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.pim.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+
+/**
+ * PIM Timer used for PIM Neighbors.
+ */
+public final class PIMTimer {
+
+    private static volatile HashedWheelTimer timer;
+
+    // Ban public construction
+    private PIMTimer() {
+    }
+
+    /**
+     * Returns the singleton hashed-wheel timer.
+     *
+     * @return hashed-wheel timer
+     */
+    public static HashedWheelTimer getTimer() {
+        if (PIMTimer.timer == null) {
+            initTimer();
+        }
+        return PIMTimer.timer;
+    }
+
+    // Start the PIM timer.
+    private static synchronized  void initTimer() {
+        if (PIMTimer.timer == null) {
+
+            // Create and start a new hashed wheel timer, if it does not exist.
+            HashedWheelTimer hwTimer = new HashedWheelTimer();
+            hwTimer.start();
+            PIMTimer.timer = hwTimer;
+        }
+    }
+}
diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java
new file mode 100644 (file)
index 0000000..29d1ce4
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * PIM Emulation speak hello messages and listen to Join/Prunes.
+ */
+package org.onosproject.pim.impl;
diff --git a/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml
new file mode 100644 (file)
index 0000000..c30e379
--- /dev/null
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.onosproject.pim.cli.PIMShowCommand"/>
+        </command>
+    </command-bundle>
+
+</blueprint>
index 334c690..b955130 100644 (file)
         <module>olt</module>
         <module>cip</module>
         <module>flowanalyzer</module>
-        <module>vtnrsc</module>
         <module>vtn</module>
-        <module>vtnweb</module>
         <module>dhcp</module>
         <module>cordvtn</module>
+        <module>mfwd</module>
+        <module>igmp</module>
+        <module>pim</module>
   </modules>
 
     <properties>
index 2eb96df..2eb1d5e 100644 (file)
@@ -23,6 +23,8 @@ import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv6;
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -214,27 +216,35 @@ public class ProxyArp {
             if (context.isHandled()) {
                 return;
             }
-            // If IPv6 NDP is disabled, don't handle IPv6 frames.
+
             InboundPacket pkt = context.inPacket();
             Ethernet ethPkt = pkt.parsed();
             if (ethPkt == null) {
                 return;
             }
-            if (!ipv6NeighborDiscovery && (ethPkt.getEtherType() == TYPE_IPV6)) {
-                return;
+
+            if (ethPkt.getEtherType() == TYPE_ARP) {
+                //handle the arp packet.
+                proxyArpService.handlePacket(context);
+            } else if (ipv6NeighborDiscovery && ethPkt.getEtherType() == TYPE_IPV6) {
+                IPv6 ipv6Pkt = (IPv6) ethPkt.getPayload();
+                if (ipv6Pkt.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
+                    ICMP6 icmp6Pkt = (ICMP6) ipv6Pkt.getPayload();
+                    if (icmp6Pkt.getIcmpType() == NEIGHBOR_SOLICITATION ||
+                        icmp6Pkt.getIcmpType() == NEIGHBOR_ADVERTISEMENT) {
+                        // handle ICMPv6 solicitations and advertisements
+                        proxyArpService.handlePacket(context);
+                    }
+                }
             }
 
+            // FIXME why were we listening to IPv4 frames at all?
             // Do not ARP for multicast packets.  Let mfwd handle them.
             if (ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
                 if (ethPkt.getDestinationMAC().isMulticast()) {
                     return;
                 }
             }
-
-            //handle the arp packet.
-            proxyArpService.handlePacket(context);
         }
     }
 }
-
-
diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java
new file mode 100644 (file)
index 0000000..01f4f70
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.reactive.routing;
+
+/**
+ * Specifies the type of an IP address or an IP prefix location.
+ */
+enum LocationType {
+    /**
+     * The location of an IP address or an IP prefix is in local SDN network.
+     */
+    LOCAL,
+    /**
+     * The location of an IP address or an IP prefix is outside local SDN network.
+     */
+    INTERNET,
+    /**
+     * There is no route for this IP address or IP prefix.
+     */
+    NO_ROUTE
+}
diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java
new file mode 100644 (file)
index 0000000..8e86056
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.reactive.routing;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.routing.IntentRequestListener;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.config.RoutingConfigurationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * FIB component for reactive routing intents.
+ */
+public class ReactiveRoutingFib implements IntentRequestListener {
+
+    private static final int PRIORITY_OFFSET = 100;
+    private static final int PRIORITY_MULTIPLIER = 5;
+    protected static final ImmutableList<Constraint> CONSTRAINTS
+            = ImmutableList.of(new PartialFailureConstraint());
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final ApplicationId appId;
+    private final HostService hostService;
+    private final RoutingConfigurationService configService;
+    private final InterfaceService interfaceService;
+    private final IntentSynchronizationService intentSynchronizer;
+
+    private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+
+    /**
+     * Class constructor.
+     *
+     * @param appId application ID to use to generate intents
+     * @param hostService host service
+     * @param configService routing configuration service
+     * @param interfaceService interface service
+     * @param intentSynchronizer intent synchronization service
+     */
+    public ReactiveRoutingFib(ApplicationId appId, HostService hostService,
+                              RoutingConfigurationService configService,
+                              InterfaceService interfaceService,
+                              IntentSynchronizationService intentSynchronizer) {
+        this.appId = appId;
+        this.hostService = hostService;
+        this.configService = configService;
+        this.interfaceService = interfaceService;
+        this.intentSynchronizer = intentSynchronizer;
+
+        routeIntents = Maps.newConcurrentMap();
+    }
+
+    @Override
+    public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
+        checkNotNull(hostIpAddress);
+        Set<ConnectPoint> ingressPoints =
+                configService.getBgpPeerConnectPoints();
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+
+        if (hostIpAddress.isIp4()) {
+            selector.matchEthType(Ethernet.TYPE_IPV4);
+        } else {
+            selector.matchEthType(Ethernet.TYPE_IPV6);
+        }
+
+        // Match the destination IP prefix at the first hop
+        IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
+        selector.matchIPDst(ipPrefix);
+
+        // Rewrite the destination MAC address
+        MacAddress hostMac = null;
+        ConnectPoint egressPoint = null;
+        for (Host host : hostService.getHostsByIp(hostIpAddress)) {
+            if (host.mac() != null) {
+                hostMac = host.mac();
+                egressPoint = host.location();
+                break;
+            }
+        }
+        if (hostMac == null) {
+            hostService.startMonitoringIp(hostIpAddress);
+            return;
+        }
+
+        TrafficTreatment.Builder treatment =
+                DefaultTrafficTreatment.builder().setEthDst(hostMac);
+        Key key = Key.of(ipPrefix.toString(), appId);
+        int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
+                + PRIORITY_OFFSET;
+        MultiPointToSinglePointIntent intent =
+                MultiPointToSinglePointIntent.builder()
+                        .appId(appId)
+                        .key(key)
+                        .selector(selector.build())
+                        .treatment(treatment.build())
+                        .ingressPoints(ingressPoints)
+                        .egressPoint(egressPoint)
+                        .priority(priority)
+                        .constraints(CONSTRAINTS)
+                        .build();
+
+        log.trace("Generates ConnectivityInternetToHost intent {}", intent);
+        submitReactiveIntent(ipPrefix, intent);
+    }
+
+    @Override
+    public void setUpConnectivityHostToInternet(IpAddress hostIp, IpPrefix prefix,
+                                                IpAddress nextHopIpAddress) {
+        // Find the attachment point (egress interface) of the next hop
+        Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
+        if (egressInterface == null) {
+            log.warn("No outgoing interface found for {}",
+                    nextHopIpAddress);
+            return;
+        }
+
+        Set<Host> hosts = hostService.getHostsByIp(nextHopIpAddress);
+        if (hosts.isEmpty()) {
+            log.warn("No host found for next hop IP address");
+            return;
+        }
+        MacAddress nextHopMacAddress = null;
+        for (Host host : hosts) {
+            nextHopMacAddress = host.mac();
+            break;
+        }
+
+        hosts = hostService.getHostsByIp(hostIp);
+        if (hosts.isEmpty()) {
+            log.warn("No host found for host IP address");
+            return;
+        }
+        Host host = hosts.stream().findFirst().get();
+        ConnectPoint ingressPoint = host.location();
+
+        // Generate the intent itself
+        ConnectPoint egressPort = egressInterface.connectPoint();
+        log.debug("Generating intent for prefix {}, next hop mac {}",
+                prefix, nextHopMacAddress);
+
+        // Match the destination IP prefix at the first hop
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        if (prefix.isIp4()) {
+            selector.matchEthType(Ethernet.TYPE_IPV4);
+            selector.matchIPDst(prefix);
+        } else {
+            selector.matchEthType(Ethernet.TYPE_IPV6);
+            selector.matchIPv6Dst(prefix);
+        }
+
+        // Rewrite the destination MAC address
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+                .setEthDst(nextHopMacAddress);
+        if (!egressInterface.vlan().equals(VlanId.NONE)) {
+            treatment.setVlanId(egressInterface.vlan());
+            // If we set VLAN ID, we have to make sure a VLAN tag exists.
+            // TODO support no VLAN -> VLAN routing
+            selector.matchVlanId(VlanId.ANY);
+        }
+
+        int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
+        Key key = Key.of(prefix.toString() + "-reactive", appId);
+        MultiPointToSinglePointIntent intent = MultiPointToSinglePointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector.build())
+                .treatment(treatment.build())
+                .ingressPoints(Collections.singleton(ingressPoint))
+                .egressPoint(egressPort)
+                .priority(priority)
+                .constraints(CONSTRAINTS)
+                .build();
+
+        submitReactiveIntent(prefix, intent);
+    }
+
+    @Override
+    public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
+                                            IpAddress srcIpAddress,
+                                            MacAddress srcMacAddress,
+                                            ConnectPoint srcConnectPoint) {
+        checkNotNull(dstIpAddress);
+        checkNotNull(srcIpAddress);
+        checkNotNull(srcMacAddress);
+        checkNotNull(srcConnectPoint);
+
+        IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
+        IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
+        ConnectPoint dstConnectPoint = null;
+        MacAddress dstMacAddress = null;
+
+        for (Host host : hostService.getHostsByIp(dstIpAddress)) {
+            if (host.mac() != null) {
+                dstMacAddress = host.mac();
+                dstConnectPoint = host.location();
+                break;
+            }
+        }
+        if (dstMacAddress == null) {
+            hostService.startMonitoringIp(dstIpAddress);
+            return;
+        }
+
+        //
+        // Handle intent from source host to destination host
+        //
+        MultiPointToSinglePointIntent srcToDstIntent =
+                hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
+                        dstMacAddress, srcConnectPoint);
+        submitReactiveIntent(dstIpPrefix, srcToDstIntent);
+
+        //
+        // Handle intent from destination host to source host
+        //
+
+        // Since we proactively handle the intent from destination host to
+        // source host, we should check whether there is an exiting intent
+        // first.
+        if (mp2pIntentExists(srcIpPrefix)) {
+            updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
+            return;
+        } else {
+            // There is no existing intent, create a new one.
+            MultiPointToSinglePointIntent dstToSrcIntent =
+                    hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
+                            srcMacAddress, dstConnectPoint);
+            submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
+        }
+    }
+
+    /**
+     * Generates MultiPointToSinglePointIntent for both source host and
+     * destination host located in local SDN network.
+     *
+     * @param dstIpAddress the destination IP address
+     * @param dstConnectPoint the destination host connect point
+     * @param dstMacAddress the MAC address of destination host
+     * @param srcConnectPoint the connect point where packet-in from
+     * @return the generated MultiPointToSinglePointIntent
+     */
+    private MultiPointToSinglePointIntent hostToHostIntentGenerator(
+            IpAddress dstIpAddress,
+            ConnectPoint dstConnectPoint,
+            MacAddress dstMacAddress,
+            ConnectPoint srcConnectPoint) {
+        checkNotNull(dstIpAddress);
+        checkNotNull(dstConnectPoint);
+        checkNotNull(dstMacAddress);
+        checkNotNull(srcConnectPoint);
+
+        Set<ConnectPoint> ingressPoints = new HashSet<>();
+        ingressPoints.add(srcConnectPoint);
+        IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        if (dstIpAddress.isIp4()) {
+            selector.matchEthType(Ethernet.TYPE_IPV4);
+            selector.matchIPDst(dstIpPrefix);
+        } else {
+            selector.matchEthType(Ethernet.TYPE_IPV6);
+            selector.matchIPv6Dst(dstIpPrefix);
+        }
+
+        // Rewrite the destination MAC address
+        TrafficTreatment.Builder treatment =
+                DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
+
+        Key key = Key.of(dstIpPrefix.toString(), appId);
+        int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
+                + PRIORITY_OFFSET;
+        MultiPointToSinglePointIntent intent =
+                MultiPointToSinglePointIntent.builder()
+                        .appId(appId)
+                        .key(key)
+                        .selector(selector.build())
+                        .treatment(treatment.build())
+                        .ingressPoints(ingressPoints)
+                        .egressPoint(dstConnectPoint)
+                        .priority(priority)
+                        .constraints(CONSTRAINTS)
+                        .build();
+
+        log.trace("Generates ConnectivityHostToHost = {} ", intent);
+        return intent;
+    }
+
+    @Override
+    public void updateExistingMp2pIntent(IpPrefix ipPrefix,
+                                         ConnectPoint ingressConnectPoint) {
+        checkNotNull(ipPrefix);
+        checkNotNull(ingressConnectPoint);
+
+        MultiPointToSinglePointIntent existingIntent =
+                getExistingMp2pIntent(ipPrefix);
+        if (existingIntent != null) {
+            Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
+            // Add host connect point into ingressPoints of the existing intent
+            if (ingressPoints.add(ingressConnectPoint)) {
+                MultiPointToSinglePointIntent updatedMp2pIntent =
+                        MultiPointToSinglePointIntent.builder()
+                                .appId(appId)
+                                .key(existingIntent.key())
+                                .selector(existingIntent.selector())
+                                .treatment(existingIntent.treatment())
+                                .ingressPoints(ingressPoints)
+                                .egressPoint(existingIntent.egressPoint())
+                                .priority(existingIntent.priority())
+                                .constraints(CONSTRAINTS)
+                                .build();
+
+                log.trace("Update an existing MultiPointToSinglePointIntent "
+                        + "to new intent = {} ", updatedMp2pIntent);
+                submitReactiveIntent(ipPrefix, updatedMp2pIntent);
+            }
+            // If adding ingressConnectPoint to ingressPoints failed, it
+            // because between the time interval from checking existing intent
+            // to generating new intent, onos updated this intent due to other
+            // packet-in and the new intent also includes the
+            // ingressConnectPoint. This will not affect reactive routing.
+        }
+    }
+
+    /**
+     * Submits a reactive intent to the intent synchronizer.
+     *
+     * @param ipPrefix IP prefix of the intent
+     * @param intent intent to submit
+     */
+    void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
+        routeIntents.put(ipPrefix, intent);
+
+        intentSynchronizer.submit(intent);
+    }
+
+    /**
+     * Gets the existing MultiPointToSinglePointIntent from memory for a given
+     * IP prefix.
+     *
+     * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
+     * @return the MultiPointToSinglePointIntent if found, otherwise null
+     */
+    private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix ipPrefix) {
+        checkNotNull(ipPrefix);
+        return routeIntents.get(ipPrefix);
+    }
+
+    @Override
+    public boolean mp2pIntentExists(IpPrefix ipPrefix) {
+        checkNotNull(ipPrefix);
+        return routeIntents.get(ipPrefix) != null;
+    }
+}
index ad78a1c..96aa06e 100644 (file)
@@ -25,27 +25,39 @@ import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
+import org.onosproject.routing.IntentRequestListener;
+import org.onosproject.routing.RouteEntry;
 import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.SdnIpService;
 import org.onosproject.routing.config.RoutingConfigurationService;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onlab.packet.Ethernet.TYPE_ARP;
 import static org.onlab.packet.Ethernet.TYPE_IPV4;
 import static org.onosproject.net.packet.PacketPriority.REACTIVE;
@@ -73,17 +85,33 @@ public class SdnIpReactiveRouting {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RoutingService routingService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected SdnIpService sdnIpService;
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RoutingConfigurationService config;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
     private ApplicationId appId;
 
+    private IntentRequestListener intentRequestListener;
+
     private ReactiveRoutingProcessor processor =
             new ReactiveRoutingProcessor();
 
     @Activate
     public void activate() {
         appId = coreService.registerApplication(APP_NAME);
+
+        intentRequestListener = new ReactiveRoutingFib(appId, hostService,
+                config, interfaceService,
+                sdnIpService.getIntentSynchronizationService());
+
         packetService.addProcessor(processor, PacketProcessor.director(2));
         requestIntercepts();
         log.info("SDN-IP Reactive Routing Started");
@@ -168,12 +196,11 @@ public class SdnIpReactiveRouting {
                 IpAddress srcIp =
                         IpAddress.valueOf(ipv4Packet.getSourceAddress());
                 MacAddress srcMac = ethPkt.getSourceMAC();
-                routingService.packetReactiveProcessor(dstIp, srcIp,
-                                                       srcConnectPoint, srcMac);
+                packetReactiveProcessor(dstIp, srcIp, srcConnectPoint, srcMac);
 
                 // TODO emit packet first or packetReactiveProcessor first
                 ConnectPoint egressConnectPoint = null;
-                egressConnectPoint = routingService.getEgressConnectPoint(dstIp);
+                egressConnectPoint = getEgressConnectPoint(dstIp);
                 if (egressConnectPoint != null) {
                     forwardPacketToDst(context, egressConnectPoint);
                 }
@@ -184,6 +211,173 @@ public class SdnIpReactiveRouting {
         }
     }
 
+    /**
+     * Routes packet reactively.
+     *
+     * @param dstIpAddress the destination IP address of a packet
+     * @param srcIpAddress the source IP address of a packet
+     * @param srcConnectPoint the connect point where a packet comes from
+     * @param srcMacAddress the source MAC address of a packet
+     */
+    private void packetReactiveProcessor(IpAddress dstIpAddress,
+                                        IpAddress srcIpAddress,
+                                        ConnectPoint srcConnectPoint,
+                                        MacAddress srcMacAddress) {
+        checkNotNull(dstIpAddress);
+        checkNotNull(srcIpAddress);
+        checkNotNull(srcConnectPoint);
+        checkNotNull(srcMacAddress);
+
+        //
+        // Step1: Try to update the existing intent first if it exists.
+        //
+        IpPrefix ipPrefix = null;
+        RouteEntry routeEntry = null;
+        if (config.isIpAddressLocal(dstIpAddress)) {
+            if (dstIpAddress.isIp4()) {
+                ipPrefix = IpPrefix.valueOf(dstIpAddress,
+                        Ip4Address.BIT_LENGTH);
+            } else {
+                ipPrefix = IpPrefix.valueOf(dstIpAddress,
+                        Ip6Address.BIT_LENGTH);
+            }
+        } else {
+            // Get IP prefix from BGP route table
+            routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
+            if (routeEntry != null) {
+                ipPrefix = routeEntry.prefix();
+            }
+        }
+        if (ipPrefix != null
+                && intentRequestListener.mp2pIntentExists(ipPrefix)) {
+            intentRequestListener.updateExistingMp2pIntent(ipPrefix,
+                    srcConnectPoint);
+            return;
+        }
+
+        //
+        // Step2: There is no existing intent for the destination IP address.
+        // Check whether it is necessary to create a new one. If necessary then
+        // create a new one.
+        //
+        TrafficType trafficType =
+                trafficTypeClassifier(srcConnectPoint, dstIpAddress);
+
+        switch (trafficType) {
+        case HOST_TO_INTERNET:
+            // If the destination IP address is outside the local SDN network.
+            // The Step 1 has already handled it. We do not need to do anything here.
+            intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
+                    ipPrefix, routeEntry.nextHop());
+            break;
+        case INTERNET_TO_HOST:
+            intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
+            break;
+        case HOST_TO_HOST:
+            intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
+                    srcIpAddress, srcMacAddress, srcConnectPoint);
+            break;
+        case INTERNET_TO_INTERNET:
+            log.trace("This is transit traffic, "
+                    + "the intent should be preinstalled already");
+            break;
+        case DROP:
+            // TODO here should setUpDropPacketIntent(...);
+            // We need a new type of intent here.
+            break;
+        case UNKNOWN:
+            log.trace("This is unknown traffic, so we do nothing");
+            break;
+        default:
+            break;
+        }
+    }
+
+    /**
+     * Classifies the traffic and return the traffic type.
+     *
+     * @param srcConnectPoint the connect point where the packet comes from
+     * @param dstIp the destination IP address in packet
+     * @return the traffic type which this packet belongs to
+     */
+    private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
+                                                             IpAddress dstIp) {
+        LocationType dstIpLocationType = getLocationType(dstIp);
+        Optional<Interface> srcInterface =
+                interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
+        Set<ConnectPoint> ingressPoints = config.getBgpPeerConnectPoints();
+
+        switch (dstIpLocationType) {
+        case INTERNET:
+            if (srcInterface.isPresent() &&
+                    (!ingressPoints.contains(srcConnectPoint))) {
+                return TrafficType.HOST_TO_INTERNET;
+            } else {
+                return TrafficType.INTERNET_TO_INTERNET;
+            }
+        case LOCAL:
+            if (srcInterface.isPresent() &&
+                    (!ingressPoints.contains(srcConnectPoint))) {
+                return TrafficType.HOST_TO_HOST;
+            } else {
+                // TODO Currently we only consider local public prefixes.
+                // In the future, we will consider the local private prefixes.
+                // If dstIpLocationType is a local private, we should return
+                // TrafficType.DROP.
+                return TrafficType.INTERNET_TO_HOST;
+            }
+        case NO_ROUTE:
+            return TrafficType.DROP;
+        default:
+            return TrafficType.UNKNOWN;
+        }
+    }
+
+    /**
+     * Evaluates the location of an IP address and returns the location type.
+     *
+     * @param ipAddress the IP address to evaluate
+     * @return the IP address location type
+     */
+    private LocationType getLocationType(IpAddress ipAddress) {
+        if (config.isIpAddressLocal(ipAddress)) {
+            return LocationType.LOCAL;
+        } else if (routingService.getLongestMatchableRouteEntry(ipAddress) != null) {
+            return LocationType.INTERNET;
+        } else {
+            return LocationType.NO_ROUTE;
+        }
+    }
+
+    public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
+        LocationType type = getLocationType(dstIpAddress);
+        if (type == LocationType.LOCAL) {
+            Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
+            if (!hosts.isEmpty()) {
+                return hosts.iterator().next().location();
+            } else {
+                hostService.startMonitoringIp(dstIpAddress);
+                return null;
+            }
+        } else if (type == LocationType.INTERNET) {
+            IpAddress nextHopIpAddress = null;
+            RouteEntry routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
+            if (routeEntry != null) {
+                nextHopIpAddress = routeEntry.nextHop();
+                Interface it = interfaceService.getMatchingInterface(nextHopIpAddress);
+                if (it != null) {
+                    return it.connectPoint();
+                } else {
+                    return null;
+                }
+            } else {
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Emits the specified packet onto the network.
      *
diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java
new file mode 100644 (file)
index 0000000..134126b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.reactive.routing;
+
+/**
+ * Specifies the type of traffic.
+ * <p>
+ * We classify traffic by the first packet of each traffic.
+ * </p>
+ */
+enum TrafficType {
+    /**
+     * Traffic from a host located in local SDN network wants to
+     * communicate with destination host located in Internet (outside
+     * local SDN network).
+     */
+    HOST_TO_INTERNET,
+    /**
+     * Traffic from Internet wants to communicate with a host located
+     * in local SDN network.
+     */
+    INTERNET_TO_HOST,
+    /**
+     * Both the source host and destination host of a traffic are in
+     * local SDN network.
+     */
+    HOST_TO_HOST,
+    /**
+     * Traffic from Internet wants to traverse local SDN network.
+     */
+    INTERNET_TO_INTERNET,
+    /**
+     * Any traffic wants to communicate with a destination which has
+     * no route, or traffic from Internet wants to access a local private
+     * IP address.
+     */
+    DROP,
+    /**
+     * Traffic does not belong to the types above.
+     */
+    UNKNOWN
+}
index 2d1bb31..1069ec5 100644 (file)
@@ -46,6 +46,16 @@ public interface IntentRequestListener {
                                      MacAddress srcMacAddress,
                                      ConnectPoint srcConnectPoint);
 
+    /**
+     * Sets up connectivity for packet from a local host to the Internet.
+     *
+     * @param hostIp IP address of the local host
+     * @param prefix external IP prefix that the host is talking to
+     * @param nextHopIpAddress IP address of the next hop router for the prefix
+     */
+    void setUpConnectivityHostToInternet(IpAddress hostIp, IpPrefix prefix,
+                                                IpAddress nextHopIpAddress);
+
     /**
      * Adds one new ingress connect point into ingress points of an existing
      * intent and resubmits the new intent.
diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java
new file mode 100644 (file)
index 0000000..dc6a838
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.routing;
+
+import org.onosproject.net.intent.Intent;
+
+/**
+ * Submits and withdraws intents to the IntentService from a single point in
+ * the cluster at any one time. The provided intents will be synchronized with
+ * the IntentService on leadership change.
+ */
+public interface IntentSynchronizationService {
+
+    /**
+     * Submits and intent to the synchronizer.
+     * <p>
+     * The intent will be submitted directly to the IntentService if this node
+     * is the leader, otherwise it will be stored in the synchronizer for
+     * synchronization if this node becomes the leader.
+     * </p>
+     *
+     * @param intent intent to submit
+     */
+    void submit(Intent intent);
+
+    /**
+     * Withdraws an intent from the synchronizer.
+     * <p>
+     * The intent will be withdrawn directly from the IntentService if this node
+     * is the leader. The intent will be removed from the synchronizer's
+     * in-memory storage.
+     * </p>
+     *
+     * @param intent intent to withdraw
+     */
+    void withdraw(Intent intent);
+}
index 8b7040e..7399ed7 100644 (file)
@@ -16,8 +16,6 @@
 package org.onosproject.routing;
 
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.routing.config.BgpConfig;
 
 import java.util.Collection;
@@ -31,63 +29,6 @@ public interface RoutingService {
 
     Class<BgpConfig> CONFIG_CLASS = BgpConfig.class;
 
-    /**
-     * Specifies the type of an IP address or an IP prefix location.
-     */
-    enum LocationType {
-        /**
-         * The location of an IP address or an IP prefix is in local SDN network.
-         */
-        LOCAL,
-        /**
-         * The location of an IP address or an IP prefix is outside local SDN network.
-         */
-        INTERNET,
-        /**
-         * There is no route for this IP address or IP prefix.
-         */
-        NO_ROUTE
-    }
-
-    /**
-     * Specifies the type of traffic.
-     * <p>
-     * We classify traffic by the first packet of each traffic.
-     * </p>
-     */
-    enum TrafficType {
-        /**
-         * Traffic from a host located in local SDN network wants to
-         * communicate with destination host located in Internet (outside
-         * local SDN network).
-         */
-        HOST_TO_INTERNET,
-        /**
-         * Traffic from Internet wants to communicate with a host located
-         * in local SDN network.
-         */
-        INTERNET_TO_HOST,
-        /**
-         * Both the source host and destination host of a traffic are in
-         * local SDN network.
-         */
-        HOST_TO_HOST,
-        /**
-         * Traffic from Internet wants to traverse local SDN network.
-         */
-        INTERNET_TO_INTERNET,
-        /**
-         * Any traffic wants to communicate with a destination which has
-         * no route, or traffic from Internet wants to access a local private
-         * IP address.
-         */
-        DROP,
-        /**
-         * Traffic does not belong to the types above.
-         */
-        UNKNOWN
-    }
-
     /**
      * Starts the routing service.
      */
@@ -100,15 +41,6 @@ public interface RoutingService {
      */
     void addFibListener(FibListener fibListener);
 
-    /**
-     * Adds intent creation and submission listener.
-     *
-     * @param intentRequestListener listener to send intent creation and
-     *        submission request to
-     */
-    void addIntentRequestListener(IntentRequestListener
-                                         intentRequestListener);
-
     /**
      * Stops the routing service.
      */
@@ -128,14 +60,6 @@ public interface RoutingService {
      */
     Collection<RouteEntry> getRoutes6();
 
-    /**
-     * Evaluates the location of an IP address and returns the location type.
-     *
-     * @param ipAddress the IP address to evaluate
-     * @return the IP address location type
-     */
-    LocationType getLocationType(IpAddress ipAddress);
-
     /**
      * Finds out the route entry which has the longest matchable IP prefix.
      *
@@ -145,25 +69,4 @@ public interface RoutingService {
      */
     RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress);
 
-    /**
-     * Finds out the egress connect point where to emit the first packet
-     * based on destination IP address.
-     *
-     * @param dstIpAddress the destination IP address
-     * @return the egress connect point if found, otherwise null
-     */
-    ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress);
-
-    /**
-     * Routes packet reactively.
-     *
-     * @param dstIpAddress the destination IP address of a packet
-     * @param srcIpAddress the source IP address of a packet
-     * @param srcConnectPoint the connect point where a packet comes from
-     * @param srcMacAddress the source MAC address of a packet
-     */
-    void packetReactiveProcessor(IpAddress dstIpAddress,
-                                        IpAddress srcIpAddress,
-                                        ConnectPoint srcConnectPoint,
-                                        MacAddress srcMacAddress);
 }
diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java
new file mode 100644 (file)
index 0000000..0945336
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routing;
+
+/**
+ * Service interface exported by SDN-IP.
+ */
+public interface SdnIpService {
+
+    /**
+     * Changes whether this SDN-IP instance is the primary or not based on the
+     * boolean parameter.
+     *
+     * @param isPrimary true if the instance is primary, false if it is not
+     */
+    void modifyPrimary(boolean isPrimary);
+
+    /**
+     * Gets the intent synchronization service.
+     *
+     * @return intent synchronization service
+     */
+    // TODO fix service resolution in SDN-IP
+    IntentSynchronizationService getIntentSynchronizationService();
+
+}
index c47d276..b89eb2d 100644 (file)
@@ -19,10 +19,15 @@ import org.hamcrest.Matchers;
 import org.junit.Test;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.Ip6Prefix;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Unit tests for the RouteEntry class.
@@ -35,34 +40,61 @@ public class RouteEntryTest {
     public void testConstructor() {
         Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24");
         Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8");
-
         RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
         assertThat(routeEntry.toString(),
                    is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
+
+        Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop6 = Ip6Address.valueOf("2000::1");
+        RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+        assertThat(routeEntry6.toString(),
+                   is("RouteEntry{prefix=1000::/64, nextHop=2000::1}"));
     }
 
     /**
      * Tests invalid class constructor for null IPv4 prefix.
      */
     @Test(expected = NullPointerException.class)
-    public void testInvalidConstructorNullPrefix() {
+    public void testInvalidConstructorNullIpv4Prefix() {
         Ip4Prefix prefix = null;
         Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8");
 
         new RouteEntry(prefix, nextHop);
     }
 
+    /**
+     * Tests invalid class constructor for null IPv6 prefix.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testInvalidConstructorNullIpv6Prefix() {
+        Ip6Prefix prefix = null;
+        Ip6Address nextHop = Ip6Address.valueOf("2000::1");
+
+        new RouteEntry(prefix, nextHop);
+    }
+
     /**
      * Tests invalid class constructor for null IPv4 next-hop.
      */
     @Test(expected = NullPointerException.class)
-    public void testInvalidConstructorNullNextHop() {
+    public void testInvalidConstructorNullIpv4NextHop() {
         Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24");
         Ip4Address nextHop = null;
 
         new RouteEntry(prefix, nextHop);
     }
 
+    /**
+     * Tests invalid class constructor for null IPv6 next-hop.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testInvalidConstructorNullIpv6NextHop() {
+        Ip6Prefix prefix = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop = null;
+
+        new RouteEntry(prefix, nextHop);
+    }
+
     /**
      * Tests getting the fields of a route entry.
      */
@@ -70,10 +102,15 @@ public class RouteEntryTest {
     public void testGetFields() {
         Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24");
         Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8");
-
         RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
         assertThat(routeEntry.prefix(), is(prefix));
         assertThat(routeEntry.nextHop(), is(nextHop));
+
+        Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop6 = Ip6Address.valueOf("2000::1");
+        RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+        assertThat(routeEntry6.prefix(), is(prefix6));
+        assertThat(routeEntry6.nextHop(), is(nextHop6));
     }
 
     /**
@@ -105,6 +142,33 @@ public class RouteEntryTest {
         prefix = Ip4Prefix.valueOf("255.255.255.255/32");
         assertThat(RouteEntry.createBinaryString(prefix),
                    is("11111111111111111111111111111111"));
+
+        Ip6Prefix prefix6;
+        Pattern pattern;
+        Matcher matcher;
+
+        prefix6 = Ip6Prefix.valueOf("::/0");
+        assertThat(RouteEntry.createBinaryString(prefix6), is(""));
+
+        prefix6 = Ip6Prefix.valueOf("2000::1000/112");
+        pattern = Pattern.compile("00100{108}");
+        matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+        assertTrue(matcher.matches());
+
+        prefix6 = Ip6Prefix.valueOf("2000::1000/116");
+        pattern = Pattern.compile("00100{108}0001");
+        matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+        assertTrue(matcher.matches());
+
+        prefix6 = Ip6Prefix.valueOf("2000::2000/116");
+        pattern = Pattern.compile("00100{108}0010");
+        matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+        assertTrue(matcher.matches());
+
+        prefix6 = Ip6Prefix.valueOf("2000::1234/128");
+        pattern = Pattern.compile("00100{108}0001001000110100");
+        matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6));
+        assertTrue(matcher.matches());
     }
 
     /**
@@ -121,6 +185,16 @@ public class RouteEntryTest {
         RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
 
         assertThat(routeEntry1, is(routeEntry2));
+
+        Ip6Prefix prefix3 = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop3 = Ip6Address.valueOf("2000::2");
+        RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3);
+
+        Ip6Prefix prefix4 = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop4 = Ip6Address.valueOf("2000::2");
+        RouteEntry routeEntry4 = new RouteEntry(prefix4, nextHop4);
+
+        assertThat(routeEntry3, is(routeEntry4));
     }
 
     /**
@@ -142,6 +216,21 @@ public class RouteEntryTest {
 
         assertThat(routeEntry1, Matchers.is(not(routeEntry2)));
         assertThat(routeEntry1, Matchers.is(not(routeEntry3)));
+
+        Ip6Prefix prefix4 = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop4 = Ip6Address.valueOf("2000::1");
+        RouteEntry routeEntry4 = new RouteEntry(prefix4, nextHop4);
+
+        Ip6Prefix prefix5 = Ip6Prefix.valueOf("1000::/65");
+        Ip6Address nextHop5 = Ip6Address.valueOf("2000::1");
+        RouteEntry routeEntry5 = new RouteEntry(prefix5, nextHop5);
+
+        Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop6 = Ip6Address.valueOf("2000::2");
+        RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+
+        assertThat(routeEntry4, Matchers.is(not(routeEntry5)));
+        assertThat(routeEntry4, Matchers.is(not(routeEntry6)));
     }
 
     /**
@@ -155,5 +244,12 @@ public class RouteEntryTest {
 
         assertThat(routeEntry.toString(),
                    is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
+
+        Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64");
+        Ip6Address nextHop6 = Ip6Address.valueOf("2000::1");
+        RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6);
+
+        assertThat(routeEntry6.toString(),
+                   is("RouteEntry{prefix=1000::/64, nextHop=2000::1}"));
     }
 }
index 4520690..c58bc1b 100644 (file)
@@ -32,8 +32,8 @@ import java.util.List;
 public class Configuration {
     // We call the BGP routers in our SDN network the BGP speakers, and call
     // the BGP routers outside our SDN network the BGP peers.
-    private List<BgpSpeaker> bgpSpeakers;
-    private List<BgpPeer> peers;
+    private List<BgpSpeaker> bgpSpeakers = Collections.emptyList();
+    private List<BgpPeer> peers = Collections.emptyList();
     private MacAddress virtualGatewayMacAddress;
 
     // All IP prefixes from the configuration are local
index 0a6f9d4..19c3f70 100644 (file)
@@ -195,13 +195,16 @@ public class RoutingConfigurationImpl implements RoutingConfigurationService {
         }
 
         BgpConfig bgpConfig = configService.getConfig(routerAppId, BgpConfig.class);
-
-        return bgpConfig.bgpSpeakers().stream()
-                .flatMap(speaker -> speaker.peers().stream())
-                .map(peer -> interfaceService.getMatchingInterface(peer))
-                .filter(intf -> intf != null)
-                .map(intf -> intf.connectPoint())
-                .collect(Collectors.toSet());
+        if (bgpConfig == null) {
+            return Collections.emptySet();
+        } else {
+            return bgpConfig.bgpSpeakers().stream()
+                    .flatMap(speaker -> speaker.peers().stream())
+                    .map(peer -> interfaceService.getMatchingInterface(peer))
+                    .filter(intf -> intf != null)
+                    .map(intf -> intf.connectPoint())
+                    .collect(Collectors.toSet());
+        }
     }
 
     @Override
index 6700d53..75d789a 100644 (file)
@@ -35,9 +35,7 @@ import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.CoreService;
-import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
@@ -46,7 +44,6 @@ import org.onosproject.routing.BgpService;
 import org.onosproject.routing.FibEntry;
 import org.onosproject.routing.FibListener;
 import org.onosproject.routing.FibUpdate;
-import org.onosproject.routing.IntentRequestListener;
 import org.onosproject.routing.RouteEntry;
 import org.onosproject.routing.RouteListener;
 import org.onosproject.routing.RouteUpdate;
@@ -61,7 +58,6 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
@@ -100,7 +96,6 @@ public class Router implements RoutingService {
     private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
 
     private FibListener fibComponent;
-    private IntentRequestListener intentRequestListener;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
@@ -145,12 +140,6 @@ public class Router implements RoutingService {
     @Override
     public void addFibListener(FibListener fibListener) {
         this.fibComponent = checkNotNull(fibListener);
-
-    }
-
-    @Override
-    public void addIntentRequestListener(IntentRequestListener intentRequestListener) {
-        this.intentRequestListener = checkNotNull(intentRequestListener);
     }
 
     @Override
@@ -287,12 +276,10 @@ public class Router implements RoutingService {
     void addRibRoute(RouteEntry routeEntry) {
         if (routeEntry.isIp4()) {
             // IPv4
-            ribTable4.put(createBinaryString(routeEntry.prefix()),
-                    routeEntry);
+            ribTable4.put(createBinaryString(routeEntry.prefix()), routeEntry);
         } else {
             // IPv6
-            ribTable6.put(createBinaryString(routeEntry.prefix()),
-                    routeEntry);
+            ribTable6.put(createBinaryString(routeEntry.prefix()), routeEntry);
         }
     }
 
@@ -552,17 +539,6 @@ public class Router implements RoutingService {
         }
     }
 
-    @Override
-    public LocationType getLocationType(IpAddress ipAddress) {
-        if (routingConfigurationService.isIpAddressLocal(ipAddress)) {
-            return LocationType.LOCAL;
-        } else if (getLongestMatchableRouteEntry(ipAddress) != null) {
-            return LocationType.INTERNET;
-        } else {
-            return LocationType.NO_ROUTE;
-        }
-    }
-
     @Override
     public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
         RouteEntry routeEntry = null;
@@ -587,142 +563,4 @@ public class Router implements RoutingService {
         return routeEntry;
     }
 
-    @Override
-    public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
-        LocationType type = getLocationType(dstIpAddress);
-        if (type == LocationType.LOCAL) {
-            Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
-            if (!hosts.isEmpty()) {
-                return hosts.iterator().next().location();
-            } else {
-                hostService.startMonitoringIp(dstIpAddress);
-                return null;
-            }
-        } else if (type == LocationType.INTERNET) {
-            IpAddress nextHopIpAddress = null;
-            RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
-            if (routeEntry != null) {
-                nextHopIpAddress = routeEntry.nextHop();
-                Interface it = interfaceService.getMatchingInterface(nextHopIpAddress);
-                if (it != null) {
-                    return it.connectPoint();
-                } else {
-                    return null;
-                }
-            } else {
-                return null;
-            }
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public void packetReactiveProcessor(IpAddress dstIpAddress,
-                                        IpAddress srcIpAddress,
-                                        ConnectPoint srcConnectPoint,
-                                        MacAddress srcMacAddress) {
-        checkNotNull(dstIpAddress);
-        checkNotNull(srcIpAddress);
-        checkNotNull(srcConnectPoint);
-        checkNotNull(srcMacAddress);
-
-        //
-        // Step1: Try to update the existing intent first if it exists.
-        //
-        IpPrefix ipPrefix = null;
-        if (routingConfigurationService.isIpAddressLocal(dstIpAddress)) {
-            if (dstIpAddress.isIp4()) {
-                ipPrefix = IpPrefix.valueOf(dstIpAddress,
-                        Ip4Address.BIT_LENGTH);
-            } else {
-                ipPrefix = IpPrefix.valueOf(dstIpAddress,
-                        Ip6Address.BIT_LENGTH);
-            }
-        } else {
-            // Get IP prefix from BGP route table
-            RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress);
-            if (routeEntry != null) {
-                ipPrefix = routeEntry.prefix();
-            }
-        }
-        if (ipPrefix != null
-                && intentRequestListener.mp2pIntentExists(ipPrefix)) {
-            intentRequestListener.updateExistingMp2pIntent(ipPrefix,
-                    srcConnectPoint);
-            return;
-        }
-
-        //
-        // Step2: There is no existing intent for the destination IP address.
-        // Check whether it is necessary to create a new one. If necessary then
-        // create a new one.
-        //
-        TrafficType trafficType =
-                trafficTypeClassifier(srcConnectPoint, dstIpAddress);
-
-        switch (trafficType) {
-        case HOST_TO_INTERNET:
-            // If the destination IP address is outside the local SDN network.
-            // The Step 1 has already handled it. We do not need to do anything here.
-            break;
-        case INTERNET_TO_HOST:
-            intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
-            break;
-        case HOST_TO_HOST:
-            intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
-                    srcIpAddress, srcMacAddress, srcConnectPoint);
-            break;
-        case INTERNET_TO_INTERNET:
-            log.trace("This is transit traffic, "
-                    + "the intent should be preinstalled already");
-            break;
-        case DROP:
-            // TODO here should setUpDropPaccketIntent(...);
-            // We need a new type of intent here.
-            break;
-        case UNKNOWN:
-            log.trace("This is unknown traffic, so we do nothing");
-            break;
-        default:
-            break;
-        }
-    }
-
-    /**
-     * Classifies the traffic and return the traffic type.
-     *
-     * @param srcConnectPoint the connect point where the packet comes from
-     * @param dstIp the destination IP address in packet
-     * @return the traffic type which this packet belongs to
-     */
-    private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
-                                              IpAddress dstIp) {
-        LocationType dstIpLocationType = getLocationType(dstIp);
-        Optional<Interface> srcInterface =
-                interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
-
-        switch (dstIpLocationType) {
-        case INTERNET:
-            if (!srcInterface.isPresent()) {
-                return TrafficType.HOST_TO_INTERNET;
-            } else {
-                return TrafficType.INTERNET_TO_INTERNET;
-            }
-        case LOCAL:
-            if (!srcInterface.isPresent()) {
-                return TrafficType.HOST_TO_HOST;
-            } else {
-                // TODO Currently we only consider local public prefixes.
-                // In the future, we will consider the local private prefixes.
-                // If dstIpLocationType is a local private, we should return
-                // TrafficType.DROP.
-                return TrafficType.INTERNET_TO_HOST;
-            }
-        case NO_ROUTE:
-            return TrafficType.DROP;
-        default:
-            return TrafficType.UNKNOWN;
-        }
-    }
 }
index 29526ff..3c86820 100644 (file)
@@ -18,10 +18,7 @@ package org.onosproject.routing.impl;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.routing.FibListener;
-import org.onosproject.routing.IntentRequestListener;
 import org.onosproject.routing.RouteEntry;
 import org.onosproject.routing.RoutingService;
 import org.onosproject.routing.StaticRoutingService;
@@ -48,11 +45,6 @@ public class StaticRouter implements RoutingService, StaticRoutingService {
         this.fibListener = fibListener;
     }
 
-    @Override
-    public void addIntentRequestListener(IntentRequestListener intentRequestListener) {
-
-    }
-
     @Override
     public void stop() {
 
@@ -68,27 +60,11 @@ public class StaticRouter implements RoutingService, StaticRoutingService {
         return null;
     }
 
-    @Override
-    public LocationType getLocationType(IpAddress ipAddress) {
-        return null;
-    }
-
     @Override
     public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
         return null;
     }
 
-    @Override
-    public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
-        return null;
-    }
-
-    @Override
-    public void packetReactiveProcessor(IpAddress dstIpAddress, IpAddress srcIpAddress,
-                                        ConnectPoint srcConnectPoint, MacAddress srcMacAddress) {
-
-    }
-
     @Override
     public FibListener getFibListener() {
         return fibListener;
index 45bc309..c73e18c 100644 (file)
@@ -22,6 +22,8 @@ import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.Ip6Prefix;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
@@ -81,6 +83,13 @@ public class RouterTest {
             DeviceId.deviceId("of:0000000000000004"),
             PortNumber.portNumber(1));
 
+    private static final ConnectPoint SW5_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000005"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW6_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000006"),
+            PortNumber.portNumber(1));
     private Router router;
 
     @Before
@@ -132,7 +141,6 @@ public class RouterTest {
         hostService.startMonitoringIp(host1Address);
         expectLastCall().anyTimes();
 
-
         IpAddress host2Address = IpAddress.valueOf("192.168.20.1");
         Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE,
                 MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
@@ -148,7 +156,7 @@ public class RouterTest {
         IpAddress host3Address = IpAddress.valueOf("192.168.40.1");
         Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
                 MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1),
-                new HostLocation(SW4_ETH1, 1),
+                new HostLocation(SW3_ETH1, 1),
                 Sets.newHashSet(host3Address));
 
         expect(hostService.getHostsByIp(host3Address))
@@ -156,6 +164,41 @@ public class RouterTest {
         hostService.startMonitoringIp(host3Address);
         expectLastCall().anyTimes();
 
+        IpAddress host4Address = IpAddress.valueOf("1000::1");
+        Host host4 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                 MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE,
+                 new HostLocation(SW4_ETH1, 1),
+                 Sets.newHashSet(host4Address));
+
+        expect(hostService.getHostsByIp(host4Address))
+                .andReturn(Sets.newHashSet(host4)).anyTimes();
+        hostService.startMonitoringIp(host4Address);
+        expectLastCall().anyTimes();
+
+        IpAddress host5Address = IpAddress.valueOf("2000::1");
+        Host host5 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                 MacAddress.valueOf("00:00:00:00:00:05"), VlanId.NONE,
+                 new HostLocation(SW5_ETH1, 1),
+                 Sets.newHashSet(host5Address));
+
+        expect(hostService.getHostsByIp(host5Address))
+                .andReturn(Sets.newHashSet(host5)).anyTimes();
+        hostService.startMonitoringIp(host5Address);
+        expectLastCall().anyTimes();
+
+        // Next hop on a VLAN
+        IpAddress host6Address = IpAddress.valueOf("3000::1");
+        Host host6 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                 MacAddress.valueOf("00:00:00:00:00:06"), VlanId.vlanId((short) 1),
+                 new HostLocation(SW6_ETH1, 1),
+                 Sets.newHashSet(host6Address));
+
+        expect(hostService.getHostsByIp(host6Address))
+                .andReturn(Sets.newHashSet(host6)).anyTimes();
+        hostService.startMonitoringIp(host6Address);
+        expectLastCall().anyTimes();
+
+
         // Called during shutdown
         hostService.removeListener(anyObject(HostListener.class));
 
@@ -163,10 +206,10 @@ public class RouterTest {
     }
 
     /**
-     * Tests adding a route entry.
+     * Tests adding a IPv4 route entry.
      */
     @Test
-    public void testRouteAdd() {
+    public void testIpv4RouteAdd() {
         // Construct a route entry
         IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
         IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1");
@@ -175,7 +218,34 @@ public class RouterTest {
 
         // Expected FIB entry
         FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
-                                         MacAddress.valueOf("00:00:00:00:00:01"));
+                MacAddress.valueOf("00:00:00:00:00:01"));
+
+        fibListener.update(Collections.singletonList(new FibUpdate(
+                FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
+
+        replay(fibListener);
+
+        router.processRouteUpdates(Collections.singletonList(
+                new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
+
+        verify(fibListener);
+    }
+
+
+    /**
+     * Tests adding a IPv6 route entry.
+     */
+    @Test
+    public void testIpv6RouteAdd() {
+        // Construct a route entry
+        IpPrefix prefix = Ip6Prefix.valueOf("4000::/64");
+        IpAddress nextHopIp = Ip6Address.valueOf("1000::1");
+
+        RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
+
+        // Expected FIB entry
+        FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
+                MacAddress.valueOf("00:00:00:00:00:04"));
 
         fibListener.update(Collections.singletonList(new FibUpdate(
                 FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
@@ -188,13 +258,14 @@ public class RouterTest {
         verify(fibListener);
     }
 
+
     /**
-     * Tests updating a route entry.
+     * Tests updating a IPv4 route entry.
      */
     @Test
     public void testRouteUpdate() {
         // Firstly add a route
-        testRouteAdd();
+        testIpv4RouteAdd();
 
         // Route entry with updated next hop for the original prefix
         RouteEntry routeEntryUpdate = new RouteEntry(
@@ -230,12 +301,53 @@ public class RouterTest {
     }
 
     /**
-     * Tests deleting a route entry.
+     * Tests updating a IPv6 route entry.
      */
     @Test
-    public void testRouteDelete() {
+    public void testIpv6RouteUpdate() {
         // Firstly add a route
-        testRouteAdd();
+        testIpv6RouteAdd();
+
+        // Route entry with updated next hop for the original prefix
+        RouteEntry routeEntryUpdate = new RouteEntry(
+                Ip6Prefix.valueOf("4000::/64"),
+                Ip6Address.valueOf("2000::1"));
+
+        // The old FIB entry will be withdrawn
+        FibEntry withdrawFibEntry = new FibEntry(
+                Ip6Prefix.valueOf("4000::/64"), null, null);
+
+        // A new FIB entry will be added
+        FibEntry updateFibEntry = new FibEntry(
+                Ip6Prefix.valueOf("4000::/64"),
+                Ip6Address.valueOf("2000::1"),
+                MacAddress.valueOf("00:00:00:00:00:05"));
+
+        reset(fibListener);
+        fibListener.update(Collections.singletonList(new FibUpdate(
+                                   FibUpdate.Type.UPDATE, updateFibEntry)),
+                           Collections.singletonList(new FibUpdate(
+                                   FibUpdate.Type.DELETE, withdrawFibEntry)));
+        replay(fibListener);
+
+        reset(routingConfigurationService);
+        expect(routingConfigurationService.isIpPrefixLocal(
+                anyObject(IpPrefix.class))).andReturn(false);
+        replay(routingConfigurationService);
+
+        router.processRouteUpdates(Collections.singletonList(new RouteUpdate(
+                RouteUpdate.Type.UPDATE, routeEntryUpdate)));
+
+        verify(fibListener);
+    }
+
+    /**
+     * Tests deleting a IPv4 route entry.
+     */
+    @Test
+    public void testIpv4RouteDelete() {
+        // Firstly add a route
+        testIpv4RouteAdd();
 
         RouteEntry deleteRouteEntry = new RouteEntry(
                 Ip4Prefix.valueOf("1.1.1.0/24"),
@@ -257,10 +369,37 @@ public class RouterTest {
     }
 
     /**
-     * Tests adding a route whose next hop is the local BGP speaker.
+     * Tests deleting a IPv6 route entry.
      */
     @Test
-    public void testLocalRouteAdd() {
+    public void testIpv6RouteDelete() {
+        // Firstly add a route
+        testIpv6RouteAdd();
+
+        RouteEntry deleteRouteEntry = new RouteEntry(
+                Ip6Prefix.valueOf("4000::/64"),
+                Ip6Address.valueOf("1000::1"));
+
+        FibEntry deleteFibEntry = new FibEntry(
+                Ip6Prefix.valueOf("4000::/64"), null, null);
+
+        reset(fibListener);
+        fibListener.update(Collections.emptyList(), Collections.singletonList(
+                new FibUpdate(FibUpdate.Type.DELETE, deleteFibEntry)));
+
+        replay(fibListener);
+
+        router.processRouteUpdates(Collections.singletonList(
+                new RouteUpdate(RouteUpdate.Type.DELETE, deleteRouteEntry)));
+
+        verify(fibListener);
+    }
+
+    /**
+     * Tests adding a IPv4 route whose next hop is the local BGP speaker.
+     */
+    @Test
+    public void testIpv4LocalRouteAdd() {
         // Construct a route entry, the next hop is the local BGP speaker
         RouteEntry routeEntry = new RouteEntry(
                 Ip4Prefix.valueOf("1.1.1.0/24"),
@@ -284,4 +423,33 @@ public class RouterTest {
         assertTrue(router.getRoutes4().contains(routeEntry));
         verify(fibListener);
     }
+
+    /**
+     * Tests adding a IPv6 route whose next hop is the local BGP speaker.
+     */
+    @Test
+    public void testIpv6LocalRouteAdd() {
+        // Construct a route entry, the next hop is the local BGP speaker
+        RouteEntry routeEntry = new RouteEntry(
+                Ip6Prefix.valueOf("4000::/64"),
+                Ip6Address.valueOf("::"));
+
+        // No methods on the FIB listener should be called
+        replay(fibListener);
+
+        reset(routingConfigurationService);
+        expect(routingConfigurationService.isIpPrefixLocal(
+                anyObject(IpPrefix.class))).andReturn(true);
+        replay(routingConfigurationService);
+
+        // Call the processRouteUpdates() method in Router class
+        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
+                                                  routeEntry);
+        router.processRouteUpdates(Collections.singletonList(routeUpdate));
+
+        // Verify
+        assertEquals(1, router.getRoutes6().size());
+        assertTrue(router.getRoutes6().contains(routeEntry));
+        verify(fibListener);
+    }
 }
index d8d8f45..eaabed3 100644 (file)
  */
 package org.onosproject.sdnip;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
-import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.IPCriterion;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.intent.Constraint;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.net.intent.constraint.PartialFailureConstraint;
-import org.onosproject.routing.FibListener;
-import org.onosproject.routing.FibUpdate;
-import org.onosproject.routing.IntentRequestListener;
-import org.onosproject.routing.config.RoutingConfigurationService;
+import org.onosproject.routing.IntentSynchronizationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Semaphore;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
 
 /**
  * Synchronizes intents between the in-memory intent store and the
  * IntentService.
  */
-public class IntentSynchronizer implements FibListener, IntentRequestListener {
-    private static final int PRIORITY_OFFSET = 100;
-    private static final int PRIORITY_MULTIPLIER = 5;
-    protected static final ImmutableList<Constraint> CONSTRAINTS
-            = ImmutableList.of(new PartialFailureConstraint());
+public class IntentSynchronizer implements IntentSynchronizationService {
 
     private static final Logger log =
         LoggerFactory.getLogger(IntentSynchronizer.class);
 
     private final ApplicationId appId;
     private final IntentService intentService;
-    private final HostService hostService;
-    private final InterfaceService interfaceService;
-    private final Map<IntentKey, PointToPointIntent> peerIntents;
-    private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+
+    private final Map<Key, Intent> intents;
 
     //
     // State to deal with SDN-IP Leader election and pushing Intents
     //
     private final ExecutorService bgpIntentsSynchronizerExecutor;
-    private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
     private volatile boolean isElectedLeader = false;
     private volatile boolean isActivatedLeader = false;
 
-    private final RoutingConfigurationService configService;
+    /**
+     * Class constructor.
+     *
+     * @param appId the Application ID
+     * @param intentService the intent service
+     */
+    IntentSynchronizer(ApplicationId appId, IntentService intentService) {
+        this(appId, intentService,
+                newSingleThreadExecutor(groupedThreads("onos/sdnip", "sync")));
+    }
 
     /**
      * Class constructor.
      *
      * @param appId the Application ID
      * @param intentService the intent service
-     * @param hostService the host service
-     * @param configService the SDN-IP configuration service
-     * @param interfaceService the interface service
+     * @param executorService executor service for synchronization thread
      */
     IntentSynchronizer(ApplicationId appId, IntentService intentService,
-                       HostService hostService,
-                       RoutingConfigurationService configService,
-                       InterfaceService interfaceService) {
+                       ExecutorService executorService) {
         this.appId = appId;
         this.intentService = intentService;
-        this.hostService = hostService;
-        this.interfaceService = interfaceService;
-        peerIntents = new ConcurrentHashMap<>();
-        routeIntents = new ConcurrentHashMap<>();
 
-        this.configService = configService;
+        intents = new ConcurrentHashMap<>();
 
-        bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
-                new ThreadFactoryBuilder()
-                .setNameFormat("sdnip-intents-synchronizer-%d").build());
+        bgpIntentsSynchronizerExecutor = executorService;
     }
 
     /**
      * Starts the synchronizer.
      */
     public void start() {
-        bgpIntentsSynchronizerExecutor.execute(this::doIntentSynchronizationThread);
+
     }
 
     /**
@@ -187,794 +148,118 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener {
         }
     }
 
-    /**
-     * Signals the synchronizer that the SDN-IP leadership has changed.
-     *
-     * @param isLeader true if this instance is now the leader, otherwise false
-     */
-    public void leaderChanged(boolean isLeader) {
-        log.debug("SDN-IP Leader changed: {}", isLeader);
-
-        if (!isLeader) {
-            this.isElectedLeader = false;
-            this.isActivatedLeader = false;
-            return;                     // Nothing to do
-        }
-        this.isActivatedLeader = false;
-        this.isElectedLeader = true;
-
-        //
-        // Tell the Intents Synchronizer thread to start the synchronization
-        //
-        intentsSynchronizerSemaphore.release();
-    }
-
-    /**
-     * Gets the route intents.
-     *
-     * @return the route intents
-     */
-    public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
-        List<MultiPointToSinglePointIntent> result = new LinkedList<>();
-
-        for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
-            routeIntents.entrySet()) {
-            result.add(entry.getValue());
-        }
-        return result;
-    }
-
-    /**
-     * Thread for Intent Synchronization.
-     */
-    private void doIntentSynchronizationThread() {
-        boolean interrupted = false;
-        try {
-            while (!interrupted) {
-                try {
-                    intentsSynchronizerSemaphore.acquire();
-                    //
-                    // Drain all permits, because a single synchronization is
-                    // sufficient.
-                    //
-                    intentsSynchronizerSemaphore.drainPermits();
-                } catch (InterruptedException e) {
-                    interrupted = true;
-                    break;
-                }
-                synchronizeIntents();
-            }
-        } finally {
-            if (interrupted) {
-                Thread.currentThread().interrupt();
-            }
-        }
-    }
-
-    /**
-     * Submits a collection of point-to-point intents.
-     *
-     * @param intents the intents to submit
-     */
-    void submitPeerIntents(Collection<PointToPointIntent> intents) {
+    @Override
+    public void submit(Intent intent) {
         synchronized (this) {
-            // Store the intents in memory
-            for (PointToPointIntent intent : intents) {
-                peerIntents.put(new IntentKey(intent), intent);
-            }
-
-            // Push the intents
+            intents.put(intent.key(), intent);
             if (isElectedLeader && isActivatedLeader) {
-                log.debug("SDN-IP Submitting all Peer Intents...");
-                for (Intent intent : intents) {
-                    log.trace("SDN-IP Submitting intents: {}", intent);
-                    intentService.submit(intent);
-                }
+                log.trace("SDN-IP Submitting intent: {}", intent);
+                intentService.submit(intent);
             }
         }
     }
 
-    /**
-     * Submits a MultiPointToSinglePointIntent for reactive routing.
-     *
-     * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent
-     * @param intent the intent to submit
-     */
-    void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
+    @Override
+    public void withdraw(Intent intent) {
         synchronized (this) {
-            // Store the intent in memory
-            routeIntents.put(ipPrefix, intent);
-
-            // Push the intent
+            intents.remove(intent.key(), intent);
             if (isElectedLeader && isActivatedLeader) {
-                log.trace("SDN-IP submitting reactive routing intent: {}", intent);
-                intentService.submit(intent);
+                log.trace("SDN-IP Withdrawing intent: {}", intent);
+                intentService.withdraw(intent);
             }
         }
     }
 
     /**
-     * Generates a route intent for a prefix, the next hop IP address, and
-     * the next hop MAC address.
-     * <p/>
-     * This method will find the egress interface for the intent.
-     * Intent will match dst IP prefix and rewrite dst MAC address at all other
-     * border switches, then forward packets according to dst MAC address.
+     * Signals the synchronizer that the SDN-IP leadership has changed.
      *
-     * @param prefix            IP prefix of the route to add
-     * @param nextHopIpAddress  IP address of the next hop
-     * @param nextHopMacAddress MAC address of the next hop
-     * @return the generated intent, or null if no intent should be submitted
+     * @param isLeader true if this instance is now the leader, otherwise false
      */
-    private MultiPointToSinglePointIntent generateRouteIntent(
-            IpPrefix prefix,
-            IpAddress nextHopIpAddress,
-            MacAddress nextHopMacAddress) {
-
-        // Find the attachment point (egress interface) of the next hop
-        Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
-        if (egressInterface == null) {
-            log.warn("No outgoing interface found for {}",
-                     nextHopIpAddress);
-            return null;
-        }
-
-        //
-        // Generate the intent itself
-        //
-        Set<ConnectPoint> ingressPorts = new HashSet<>();
-        ConnectPoint egressPort = egressInterface.connectPoint();
-        log.debug("Generating intent for prefix {}, next hop mac {}",
-                  prefix, nextHopMacAddress);
-
-        for (Interface intf : interfaceService.getInterfaces()) {
-            // TODO this should be only peering interfaces
-            if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
-                ConnectPoint srcPort = intf.connectPoint();
-                ingressPorts.add(srcPort);
-            }
-        }
-
-        // Match the destination IP prefix at the first hop
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-        if (prefix.isIp4()) {
-            selector.matchEthType(Ethernet.TYPE_IPV4);
-            selector.matchIPDst(prefix);
-        } else {
-            selector.matchEthType(Ethernet.TYPE_IPV6);
-            selector.matchIPv6Dst(prefix);
-        }
-
-        // Rewrite the destination MAC address
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
-                .setEthDst(nextHopMacAddress);
-        if (!egressInterface.vlan().equals(VlanId.NONE)) {
-            treatment.setVlanId(egressInterface.vlan());
-            // If we set VLAN ID, we have to make sure a VLAN tag exists.
-            // TODO support no VLAN -> VLAN routing
-            selector.matchVlanId(VlanId.ANY);
-        }
-
-        int priority =
-            prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
-        Key key = Key.of(prefix.toString(), appId);
-        return MultiPointToSinglePointIntent.builder()
-                .appId(appId)
-                .key(key)
-                .selector(selector.build())
-                .treatment(treatment.build())
-                .ingressPoints(ingressPorts)
-                .egressPoint(egressPort)
-                .priority(priority)
-                .constraints(CONSTRAINTS)
-                .build();
-    }
-
-    @Override
-    public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
-        checkNotNull(hostIpAddress);
-        Set<ConnectPoint> ingressPoints =
-                configService.getBgpPeerConnectPoints();
-
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-
-        if (hostIpAddress.isIp4()) {
-            selector.matchEthType(Ethernet.TYPE_IPV4);
-        } else {
-            selector.matchEthType(Ethernet.TYPE_IPV6);
-        }
+    public void leaderChanged(boolean isLeader) {
+        log.debug("SDN-IP Leader changed: {}", isLeader);
 
-        // Match the destination IP prefix at the first hop
-        IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
-        selector.matchIPDst(ipPrefix);
-
-        // Rewrite the destination MAC address
-        MacAddress hostMac = null;
-        ConnectPoint egressPoint = null;
-        for (Host host : hostService.getHostsByIp(hostIpAddress)) {
-            if (host.mac() != null) {
-                hostMac = host.mac();
-                egressPoint = host.location();
-                break;
-            }
-        }
-        if (hostMac == null) {
-            hostService.startMonitoringIp(hostIpAddress);
-            return;
+        if (!isLeader) {
+            this.isElectedLeader = false;
+            this.isActivatedLeader = false;
+            return;                     // Nothing to do
         }
+        this.isActivatedLeader = false;
+        this.isElectedLeader = true;
 
-        TrafficTreatment.Builder treatment =
-                DefaultTrafficTreatment.builder().setEthDst(hostMac);
-        Key key = Key.of(ipPrefix.toString(), appId);
-        int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
-                + PRIORITY_OFFSET;
-        MultiPointToSinglePointIntent intent =
-                MultiPointToSinglePointIntent.builder()
-                .appId(appId)
-                .key(key)
-                .selector(selector.build())
-                .treatment(treatment.build())
-                .ingressPoints(ingressPoints)
-                .egressPoint(egressPoint)
-                .priority(priority)
-                .constraints(CONSTRAINTS)
-                .build();
-
-        log.trace("Generates ConnectivityInternetToHost intent {}", intent);
-        submitReactiveIntent(ipPrefix, intent);
-    }
-
-
-    @Override
-    public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
-        //
-        // NOTE: Semantically, we MUST withdraw existing intents before
-        // submitting new intents.
-        //
-        synchronized (this) {
-            MultiPointToSinglePointIntent intent;
-
-            log.debug("SDN-IP submitting intents = {} withdrawing = {}",
-                     updates.size(), withdraws.size());
-
-            //
-            // Prepare the Intent batch operations for the intents to withdraw
-            //
-            for (FibUpdate withdraw : withdraws) {
-                checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
-                              "FibUpdate with wrong type in withdraws list");
-
-                IpPrefix prefix = withdraw.entry().prefix();
-                intent = routeIntents.remove(prefix);
-                if (intent == null) {
-                    log.trace("SDN-IP No intent in routeIntents to delete " +
-                              "for prefix: {}", prefix);
-                    continue;
-                }
-                if (isElectedLeader && isActivatedLeader) {
-                    log.trace("SDN-IP Withdrawing intent: {}", intent);
-                    intentService.withdraw(intent);
-                }
-            }
-
-            //
-            // Prepare the Intent batch operations for the intents to submit
-            //
-            for (FibUpdate update : updates) {
-                checkArgument(update.type() == FibUpdate.Type.UPDATE,
-                              "FibUpdate with wrong type in updates list");
-
-                IpPrefix prefix = update.entry().prefix();
-                intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
-                                             update.entry().nextHopMac());
-
-                if (intent == null) {
-                    // This preserves the old semantics - if an intent can't be
-                    // generated, we don't do anything with that prefix. But
-                    // perhaps we should withdraw the old intent anyway?
-                    continue;
-                }
-
-                MultiPointToSinglePointIntent oldIntent =
-                    routeIntents.put(prefix, intent);
-                if (isElectedLeader && isActivatedLeader) {
-                    if (oldIntent != null) {
-                        log.trace("SDN-IP Withdrawing old intent: {}",
-                                  oldIntent);
-                        intentService.withdraw(oldIntent);
-                    }
-                    log.trace("SDN-IP Submitting intent: {}", intent);
-                    intentService.submit(intent);
-                }
-            }
-        }
+        // Run the synchronization method off-thread
+        bgpIntentsSynchronizerExecutor.execute(this::synchronizeIntents);
     }
 
-    /**
-     * Synchronize the in-memory Intents with the Intents in the Intent
-     * framework.
-     */
-    void synchronizeIntents() {
-        synchronized (this) {
-
-            Map<IntentKey, Intent> localIntents = new HashMap<>();
-            Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
-            Collection<Intent> storeInMemoryIntents = new LinkedList<>();
-            Collection<Intent> addIntents = new LinkedList<>();
-            Collection<Intent> deleteIntents = new LinkedList<>();
-
-            if (!isElectedLeader) {
-                return;         // Nothing to do: not the leader anymore
-            }
-            log.debug("SDN-IP synchronizing all intents...");
-
-            // Prepare the local intents
-            for (Intent intent : routeIntents.values()) {
-                localIntents.put(new IntentKey(intent), intent);
-            }
-            for (Intent intent : peerIntents.values()) {
-                localIntents.put(new IntentKey(intent), intent);
+    private void synchronizeIntents() {
+        Map<Key, Intent> serviceIntents = new HashMap<>();
+        intentService.getIntents().forEach(i -> {
+            if (i.appId().equals(appId)) {
+                serviceIntents.put(i.key(), i);
             }
+        });
 
-            // Fetch all intents for this application
-            for (Intent intent : intentService.getIntents()) {
-                if (!intent.appId().equals(appId)) {
-                    continue;
-                }
-                fetchedIntents.put(new IntentKey(intent), intent);
-            }
-            if (log.isDebugEnabled()) {
-                for (Intent intent: fetchedIntents.values()) {
-                    log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
-                              intent);
-                }
-            }
-
-            computeIntentsDelta(localIntents, fetchedIntents,
-                                storeInMemoryIntents, addIntents,
-                                deleteIntents);
+        List<Intent> intentsToAdd = new LinkedList<>();
+        List<Intent> intentsToRemove = new LinkedList<>();
 
-            //
-            // Perform the actions:
-            // 1. Store in memory fetched intents that are same. Can be done
-            //    even if we are not the leader anymore
-            // 2. Delete intents: check if the leader before the operation
-            // 3. Add intents: check if the leader before the operation
-            //
-            for (Intent intent : storeInMemoryIntents) {
-                // Store the intent in memory based on its type
-                if (intent instanceof MultiPointToSinglePointIntent) {
-                    MultiPointToSinglePointIntent mp2pIntent =
-                        (MultiPointToSinglePointIntent) intent;
-                    // Find the IP prefix
-                    Criterion c =
-                        mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
-                    if (c == null) {
-                        // Try IPv6
-                        c =
-                            mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
-                    }
-                    if (c != null && c instanceof IPCriterion) {
-                        IPCriterion ipCriterion = (IPCriterion) c;
-                        IpPrefix ipPrefix = ipCriterion.ip();
-                        if (ipPrefix == null) {
-                            continue;
-                        }
-                        log.trace("SDN-IP Intent Synchronizer: updating " +
-                                  "in-memory Route Intent for prefix {}",
-                                  ipPrefix);
-                        routeIntents.put(ipPrefix, mp2pIntent);
-                    } else {
-                        log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
-                                 mp2pIntent.id());
-                    }
-                    continue;
-                }
-                if (intent instanceof PointToPointIntent) {
-                    PointToPointIntent p2pIntent = (PointToPointIntent) intent;
-                    log.trace("SDN-IP Intent Synchronizer: updating " +
-                              "in-memory Peer Intent {}", p2pIntent);
-                    peerIntents.put(new IntentKey(intent), p2pIntent);
-                    continue;
-                }
-            }
-
-            // Withdraw Intents
-            for (Intent intent : deleteIntents) {
-                intentService.withdraw(intent);
-                log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
-                      intent);
-            }
-            if (!isElectedLeader) {
-                log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
-                          "not elected leader anymore");
-                isActivatedLeader = false;
-                return;
-            }
-
-            // Add Intents
-            for (Intent intent : addIntents) {
-                intentService.submit(intent);
-                log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
-                          intent);
-            }
-            if (!isElectedLeader) {
-                log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
-                          "not elected leader anymore");
-                isActivatedLeader = false;
-                return;
-            }
-
-            if (isElectedLeader) {
-                isActivatedLeader = true;       // Allow push of Intents
+        for (Intent localIntent : intents.values()) {
+            Intent serviceIntent = serviceIntents.remove(localIntent.key());
+            if (serviceIntent == null) {
+                intentsToAdd.add(localIntent);
             } else {
-                isActivatedLeader = false;
-            }
-            log.debug("SDN-IP intent synchronization completed");
-        }
-    }
-
-    /**
-     * Computes the delta in two sets of Intents: local in-memory Intents,
-     * and intents fetched from the Intent framework.
-     *
-     * @param localIntents the local in-memory Intents
-     * @param fetchedIntents the Intents fetched from the Intent framework
-     * @param storeInMemoryIntents the Intents that should be stored in memory.
-     * Note: This Collection must be allocated by the caller, and it will
-     * be populated by this method.
-     * @param addIntents the Intents that should be added to the Intent
-     * framework. Note: This Collection must be allocated by the caller, and
-     * it will be populated by this method.
-     * @param deleteIntents the Intents that should be deleted from the Intent
-     * framework. Note: This Collection must be allocated by the caller, and
-     * it will be populated by this method.
-     */
-    private void computeIntentsDelta(
-                                final Map<IntentKey, Intent> localIntents,
-                                final Map<IntentKey, Intent> fetchedIntents,
-                                Collection<Intent> storeInMemoryIntents,
-                                Collection<Intent> addIntents,
-                                Collection<Intent> deleteIntents) {
-
-        //
-        // Compute the deltas between the LOCAL in-memory Intents and the
-        // FETCHED Intents:
-        //  - If an Intent is in both the LOCAL and FETCHED sets:
-        //    If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
-        //    the LOCAL Intent should be added/installed; otherwise the
-        //    FETCHED intent should be stored in the local memory
-        //    (i.e., override the LOCAL Intent) to preserve the original
-        //    Intent ID.
-        //  - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
-        //    Intent should be added/installed.
-        //  - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
-        //    Intent should be deleted/withdrawn.
-        //
-        for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
-            IntentKey intentKey = entry.getKey();
-            Intent localIntent = entry.getValue();
-            Intent fetchedIntent = fetchedIntents.get(intentKey);
-
-            if (fetchedIntent == null) {
-                //
-                // No FETCHED Intent found: push the LOCAL Intent.
-                //
-                addIntents.add(localIntent);
-                continue;
-            }
-
-            IntentState state =
-                intentService.getIntentState(fetchedIntent.key());
-            if (state == null ||
-                state == IntentState.WITHDRAWING ||
-                state == IntentState.WITHDRAWN) {
-                // The intent has been withdrawn but according to our route
-                // table it should be installed. We'll reinstall it.
-                addIntents.add(localIntent);
-                continue;
+                IntentState state = intentService.getIntentState(serviceIntent.key());
+                if (!IntentUtils.equals(serviceIntent, localIntent) || state == null ||
+                        state == IntentState.WITHDRAW_REQ ||
+                        state == IntentState.WITHDRAWING ||
+                        state == IntentState.WITHDRAWN) {
+                    intentsToAdd.add(localIntent);
+                }
             }
-            storeInMemoryIntents.add(fetchedIntent);
         }
 
-        for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
-            IntentKey intentKey = entry.getKey();
-            Intent fetchedIntent = entry.getValue();
-            Intent localIntent = localIntents.get(intentKey);
-
-            if (localIntent != null) {
-                continue;
+        for (Intent serviceIntent : serviceIntents.values()) {
+            IntentState state = intentService.getIntentState(serviceIntent.key());
+            if (state != null && state != IntentState.WITHDRAW_REQ
+                    && state != IntentState.WITHDRAWING
+                    && state != IntentState.WITHDRAWN) {
+                intentsToRemove.add(serviceIntent);
             }
-
-            IntentState state =
-                intentService.getIntentState(fetchedIntent.key());
-            if (state == null ||
-                state == IntentState.WITHDRAWING ||
-                state == IntentState.WITHDRAWN) {
-                // Nothing to do. The intent has been already withdrawn.
-                continue;
-            }
-            //
-            // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
-            //
-            deleteIntents.add(fetchedIntent);
-        }
-    }
-
-    /**
-     * Helper class that can be used to compute the key for an Intent by
-     * by excluding the Intent ID.
-     */
-    static final class IntentKey {
-        private final Intent intent;
-
-        /**
-         * Constructor.
-         *
-         * @param intent the intent to use
-         */
-        IntentKey(Intent intent) {
-            checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
-                          (intent instanceof PointToPointIntent),
-                          "Intent type not recognized", intent);
-            this.intent = intent;
         }
 
-        /**
-         * Compares two Multi-Point to Single-Point Intents whether they
-         * represent same logical intention.
-         *
-         * @param intent1 the first Intent to compare
-         * @param intent2 the second Intent to compare
-         * @return true if both Intents represent same logical intention,
-         * otherwise false
-         */
-        static boolean equalIntents(MultiPointToSinglePointIntent intent1,
-                                    MultiPointToSinglePointIntent intent2) {
-            return Objects.equals(intent1.appId(), intent2.appId()) &&
-                Objects.equals(intent1.selector(), intent2.selector()) &&
-                Objects.equals(intent1.treatment(), intent2.treatment()) &&
-                Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
-                Objects.equals(intent1.egressPoint(), intent2.egressPoint());
-        }
+        log.debug("SDN-IP Intent Synchronizer: submitting {}, withdrawing {}",
+                intentsToAdd.size(), intentsToRemove.size());
 
-        /**
-         * Compares two Point-to-Point Intents whether they represent
-         * same logical intention.
-         *
-         * @param intent1 the first Intent to compare
-         * @param intent2 the second Intent to compare
-         * @return true if both Intents represent same logical intention,
-         * otherwise false
-         */
-        static boolean equalIntents(PointToPointIntent intent1,
-                                    PointToPointIntent intent2) {
-            return Objects.equals(intent1.appId(), intent2.appId()) &&
-                Objects.equals(intent1.selector(), intent2.selector()) &&
-                Objects.equals(intent1.treatment(), intent2.treatment()) &&
-                Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
-                Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+        // Withdraw Intents
+        for (Intent intent : intentsToRemove) {
+            intentService.withdraw(intent);
+            log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
+                    intent);
         }
-
-        @Override
-        public int hashCode() {
-            if (intent instanceof PointToPointIntent) {
-                PointToPointIntent p2pIntent = (PointToPointIntent) intent;
-                return Objects.hash(p2pIntent.appId(),
-                                    p2pIntent.resources(),
-                                    p2pIntent.selector(),
-                                    p2pIntent.treatment(),
-                                    p2pIntent.constraints(),
-                                    p2pIntent.ingressPoint(),
-                                    p2pIntent.egressPoint());
-            }
-            if (intent instanceof MultiPointToSinglePointIntent) {
-                MultiPointToSinglePointIntent m2pIntent =
-                    (MultiPointToSinglePointIntent) intent;
-                return Objects.hash(m2pIntent.appId(),
-                                    m2pIntent.resources(),
-                                    m2pIntent.selector(),
-                                    m2pIntent.treatment(),
-                                    m2pIntent.constraints(),
-                                    m2pIntent.ingressPoints(),
-                                    m2pIntent.egressPoint());
-            }
-            checkArgument(false, "Intent type not recognized", intent);
-            return 0;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if ((obj == null) || (!(obj instanceof IntentKey))) {
-                return false;
-            }
-            IntentKey other = (IntentKey) obj;
-
-            if (this.intent instanceof PointToPointIntent) {
-                if (!(other.intent instanceof PointToPointIntent)) {
-                    return false;
-                }
-                return equalIntents((PointToPointIntent) this.intent,
-                                    (PointToPointIntent) other.intent);
-            }
-            if (this.intent instanceof MultiPointToSinglePointIntent) {
-                if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
-                    return false;
-                }
-                return equalIntents(
-                                (MultiPointToSinglePointIntent) this.intent,
-                                (MultiPointToSinglePointIntent) other.intent);
-            }
-            checkArgument(false, "Intent type not recognized", intent);
-            return false;
+        if (!isElectedLeader) {
+            log.debug("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
+                    "not elected leader anymore");
+            isActivatedLeader = false;
+            return;
         }
-    }
 
-    @Override
-    public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
-                                            IpAddress srcIpAddress,
-                                            MacAddress srcMacAddress,
-                                            ConnectPoint srcConnectPoint) {
-        checkNotNull(dstIpAddress);
-        checkNotNull(srcIpAddress);
-        checkNotNull(srcMacAddress);
-        checkNotNull(srcConnectPoint);
-
-        IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
-        IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
-        ConnectPoint dstConnectPoint = null;
-        MacAddress dstMacAddress = null;
-
-        for (Host host : hostService.getHostsByIp(dstIpAddress)) {
-            if (host.mac() != null) {
-                dstMacAddress = host.mac();
-                dstConnectPoint = host.location();
-                break;
-            }
+        // Add Intents
+        for (Intent intent : intentsToAdd) {
+            intentService.submit(intent);
+            log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
+                    intent);
         }
-        if (dstMacAddress == null) {
-            hostService.startMonitoringIp(dstIpAddress);
+        if (!isElectedLeader) {
+            log.debug("SDN-IP Intent Synchronizer: cannot submit intents: " +
+                    "not elected leader anymore");
+            isActivatedLeader = false;
             return;
         }
 
-        //
-        // Handle intent from source host to destination host
-        //
-        MultiPointToSinglePointIntent srcToDstIntent =
-                hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
-                                    dstMacAddress, srcConnectPoint);
-        submitReactiveIntent(dstIpPrefix, srcToDstIntent);
-
-        //
-        // Handle intent from destination host to source host
-        //
-
-        // Since we proactively handle the intent from destination host to
-        // source host, we should check whether there is an exiting intent
-        // first.
-        if (mp2pIntentExists(srcIpPrefix)) {
-            updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
-            return;
+        if (isElectedLeader) {
+            isActivatedLeader = true;       // Allow push of Intents
         } else {
-            // There is no existing intent, create a new one.
-            MultiPointToSinglePointIntent dstToSrcIntent =
-                    hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
-                                        srcMacAddress, dstConnectPoint);
-            submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
+            isActivatedLeader = false;
         }
+        log.debug("SDN-IP intent synchronization completed");
     }
 
-    /**
-     * Generates MultiPointToSinglePointIntent for both source host and
-     * destination host located in local SDN network.
-     *
-     * @param dstIpAddress the destination IP address
-     * @param dstConnectPoint the destination host connect point
-     * @param dstMacAddress the MAC address of destination host
-     * @param srcConnectPoint the connect point where packet-in from
-     * @return the generated MultiPointToSinglePointIntent
-     */
-    private MultiPointToSinglePointIntent hostToHostIntentGenerator(
-                                       IpAddress dstIpAddress,
-                                       ConnectPoint dstConnectPoint,
-                                       MacAddress dstMacAddress,
-                                       ConnectPoint srcConnectPoint) {
-        checkNotNull(dstIpAddress);
-        checkNotNull(dstConnectPoint);
-        checkNotNull(dstMacAddress);
-        checkNotNull(srcConnectPoint);
-
-        Set<ConnectPoint> ingressPoints = new HashSet<>();
-        ingressPoints.add(srcConnectPoint);
-        IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
-
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-        if (dstIpAddress.isIp4()) {
-            selector.matchEthType(Ethernet.TYPE_IPV4);
-            selector.matchIPDst(dstIpPrefix);
-        } else {
-            selector.matchEthType(Ethernet.TYPE_IPV6);
-            selector.matchIPv6Dst(dstIpPrefix);
-        }
-
-        // Rewrite the destination MAC address
-        TrafficTreatment.Builder treatment =
-                DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
-
-        Key key = Key.of(dstIpPrefix.toString(), appId);
-        int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
-                + PRIORITY_OFFSET;
-        MultiPointToSinglePointIntent intent =
-                MultiPointToSinglePointIntent.builder()
-                .appId(appId)
-                .key(key)
-                .selector(selector.build())
-                .treatment(treatment.build())
-                .ingressPoints(ingressPoints)
-                .egressPoint(dstConnectPoint)
-                .priority(priority)
-                .constraints(CONSTRAINTS)
-                .build();
-
-        log.trace("Generates ConnectivityHostToHost = {} ", intent);
-        return intent;
-    }
-
-    @Override
-    public void updateExistingMp2pIntent(IpPrefix ipPrefix,
-                                         ConnectPoint ingressConnectPoint) {
-        checkNotNull(ipPrefix);
-        checkNotNull(ingressConnectPoint);
-
-        MultiPointToSinglePointIntent existingIntent =
-                getExistingMp2pIntent(ipPrefix);
-        if (existingIntent != null) {
-            Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
-            // Add host connect point into ingressPoints of the existing intent
-            if (ingressPoints.add(ingressConnectPoint)) {
-                MultiPointToSinglePointIntent updatedMp2pIntent =
-                        MultiPointToSinglePointIntent.builder()
-                        .appId(appId)
-                        .key(existingIntent.key())
-                        .selector(existingIntent.selector())
-                        .treatment(existingIntent.treatment())
-                        .ingressPoints(ingressPoints)
-                        .egressPoint(existingIntent.egressPoint())
-                        .priority(existingIntent.priority())
-                        .constraints(CONSTRAINTS)
-                        .build();
-
-                log.trace("Update an existing MultiPointToSinglePointIntent "
-                        + "to new intent = {} ", updatedMp2pIntent);
-                submitReactiveIntent(ipPrefix, updatedMp2pIntent);
-            }
-            // If adding ingressConnectPoint to ingressPoints failed, it
-            // because between the time interval from checking existing intent
-            // to generating new intent, onos updated this intent due to other
-            // packet-in and the new intent also includes the
-            // ingressConnectPoint. This will not affect reactive routing.
-        }
-    }
-
-    @Override
-    public boolean mp2pIntentExists(IpPrefix ipPrefix) {
-        checkNotNull(ipPrefix);
-        return routeIntents.get(ipPrefix) != null;
-    }
-
-    /**
-     * Gets the existing MultiPointToSinglePointIntent from memory for a given
-     * IP prefix.
-     *
-     * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
-     * @return the MultiPointToSinglePointIntent if found, otherwise null
-     */
-    private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
-                                                                ipPrefix) {
-        checkNotNull(ipPrefix);
-        return routeIntents.get(ipPrefix);
-    }
 }
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java
new file mode 100644 (file)
index 0000000..8e2a3df
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Utilities for dealing with intents.
+ */
+public final class IntentUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(IntentUtils.class);
+
+    private IntentUtils() {
+
+    }
+
+    /**
+     * Checks if two intents represent the same value.
+     *
+     * <p>({@link Intent#equals(Object)} only checks ID equality)</p>
+     *
+     * <p>Both intents must be of the same type.</p>
+     *
+     * @param one first intent
+     * @param two second intent
+     * @return true if the two intents represent the same value, otherwise false
+     */
+    public static boolean equals(Intent one, Intent two) {
+        checkArgument(one.getClass() == two.getClass(),
+                "Intents are not the same type");
+
+        if (!(Objects.equals(one.appId(), two.appId()) &&
+                Objects.equals(one.key(), two.key()))) {
+            return false;
+        }
+
+        if (one instanceof MultiPointToSinglePointIntent) {
+            MultiPointToSinglePointIntent intent1 = (MultiPointToSinglePointIntent) one;
+            MultiPointToSinglePointIntent intent2 = (MultiPointToSinglePointIntent) two;
+
+            return Objects.equals(intent1.selector(), intent2.selector()) &&
+                    Objects.equals(intent1.treatment(), intent2.treatment()) &&
+                    Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
+                    Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+        } else if (one instanceof PointToPointIntent) {
+            PointToPointIntent intent1 = (PointToPointIntent) one;
+            PointToPointIntent intent2 = (PointToPointIntent) two;
+
+            return Objects.equals(intent1.selector(), intent2.selector()) &&
+                    Objects.equals(intent1.treatment(), intent2.treatment()) &&
+                    Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
+                    Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+        } else {
+            log.error("Unimplemented intent type");
+            return false;
+        }
+    }
+}
index 459db2b..b2ce0f8 100644 (file)
@@ -15,6 +15,8 @@
  */
 package org.onosproject.sdnip;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IPv6;
@@ -22,16 +24,18 @@ import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.TpPort;
 import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
 import org.onosproject.routing.RoutingService;
 import org.onosproject.routing.config.BgpConfig;
 import org.slf4j.Logger;
@@ -49,18 +53,26 @@ import static com.google.common.base.Preconditions.checkNotNull;
 public class PeerConnectivityManager {
     private static final int PRIORITY_OFFSET = 1000;
 
+    private static final String SUFFIX_DST = "dst";
+    private static final String SUFFIX_SRC = "src";
+    private static final String SUFFIX_ICMP = "icmp";
+
     private static final Logger log = LoggerFactory.getLogger(
             PeerConnectivityManager.class);
 
     private static final short BGP_PORT = 179;
 
-    private final IntentSynchronizer intentSynchronizer;
+    private final IntentSynchronizationService intentSynchronizer;
     private final NetworkConfigService configService;
     private final InterfaceService interfaceService;
 
     private final ApplicationId appId;
     private final ApplicationId routerAppId;
 
+    // Just putting something random here for now. Figure out exactly what
+    // indexes we need when we start making use of them.
+    private final Multimap<BgpConfig.BgpSpeakerConfig, PointToPointIntent> peerIntents;
+
     /**
      * Creates a new PeerConnectivityManager.
      *
@@ -71,7 +83,7 @@ public class PeerConnectivityManager {
      * @param routerAppId        application ID
      */
     public PeerConnectivityManager(ApplicationId appId,
-                                   IntentSynchronizer intentSynchronizer,
+                                   IntentSynchronizationService intentSynchronizer,
                                    NetworkConfigService configService,
                                    ApplicationId routerAppId,
                                    InterfaceService interfaceService) {
@@ -80,6 +92,8 @@ public class PeerConnectivityManager {
         this.configService = configService;
         this.routerAppId = routerAppId;
         this.interfaceService = interfaceService;
+
+        peerIntents = HashMultimap.create();
     }
 
     /**
@@ -100,8 +114,6 @@ public class PeerConnectivityManager {
      * BGP speakers and external BGP peers.
      */
     private void setUpConnectivity() {
-        List<PointToPointIntent> intents = new ArrayList<>();
-
         BgpConfig config = configService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
 
         if (config == null) {
@@ -113,11 +125,12 @@ public class PeerConnectivityManager {
             log.debug("Start to set up BGP paths for BGP speaker: {}",
                     bgpSpeaker);
 
-            intents.addAll(buildSpeakerIntents(bgpSpeaker));
-        }
+            buildSpeakerIntents(bgpSpeaker).forEach(i -> {
+                peerIntents.put(bgpSpeaker, i);
+                intentSynchronizer.submit(i);
+            });
 
-        // Submit all the intents.
-        intentSynchronizer.submitPeerIntents(intents);
+        }
     }
 
     private Collection<PointToPointIntent> buildSpeakerIntents(BgpConfig.BgpSpeakerConfig speaker) {
@@ -167,8 +180,8 @@ public class PeerConnectivityManager {
         List<PointToPointIntent> intents = new ArrayList<>();
 
         TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
-
         TrafficSelector selector;
+        Key key;
 
         byte tcpProtocol;
         byte icmpProtocol;
@@ -188,8 +201,11 @@ public class PeerConnectivityManager {
                 null,
                 BGP_PORT);
 
+        key = buildKey(ipOne, ipTwo, SUFFIX_DST);
+
         intents.add(PointToPointIntent.builder()
                 .appId(appId)
+                .key(key)
                 .selector(selector)
                 .treatment(treatment)
                 .ingressPoint(portOne)
@@ -204,8 +220,11 @@ public class PeerConnectivityManager {
                 BGP_PORT,
                 null);
 
+        key = buildKey(ipOne, ipTwo, SUFFIX_SRC);
+
         intents.add(PointToPointIntent.builder()
                 .appId(appId)
+                .key(key)
                 .selector(selector)
                 .treatment(treatment)
                 .ingressPoint(portOne)
@@ -220,8 +239,11 @@ public class PeerConnectivityManager {
                 null,
                 BGP_PORT);
 
+        key = buildKey(ipTwo, ipOne, SUFFIX_DST);
+
         intents.add(PointToPointIntent.builder()
                 .appId(appId)
+                .key(key)
                 .selector(selector)
                 .treatment(treatment)
                 .ingressPoint(portTwo)
@@ -236,8 +258,11 @@ public class PeerConnectivityManager {
                 BGP_PORT,
                 null);
 
+        key = buildKey(ipTwo, ipOne, SUFFIX_SRC);
+
         intents.add(PointToPointIntent.builder()
                 .appId(appId)
+                .key(key)
                 .selector(selector)
                 .treatment(treatment)
                 .ingressPoint(portTwo)
@@ -252,8 +277,11 @@ public class PeerConnectivityManager {
                 null,
                 null);
 
+        key = buildKey(ipOne, ipTwo, SUFFIX_ICMP);
+
         intents.add(PointToPointIntent.builder()
                 .appId(appId)
+                .key(key)
                 .selector(selector)
                 .treatment(treatment)
                 .ingressPoint(portOne)
@@ -268,8 +296,11 @@ public class PeerConnectivityManager {
                 null,
                 null);
 
+        key = buildKey(ipTwo, ipOne, SUFFIX_ICMP);
+
         intents.add(PointToPointIntent.builder()
                 .appId(appId)
+                .key(key)
                 .selector(selector)
                 .treatment(treatment)
                 .ingressPoint(portTwo)
@@ -316,4 +347,27 @@ public class PeerConnectivityManager {
         return builder.build();
     }
 
+    /**
+     * Builds an intent Key for a point-to-point intent based off the source
+     * and destination IP address, as well as a suffix String to distinguish
+     * between different types of intents between the same source and
+     * destination.
+     *
+     * @param srcIp source IP address
+     * @param dstIp destination IP address
+     * @param suffix suffix string
+     * @return
+     */
+    private Key buildKey(IpAddress srcIp, IpAddress dstIp, String suffix) {
+        String keyString = new StringBuilder()
+                .append(srcIp.toString())
+                .append("-")
+                .append(dstIp.toString())
+                .append("-")
+                .append(suffix)
+                .toString();
+
+        return Key.of(keyString, appId);
+    }
+
 }
index 3d1fe65..1b3eda9 100644 (file)
@@ -32,7 +32,9 @@ import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.intent.IntentService;
+import org.onosproject.routing.IntentSynchronizationService;
 import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.SdnIpService;
 import org.onosproject.routing.config.RoutingConfigurationService;
 import org.slf4j.Logger;
 
@@ -79,6 +81,7 @@ public class SdnIp implements SdnIpService {
 
     private IntentSynchronizer intentSynchronizer;
     private PeerConnectivityManager peerConnectivity;
+    private SdnIpFib fib;
 
     private LeadershipEventListener leadershipEventListener =
         new InnerLeadershipEventListener();
@@ -93,10 +96,7 @@ public class SdnIp implements SdnIpService {
 
         localControllerNode = clusterService.getLocalNode();
 
-        intentSynchronizer = new IntentSynchronizer(appId, intentService,
-                                                    hostService,
-                                                    config,
-                                                    interfaceService);
+        intentSynchronizer = new IntentSynchronizer(appId, intentService);
         intentSynchronizer.start();
 
         peerConnectivity = new PeerConnectivityManager(appId,
@@ -106,8 +106,9 @@ public class SdnIp implements SdnIpService {
                                                        interfaceService);
         peerConnectivity.start();
 
-        routingService.addFibListener(intentSynchronizer);
-        routingService.addIntentRequestListener(intentSynchronizer);
+        fib = new SdnIpFib(appId, interfaceService, intentSynchronizer);
+
+        routingService.addFibListener(fib);
         routingService.start();
 
         leadershipService.addListener(leadershipEventListener);
@@ -131,6 +132,11 @@ public class SdnIp implements SdnIpService {
         intentSynchronizer.leaderChanged(isPrimary);
     }
 
+    @Override
+    public IntentSynchronizationService getIntentSynchronizationService() {
+        return intentSynchronizer;
+    }
+
     /**
      * Converts DPIDs of the form xx:xx:xx:xx:xx:xx:xx to OpenFlow provider
      * device URIs.
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
new file mode 100644 (file)
index 0000000..c0001bd
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import com.google.common.collect.ImmutableList;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.routing.FibListener;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * FIB component of SDN-IP.
+ */
+public class SdnIpFib implements FibListener {
+    private Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final int PRIORITY_OFFSET = 100;
+    private static final int PRIORITY_MULTIPLIER = 5;
+    protected static final ImmutableList<Constraint> CONSTRAINTS
+            = ImmutableList.of(new PartialFailureConstraint());
+
+    private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+
+    private final ApplicationId appId;
+    private final InterfaceService interfaceService;
+    private final IntentSynchronizationService intentSynchronizer;
+
+    /**
+     * Class constructor.
+     *
+     * @param appId application ID to use when generating intents
+     * @param interfaceService interface service
+     * @param intentSynchronizer intent synchronizer
+     */
+    public SdnIpFib(ApplicationId appId, InterfaceService interfaceService,
+                    IntentSynchronizationService intentSynchronizer) {
+        routeIntents = new ConcurrentHashMap<>();
+
+        this.appId = appId;
+        this.interfaceService = interfaceService;
+        this.intentSynchronizer = intentSynchronizer;
+    }
+
+
+    @Override
+    public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
+        int submitCount = 0, withdrawCount = 0;
+        //
+        // NOTE: Semantically, we MUST withdraw existing intents before
+        // submitting new intents.
+        //
+        synchronized (this) {
+            MultiPointToSinglePointIntent intent;
+
+            //
+            // Prepare the Intent batch operations for the intents to withdraw
+            //
+            for (FibUpdate withdraw : withdraws) {
+                checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
+                        "FibUpdate with wrong type in withdraws list");
+
+                IpPrefix prefix = withdraw.entry().prefix();
+                intent = routeIntents.remove(prefix);
+                if (intent == null) {
+                    log.trace("SDN-IP No intent in routeIntents to delete " +
+                            "for prefix: {}", prefix);
+                    continue;
+                }
+                intentSynchronizer.withdraw(intent);
+                withdrawCount++;
+            }
+
+            //
+            // Prepare the Intent batch operations for the intents to submit
+            //
+            for (FibUpdate update : updates) {
+                checkArgument(update.type() == FibUpdate.Type.UPDATE,
+                        "FibUpdate with wrong type in updates list");
+
+                IpPrefix prefix = update.entry().prefix();
+                intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
+                        update.entry().nextHopMac());
+
+                if (intent == null) {
+                    // This preserves the old semantics - if an intent can't be
+                    // generated, we don't do anything with that prefix. But
+                    // perhaps we should withdraw the old intent anyway?
+                    continue;
+                }
+
+                routeIntents.put(prefix, intent);
+                intentSynchronizer.submit(intent);
+                submitCount++;
+            }
+
+            log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount,
+                    updates.size(), withdrawCount, withdraws.size());
+        }
+    }
+
+    /**
+     * Generates a route intent for a prefix, the next hop IP address, and
+     * the next hop MAC address.
+     * <p/>
+     * This method will find the egress interface for the intent.
+     * Intent will match dst IP prefix and rewrite dst MAC address at all other
+     * border switches, then forward packets according to dst MAC address.
+     *
+     * @param prefix            IP prefix of the route to add
+     * @param nextHopIpAddress  IP address of the next hop
+     * @param nextHopMacAddress MAC address of the next hop
+     * @return the generated intent, or null if no intent should be submitted
+     */
+    private MultiPointToSinglePointIntent generateRouteIntent(
+            IpPrefix prefix,
+            IpAddress nextHopIpAddress,
+            MacAddress nextHopMacAddress) {
+
+        // Find the attachment point (egress interface) of the next hop
+        Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
+        if (egressInterface == null) {
+            log.warn("No outgoing interface found for {}",
+                    nextHopIpAddress);
+            return null;
+        }
+
+        // Generate the intent itself
+        Set<ConnectPoint> ingressPorts = new HashSet<>();
+        ConnectPoint egressPort = egressInterface.connectPoint();
+        log.debug("Generating intent for prefix {}, next hop mac {}",
+                prefix, nextHopMacAddress);
+
+        for (Interface intf : interfaceService.getInterfaces()) {
+            // TODO this should be only peering interfaces
+            if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
+                ConnectPoint srcPort = intf.connectPoint();
+                ingressPorts.add(srcPort);
+            }
+        }
+
+        // Match the destination IP prefix at the first hop
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        if (prefix.isIp4()) {
+            selector.matchEthType(Ethernet.TYPE_IPV4);
+            selector.matchIPDst(prefix);
+        } else {
+            selector.matchEthType(Ethernet.TYPE_IPV6);
+            selector.matchIPv6Dst(prefix);
+        }
+
+        // Rewrite the destination MAC address
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+                .setEthDst(nextHopMacAddress);
+        if (!egressInterface.vlan().equals(VlanId.NONE)) {
+            treatment.setVlanId(egressInterface.vlan());
+            // If we set VLAN ID, we have to make sure a VLAN tag exists.
+            // TODO support no VLAN -> VLAN routing
+            selector.matchVlanId(VlanId.ANY);
+        }
+
+        int priority =
+                prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
+        Key key = Key.of(prefix.toString(), appId);
+        return MultiPointToSinglePointIntent.builder()
+                .appId(appId)
+                .key(key)
+                .selector(selector.build())
+                .treatment(treatment.build())
+                .ingressPoints(ingressPorts)
+                .egressPoint(egressPort)
+                .priority(priority)
+                .constraints(CONSTRAINTS)
+                .build();
+    }
+
+}
index 72cc112..7a17cfe 100644 (file)
@@ -18,7 +18,7 @@ package org.onosproject.sdnip.cli;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.sdnip.SdnIpService;
+import org.onosproject.routing.SdnIpService;
 
 /**
  * Command to change whether this SDNIP instance is primary or not.
index fc5782e..6dc3ce1 100644 (file)
@@ -16,6 +16,7 @@
 package org.onosproject.sdnip;
 
 import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.MoreExecutors;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
@@ -27,10 +28,9 @@ import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
 import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -43,20 +43,13 @@ import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.routing.FibEntry;
-import org.onosproject.routing.FibUpdate;
 import org.onosproject.routing.RouteEntry;
-import org.onosproject.routing.config.BgpPeer;
-import org.onosproject.routing.config.RoutingConfigurationService;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
 
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
@@ -64,11 +57,8 @@ import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.reset;
 import static org.easymock.EasyMock.verify;
 import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
 
 /**
  * This class tests the intent synchronization function in the
@@ -76,10 +66,7 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
  */
 public class IntentSyncTest extends AbstractIntentTest {
 
-    private RoutingConfigurationService routingConfig;
-    private InterfaceService interfaceService;
     private IntentService intentService;
-    private NetworkConfigService configService;
 
     private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
             DeviceId.deviceId("of:0000000000000001"),
@@ -100,65 +87,18 @@ public class IntentSyncTest extends AbstractIntentTest {
     private IntentSynchronizer intentSynchronizer;
     private final Set<Interface> interfaces = Sets.newHashSet();
 
-    private static final ApplicationId APPID = new ApplicationId() {
-        @Override
-        public short id() {
-            return 1;
-        }
-
-        @Override
-        public String name() {
-            return "SDNIP";
-        }
-    };
+    private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        routingConfig = createMock(RoutingConfigurationService.class);
-        interfaceService = createMock(InterfaceService.class);
-        configService = createMock(NetworkConfigService.class);
-
-        // These will set expectations on routingConfig
         setUpInterfaceService();
-        setUpBgpPeers();
-
-        replay(routingConfig);
-        replay(interfaceService);
 
         intentService = createMock(IntentService.class);
 
         intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    null, routingConfig,
-                                                    interfaceService);
-    }
-
-    /**
-     * Sets up BGP peers in external networks.
-     */
-    private void setUpBgpPeers() {
-
-        Map<IpAddress, BgpPeer> peers = new HashMap<>();
-
-        String peerSw1Eth1 = "192.168.10.1";
-        peers.put(IpAddress.valueOf(peerSw1Eth1),
-                  new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
-
-        // Two BGP peers are connected to switch 2 port 1.
-        String peer1Sw2Eth1 = "192.168.20.1";
-        peers.put(IpAddress.valueOf(peer1Sw2Eth1),
-                  new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
-
-        String peer2Sw2Eth1 = "192.168.20.2";
-        peers.put(IpAddress.valueOf(peer2Sw2Eth1),
-                  new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
-
-        String peer1Sw4Eth1 = "192.168.40.1";
-        peers.put(IpAddress.valueOf(peer1Sw4Eth1),
-                  new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
-
-        expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
+                MoreExecutors.newDirectExecutorService());
     }
 
     /**
@@ -200,267 +140,13 @@ public class IntentSyncTest extends AbstractIntentTest {
                                           MacAddress.valueOf("00:00:00:00:00:04"),
                                           VlanId.vlanId((short) 1));
 
-        expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn(
-                Collections.singleton(sw4Eth1)).anyTimes();
-        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1")))
-                .andReturn(sw4Eth1).anyTimes();
-
         interfaces.add(sw4Eth1);
-
-        expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn(
-                Collections.singleton(sw1Eth1)).anyTimes();
-        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1")))
-                .andReturn(sw1Eth1).anyTimes();
-        expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn(
-                Collections.singleton(sw2Eth1)).anyTimes();
-        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1")))
-                .andReturn(sw2Eth1).anyTimes();
-        expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn(
-                Collections.singleton(sw3Eth1)).anyTimes();
-        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1")))
-                .andReturn(sw3Eth1).anyTimes();
-        expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
-    }
-
-    /**
-     * Tests adding a FIB entry to the IntentSynchronizer.
-     *
-     * We verify that the synchronizer records the correct state and that the
-     * correct intent is submitted to the IntentService.
-     *
-     * @throws TestUtilsException
-     */
-    @Test
-    public void testFibAdd() throws TestUtilsException {
-        FibEntry fibEntry = new FibEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.10.1"),
-                MacAddress.valueOf("00:00:00:00:00:01"));
-
-        // Construct a MultiPointToSinglePointIntent intent
-        TrafficSelector.Builder selectorBuilder =
-                DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
-                fibEntry.prefix());
-
-        TrafficTreatment.Builder treatmentBuilder =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
-
-        Set<ConnectPoint> ingressPoints = new HashSet<>();
-        ingressPoints.add(SW2_ETH1);
-        ingressPoints.add(SW3_ETH1);
-        ingressPoints.add(SW4_ETH1);
-
-        MultiPointToSinglePointIntent intent =
-                MultiPointToSinglePointIntent.builder()
-                        .appId(APPID)
-                        .selector(selectorBuilder.build())
-                        .treatment(treatmentBuilder.build())
-                        .ingressPoints(ingressPoints)
-                        .egressPoint(SW1_ETH1)
-                        .constraints(IntentSynchronizer.CONSTRAINTS)
-                        .build();
-
-        // Setup the expected intents
-        intentService.submit(eqExceptId(intent));
-        replay(intentService);
-
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-
-        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
-                                            fibEntry);
-        intentSynchronizer.update(Collections.singleton(fibUpdate),
-                                  Collections.emptyList());
-
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-                intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentKey = new IntentKey(intent);
-        assertTrue(firstIntentKey.equals(intentKey));
-        verify(intentService);
-    }
-
-    /**
-     * Tests adding a FIB entry with to a next hop in a VLAN.
-     *
-     * We verify that the synchronizer records the correct state and that the
-     * correct intent is submitted to the IntentService.
-     *
-     * @throws TestUtilsException
-     */
-    @Test
-    public void testFibAddWithVlan() throws TestUtilsException {
-        FibEntry fibEntry = new FibEntry(
-                Ip4Prefix.valueOf("3.3.3.0/24"),
-                Ip4Address.valueOf("192.168.40.1"),
-                MacAddress.valueOf("00:00:00:00:00:04"));
-
-        // Construct a MultiPointToSinglePointIntent intent
-        TrafficSelector.Builder selectorBuilder =
-                DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                       .matchIPDst(fibEntry.prefix())
-                       .matchVlanId(VlanId.ANY);
-
-        TrafficTreatment.Builder treatmentBuilder =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04"))
-                        .setVlanId(VlanId.vlanId((short) 1));
-
-        Set<ConnectPoint> ingressPoints = new HashSet<>();
-        ingressPoints.add(SW1_ETH1);
-        ingressPoints.add(SW2_ETH1);
-        ingressPoints.add(SW3_ETH1);
-
-        MultiPointToSinglePointIntent intent =
-                MultiPointToSinglePointIntent.builder()
-                        .appId(APPID)
-                        .selector(selectorBuilder.build())
-                        .treatment(treatmentBuilder.build())
-                        .ingressPoints(ingressPoints)
-                        .egressPoint(SW4_ETH1)
-                        .constraints(IntentSynchronizer.CONSTRAINTS)
-                        .build();
-
-        // Setup the expected intents
-        intentService.submit(eqExceptId(intent));
-
-        replay(intentService);
-
-        // Run the test
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
-
-        intentSynchronizer.update(Collections.singleton(fibUpdate),
-                                  Collections.emptyList());
-
-        // Verify
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentKey = new IntentKey(intent);
-        assertTrue(firstIntentKey.equals(intentKey));
-        verify(intentService);
-    }
-
-    /**
-     * Tests updating a FIB entry.
-     *
-     * We verify that the synchronizer records the correct state and that the
-     * correct intent is submitted to the IntentService.
-     *
-     * @throws TestUtilsException
-     */
-    @Test
-    public void testFibUpdate() throws TestUtilsException {
-        // Firstly add a route
-        testFibAdd();
-
-        Intent addedIntent =
-                intentSynchronizer.getRouteIntents().iterator().next();
-
-        // Start to construct a new route entry and new intent
-        FibEntry fibEntryUpdate = new FibEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.20.1"),
-                MacAddress.valueOf("00:00:00:00:00:02"));
-
-        // Construct a new MultiPointToSinglePointIntent intent
-        TrafficSelector.Builder selectorBuilderNew =
-                DefaultTrafficSelector.builder();
-        selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
-                fibEntryUpdate.prefix());
-
-        TrafficTreatment.Builder treatmentBuilderNew =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
-
-
-        Set<ConnectPoint> ingressPointsNew = new HashSet<>();
-        ingressPointsNew.add(SW1_ETH1);
-        ingressPointsNew.add(SW3_ETH1);
-        ingressPointsNew.add(SW4_ETH1);
-
-        MultiPointToSinglePointIntent intentNew =
-                MultiPointToSinglePointIntent.builder()
-                        .appId(APPID)
-                        .selector(selectorBuilderNew.build())
-                        .treatment(treatmentBuilderNew.build())
-                        .ingressPoints(ingressPointsNew)
-                        .egressPoint(SW2_ETH1)
-                        .constraints(IntentSynchronizer.CONSTRAINTS)
-                        .build();
-
-        // Set up test expectation
-        reset(intentService);
-        // Setup the expected intents
-        intentService.withdraw(eqExceptId(addedIntent));
-        intentService.submit(eqExceptId(intentNew));
-        replay(intentService);
-
-        // Call the update() method in IntentSynchronizer class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
-                                                  fibEntryUpdate);
-        intentSynchronizer.update(Collections.singletonList(fibUpdate),
-                                  Collections.emptyList());
-
-        // Verify
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-                intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentNewKey = new IntentKey(intentNew);
-        assertTrue(firstIntentKey.equals(intentNewKey));
-        verify(intentService);
     }
 
     /**
-     * Tests deleting a FIB entry.
-     *
-     * We verify that the synchronizer records the correct state and that the
-     * correct intent is withdrawn from the IntentService.
-     *
-     * @throws TestUtilsException
-     */
-    @Test
-    public void testFibDelete() throws TestUtilsException {
-        // Firstly add a route
-        testFibAdd();
-
-        Intent addedIntent =
-                intentSynchronizer.getRouteIntents().iterator().next();
-
-        // Construct the existing route entry
-        FibEntry fibEntry = new FibEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
-
-        // Set up expectation
-        reset(intentService);
-        // Setup the expected intents
-        intentService.withdraw(eqExceptId(addedIntent));
-        replay(intentService);
-
-        // Call the update() method in IntentSynchronizer class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
-        intentSynchronizer.update(Collections.emptyList(),
-                                  Collections.singletonList(fibUpdate));
-
-        // Verify
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
-        verify(intentService);
-    }
-
-    /**
-     * This method tests the behavior of intent Synchronizer.
+     * Tests the synchronization behavior of intent synchronizer. We set up
+     * a discrepancy between the intent service state and the intent
+     * synchronizer's state and ensure that this is reconciled correctly.
      *
      * @throws TestUtilsException
      */
@@ -529,27 +215,13 @@ public class IntentSyncTest extends AbstractIntentTest {
         // Compose a intent, which is equal to intent5 but the id is different.
         MultiPointToSinglePointIntent intent5New =
                 staticIntentBuilder(intent5, routeEntry5, "00:00:00:00:00:01");
-        assertThat(IntentSynchronizer.IntentKey.equalIntents(
-                        intent5, intent5New),
-                   is(true));
+        assertThat(IntentUtils.equals(intent5, intent5New), is(true));
         assertFalse(intent5.equals(intent5New));
 
         MultiPointToSinglePointIntent intent6 = intentBuilder(
                 routeEntry6.prefix(), "00:00:00:00:00:01",  SW1_ETH1);
 
-        // Set up the routeIntents field in IntentSynchronizer class
-        ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
-            routeIntents =  new ConcurrentHashMap<>();
-        routeIntents.put(routeEntry1.prefix(), intent1);
-        routeIntents.put(routeEntry3.prefix(), intent3);
-        routeIntents.put(routeEntry4Update.prefix(), intent4Update);
-        routeIntents.put(routeEntry5.prefix(), intent5New);
-        routeIntents.put(routeEntry6.prefix(), intent6);
-        routeIntents.put(routeEntry7.prefix(), intent7);
-        TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
-
         // Set up expectation
-        reset(intentService);
         Set<Intent> intents = new HashSet<>();
         intents.add(intent1);
         expect(intentService.getIntentState(intent1.key()))
@@ -568,9 +240,9 @@ public class IntentSyncTest extends AbstractIntentTest {
                 .andReturn(IntentState.WITHDRAWING).anyTimes();
         expect(intentService.getIntents()).andReturn(intents).anyTimes();
 
+        // These are the operations that should be done to the intentService
+        // during synchronization
         intentService.withdraw(intent2);
-        intentService.withdraw(intent4);
-
         intentService.submit(intent3);
         intentService.submit(intent4Update);
         intentService.submit(intent6);
@@ -578,16 +250,101 @@ public class IntentSyncTest extends AbstractIntentTest {
         replay(intentService);
 
         // Start the test
+
+        // Simulate some input from the clients. The intent synchronizer has not
+        // gained the global leadership yet, but it will remember this input for
+        // when it does.
+        intentSynchronizer.submit(intent1);
+        intentSynchronizer.submit(intent2);
+        intentSynchronizer.withdraw(intent2);
+        intentSynchronizer.submit(intent3);
+        intentSynchronizer.submit(intent4);
+        intentSynchronizer.submit(intent4Update);
+        intentSynchronizer.submit(intent5);
+        intentSynchronizer.submit(intent6);
+        intentSynchronizer.submit(intent7);
+
+        // Give the leadership to the intent synchronizer. It will now attempt
+        // to synchronize the intents in the store with the intents it has
+        // recorded based on the earlier user input.
+        intentSynchronizer.leaderChanged(true);
+
+        verify(intentService);
+    }
+
+    /**
+     * Tests the behavior of the submit API, both when the synchronizer has
+     * leadership and when it does not.
+     */
+    @Test
+    public void testSubmit() {
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+        Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1);
+
+        // Set up expectations
+        intentService.submit(intent);
+        expect(intentService.getIntents()).andReturn(Collections.emptyList())
+                .anyTimes();
+        replay(intentService);
+
+        // Give the intent synchronizer leadership so it will submit intents
+        // to the intent service
+        intentSynchronizer.leaderChanged(true);
+
+        // Test the submit
+        intentSynchronizer.submit(intent);
+
+        verify(intentService);
+
+        // Now we'll remove leadership from the intent synchronizer and verify
+        // that it does not submit any intents to the intent service when we
+        // call the submit API
+        reset(intentService);
+        replay(intentService);
+
+        intentSynchronizer.leaderChanged(false);
+
+        intentSynchronizer.submit(intent);
+
+        verify(intentService);
+    }
+
+    /**
+     * Tests the behavior of the withdraw API, both when the synchronizer has
+     * leadership and when it does not.
+     */
+    @Test
+    public void testWithdraw() {
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+        Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1);
+
+        // Submit an intent first so we can withdraw it later
+        intentService.submit(intent);
+        intentService.withdraw(intent);
+        expect(intentService.getIntents()).andReturn(Collections.emptyList())
+                .anyTimes();
+        replay(intentService);
+
+        // Give the intent synchronizer leadership so it will submit intents
+        // to the intent service
         intentSynchronizer.leaderChanged(true);
-        intentSynchronizer.synchronizeIntents();
 
-        // Verify
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 6);
-        assertTrue(intentSynchronizer.getRouteIntents().contains(intent1));
-        assertTrue(intentSynchronizer.getRouteIntents().contains(intent3));
-        assertTrue(intentSynchronizer.getRouteIntents().contains(intent4Update));
-        assertTrue(intentSynchronizer.getRouteIntents().contains(intent5));
-        assertTrue(intentSynchronizer.getRouteIntents().contains(intent6));
+        // Test the submit then withdraw
+        intentSynchronizer.submit(intent);
+        intentSynchronizer.withdraw(intent);
+
+        verify(intentService);
+
+        // Now we'll remove leadership from the intent synchronizer and verify
+        // that it does not withdraw any intents to the intent service when we
+        // call the withdraw API
+        reset(intentService);
+        replay(intentService);
+
+        intentSynchronizer.leaderChanged(false);
+
+        intentSynchronizer.submit(intent);
+        intentSynchronizer.withdraw(intent);
 
         verify(intentService);
     }
@@ -607,10 +364,10 @@ public class IntentSyncTest extends AbstractIntentTest {
         TrafficSelector.Builder selectorBuilder =
                 DefaultTrafficSelector.builder();
         if (ipPrefix.isIp4()) {
-            selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);   // IPv4
+            selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
             selectorBuilder.matchIPDst(ipPrefix);
         } else {
-            selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);   // IPv6
+            selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
             selectorBuilder.matchIPv6Dst(ipPrefix);
         }
 
@@ -628,11 +385,12 @@ public class IntentSyncTest extends AbstractIntentTest {
         MultiPointToSinglePointIntent intent =
                 MultiPointToSinglePointIntent.builder()
                         .appId(APPID)
+                        .key(Key.of(ipPrefix.toString(), APPID))
                         .selector(selectorBuilder.build())
                         .treatment(treatmentBuilder.build())
                         .ingressPoints(ingressPoints)
                         .egressPoint(egressPoint)
-                        .constraints(IntentSynchronizer.CONSTRAINTS)
+                        .constraints(SdnIpFib.CONSTRAINTS)
                         .build();
         return intent;
     }
@@ -646,7 +404,7 @@ public class IntentSyncTest extends AbstractIntentTest {
      * @return the newly constructed MultiPointToSinglePointIntent
      * @throws TestUtilsException
      */
-    private  MultiPointToSinglePointIntent staticIntentBuilder(
+    private MultiPointToSinglePointIntent staticIntentBuilder(
             MultiPointToSinglePointIntent intent, RouteEntry routeEntry,
             String nextHopMacAddress) throws TestUtilsException {
 
index d89c3c2..c4b2daa 100644 (file)
@@ -19,7 +19,6 @@ import com.google.common.collect.Sets;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
-import org.onlab.junit.TestUtils;
 import org.onlab.junit.TestUtils.TestUtilsException;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
@@ -28,13 +27,14 @@ import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
 import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -42,8 +42,9 @@ import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
 import org.onosproject.routing.config.BgpConfig;
 import org.onosproject.routing.config.BgpPeer;
 import org.onosproject.routing.config.BgpSpeaker;
@@ -71,26 +72,15 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
  */
 public class PeerConnectivityManagerTest extends AbstractIntentTest {
 
-    private static final ApplicationId APPID = new ApplicationId() {
-        @Override
-        public short id() {
-            return 0;
-        }
-
-        @Override
-        public String name() {
-            return "foo";
-        }
-    };
+    private static final ApplicationId APPID = TestApplicationId.create("foo");
 
     private static final ApplicationId CONFIG_APP_ID = APPID;
 
     private PeerConnectivityManager peerConnectivityManager;
-    private IntentSynchronizer intentSynchronizer;
+    private IntentSynchronizationService intentSynchronizer;
     private RoutingConfigurationService routingConfig;
     private InterfaceService interfaceService;
     private NetworkConfigService networkConfigService;
-    private IntentService intentService;
 
     private Set<BgpConfig.BgpSpeakerConfig> bgpSpeakers;
     private Map<String, Interface> interfaces;
@@ -98,8 +88,6 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
 
     private BgpConfig bgpConfig;
 
-    private Map<String, Interface> configuredInterfaces;
-    private Map<IpAddress, BgpPeer> configuredPeers;
     private List<PointToPointIntent> intentList;
 
     private final String dpid1 = "00:00:00:00:00:00:00:01";
@@ -136,7 +124,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
         // These will set expectations on routingConfig and interfaceService
         bgpSpeakers = setUpBgpSpeakers();
         interfaces = Collections.unmodifiableMap(setUpInterfaces());
-        peers = Collections.unmodifiableMap(setUpPeers());
+        peers = setUpPeers();
 
         initPeerConnectivity();
         intentList = setUpIntentList();
@@ -169,11 +157,11 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
      * Sets up logical interfaces, which emulate the configured interfaces
      * in SDN-IP application.
      *
-     * @return configured interfaces as a MAP from Interface name to Interface
+     * @return configured interfaces as a map from interface name to Interface
      */
     private Map<String, Interface> setUpInterfaces() {
 
-        configuredInterfaces = new HashMap<>();
+        Map<String, Interface> configuredInterfaces = new HashMap<>();
 
         String interfaceSw1Eth1 = "s1-eth1";
         InterfaceIpAddress ia1 =
@@ -242,7 +230,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
      */
     private Map<IpAddress, BgpPeer> setUpPeers() {
 
-        configuredPeers = new HashMap<>();
+        Map<IpAddress, BgpPeer> configuredPeers = new HashMap<>();
 
         String peerSw1Eth1 = "192.168.10.1";
         configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
@@ -266,14 +254,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
      * @return point to point intent list
      */
     private List<PointToPointIntent> setUpIntentList() {
-
         intentList = new ArrayList<>();
 
         setUpBgpIntents();
         setUpIcmpIntents();
 
         return intentList;
-
     }
 
     /**
@@ -306,8 +292,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
             builder.matchTcpDst(TpPort.tpPort(dstTcpPort));
         }
 
+        Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0]
+                + "-" + ((srcTcpPort == null) ? "dst" : "src"), APPID);
+
         PointToPointIntent intent = PointToPointIntent.builder()
                 .appId(APPID)
+                .key(key)
                 .selector(builder.build())
                 .treatment(noTreatment)
                 .ingressPoint(srcConnectPoint)
@@ -392,8 +382,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
                 .matchIPDst(IpPrefix.valueOf(dstPrefix))
                 .build();
 
+        Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0]
+                + "-" + "icmp", APPID);
+
         PointToPointIntent intent = PointToPointIntent.builder()
                 .appId(APPID)
+                .key(key)
                 .selector(selector)
                 .treatment(noTreatment)
                 .ingressPoint(srcConnectPoint)
@@ -434,19 +428,14 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
         expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
         expect(bgpConfig.bgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
         replay(bgpConfig);
-        expect(networkConfigService.getConfig(APPID, BgpConfig.class)).andReturn(bgpConfig).anyTimes();
+        expect(networkConfigService.getConfig(APPID, BgpConfig.class))
+                .andReturn(bgpConfig).anyTimes();
         replay(networkConfigService);
         replay(routingConfig);
         replay(interfaceService);
 
-        intentService = createMock(IntentService.class);
-        replay(intentService);
-
-        intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    null, routingConfig,
-                                                    interfaceService);
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+        intentSynchronizer = createMock(IntentSynchronizationService.class);
+        replay(intentSynchronizer);
 
         peerConnectivityManager =
             new PeerConnectivityManager(APPID, intentSynchronizer,
@@ -464,20 +453,18 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
      */
     @Test
     public void testConnectionSetup() {
-
-        reset(intentService);
+        reset(intentSynchronizer);
 
         // Setup the expected intents
         for (Intent intent : intentList) {
-            intentService.submit(eqExceptId(intent));
+            intentSynchronizer.submit(eqExceptId(intent));
         }
-        replay(intentService);
+        replay(intentSynchronizer);
 
         // Running the interface to be tested.
         peerConnectivityManager.start();
 
-        verify(intentService);
-
+        verify(intentSynchronizer);
     }
 
     /**
@@ -488,7 +475,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
         reset(interfaceService);
 
         expect(interfaceService.getInterfaces()).andReturn(
-                Sets.<Interface>newHashSet()).anyTimes();
+                Sets.newHashSet()).anyTimes();
         expect(interfaceService.getInterfacesByPort(s2Eth1))
                 .andReturn(Collections.emptySet()).anyTimes();
         expect(interfaceService.getInterfacesByPort(s1Eth1))
@@ -508,10 +495,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
 
         replay(interfaceService);
 
-        reset(intentService);
-        replay(intentService);
+        reset(intentSynchronizer);
+        replay(intentSynchronizer);
         peerConnectivityManager.start();
-        verify(intentService);
+        verify(intentSynchronizer);
     }
 
     /**
@@ -527,10 +514,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
         expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
         replay(routingConfig);
 
-        reset(intentService);
-        replay(intentService);
+        reset(intentSynchronizer);
+        replay(intentSynchronizer);
         peerConnectivityManager.start();
-        verify(intentService);
+        verify(intentSynchronizer);
     }
 
     /**
@@ -540,7 +527,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
     @Test
     public void testNoPeerInterface() {
         String peerSw100Eth1 = "192.168.200.1";
-        configuredPeers.put(IpAddress.valueOf(peerSw100Eth1),
+        peers.put(IpAddress.valueOf(peerSw100Eth1),
                 new BgpPeer("00:00:00:00:00:00:01:00", 1, peerSw100Eth1));
         testConnectionSetup();
     }
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
new file mode 100644 (file)
index 0000000..5466d52
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intent.AbstractIntentTest;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.routing.FibEntry;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.config.BgpPeer;
+import org.onosproject.routing.config.RoutingConfigurationService;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
+
+/**
+ * Unit tests for SdnIpFib.
+ */
+public class SdnIpFibTest extends AbstractIntentTest {
+
+    private RoutingConfigurationService routingConfig;
+    private InterfaceService interfaceService;
+
+    private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000001"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000002"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000003"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000004"),
+            PortNumber.portNumber(1));
+
+    private SdnIpFib sdnipFib;
+    private IntentSynchronizationService intentSynchronizer;
+    private final Set<Interface> interfaces = Sets.newHashSet();
+
+    private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        routingConfig = createMock(RoutingConfigurationService.class);
+        interfaceService = createMock(InterfaceService.class);
+
+        // These will set expectations on routingConfig and interfaceService
+        setUpInterfaceService();
+        setUpBgpPeers();
+
+        replay(routingConfig);
+        replay(interfaceService);
+
+        intentSynchronizer = createMock(IntentSynchronizationService.class);
+
+        sdnipFib = new SdnIpFib(APPID, interfaceService, intentSynchronizer);
+    }
+
+    /**
+     * Sets up BGP peers in external networks.
+     */
+    private void setUpBgpPeers() {
+
+        Map<IpAddress, BgpPeer> peers = new HashMap<>();
+
+        String peerSw1Eth1 = "192.168.10.1";
+        peers.put(IpAddress.valueOf(peerSw1Eth1),
+                new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
+
+        // Two BGP peers are connected to switch 2 port 1.
+        String peer1Sw2Eth1 = "192.168.20.1";
+        peers.put(IpAddress.valueOf(peer1Sw2Eth1),
+                new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
+
+        String peer2Sw2Eth1 = "192.168.20.2";
+        peers.put(IpAddress.valueOf(peer2Sw2Eth1),
+                new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
+
+        String peer1Sw4Eth1 = "192.168.40.1";
+        peers.put(IpAddress.valueOf(peer1Sw4Eth1),
+                new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
+
+        expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
+    }
+
+    /**
+     * Sets up InterfaceService.
+     */
+    private void setUpInterfaceService() {
+        Set<InterfaceIpAddress> interfaceIpAddresses1 = Sets.newHashSet();
+        interfaceIpAddresses1.add(new InterfaceIpAddress(
+                IpAddress.valueOf("192.168.10.101"),
+                IpPrefix.valueOf("192.168.10.0/24")));
+        Interface sw1Eth1 = new Interface(SW1_ETH1,
+                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
+                VlanId.NONE);
+        interfaces.add(sw1Eth1);
+
+        Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
+        interfaceIpAddresses2.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.20.101"),
+                        IpPrefix.valueOf("192.168.20.0/24")));
+        Interface sw2Eth1 = new Interface(SW2_ETH1,
+                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
+                VlanId.NONE);
+        interfaces.add(sw2Eth1);
+
+        Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
+        interfaceIpAddresses3.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.30.101"),
+                        IpPrefix.valueOf("192.168.30.0/24")));
+        Interface sw3Eth1 = new Interface(SW3_ETH1,
+                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
+                VlanId.NONE);
+        interfaces.add(sw3Eth1);
+
+        InterfaceIpAddress interfaceIpAddress4 =
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"),
+                        IpPrefix.valueOf("192.168.40.0/24"));
+        Interface sw4Eth1 = new Interface(SW4_ETH1,
+                Sets.newHashSet(interfaceIpAddress4),
+                MacAddress.valueOf("00:00:00:00:00:04"),
+                VlanId.vlanId((short) 1));
+
+        expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn(
+                Collections.singleton(sw4Eth1)).anyTimes();
+        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1")))
+                .andReturn(sw4Eth1).anyTimes();
+
+        interfaces.add(sw4Eth1);
+
+        expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn(
+                Collections.singleton(sw1Eth1)).anyTimes();
+        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1")))
+                .andReturn(sw1Eth1).anyTimes();
+        expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn(
+                Collections.singleton(sw2Eth1)).anyTimes();
+        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1")))
+                .andReturn(sw2Eth1).anyTimes();
+        expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn(
+                Collections.singleton(sw3Eth1)).anyTimes();
+        expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1")))
+                .andReturn(sw3Eth1).anyTimes();
+        expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
+    }
+
+    /**
+     * Tests adding a FIB entry to the IntentSynchronizer.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is submitted to the IntentService.
+     */
+    @Test
+    public void testFibAdd() {
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+        FibEntry fibEntry = new FibEntry(prefix,
+                Ip4Address.valueOf("192.168.10.1"),
+                MacAddress.valueOf("00:00:00:00:00:01"));
+
+        // Construct a MultiPointToSinglePointIntent intent
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+                fibEntry.prefix());
+
+        TrafficTreatment.Builder treatmentBuilder =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
+
+        Set<ConnectPoint> ingressPoints = new HashSet<>();
+        ingressPoints.add(SW2_ETH1);
+        ingressPoints.add(SW3_ETH1);
+        ingressPoints.add(SW4_ETH1);
+
+        MultiPointToSinglePointIntent intent =
+                MultiPointToSinglePointIntent.builder()
+                        .appId(APPID)
+                        .key(Key.of(prefix.toString(), APPID))
+                        .selector(selectorBuilder.build())
+                        .treatment(treatmentBuilder.build())
+                        .ingressPoints(ingressPoints)
+                        .egressPoint(SW1_ETH1)
+                        .constraints(SdnIpFib.CONSTRAINTS)
+                        .build();
+
+        // Setup the expected intents
+        intentSynchronizer.submit(eqExceptId(intent));
+        replay(intentSynchronizer);
+
+        // Send in the UPDATE FibUpdate
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
+        sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+
+        verify(intentSynchronizer);
+    }
+
+    /**
+     * Tests adding a FIB entry with to a next hop in a VLAN.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is submitted to the IntentService.
+     */
+    @Test
+    public void testFibAddWithVlan() {
+        IpPrefix prefix = Ip4Prefix.valueOf("3.3.3.0/24");
+        FibEntry fibEntry = new FibEntry(prefix,
+                Ip4Address.valueOf("192.168.40.1"),
+                MacAddress.valueOf("00:00:00:00:00:04"));
+
+        // Construct a MultiPointToSinglePointIntent intent
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(fibEntry.prefix())
+                .matchVlanId(VlanId.ANY);
+
+        TrafficTreatment.Builder treatmentBuilder =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04"))
+                .setVlanId(VlanId.vlanId((short) 1));
+
+        Set<ConnectPoint> ingressPoints = new HashSet<>();
+        ingressPoints.add(SW1_ETH1);
+        ingressPoints.add(SW2_ETH1);
+        ingressPoints.add(SW3_ETH1);
+
+        MultiPointToSinglePointIntent intent =
+                MultiPointToSinglePointIntent.builder()
+                        .appId(APPID)
+                        .key(Key.of(prefix.toString(), APPID))
+                        .selector(selectorBuilder.build())
+                        .treatment(treatmentBuilder.build())
+                        .ingressPoints(ingressPoints)
+                        .egressPoint(SW4_ETH1)
+                        .constraints(SdnIpFib.CONSTRAINTS)
+                        .build();
+
+        // Setup the expected intents
+        intentSynchronizer.submit(eqExceptId(intent));
+
+        replay(intentSynchronizer);
+
+        // Send in the UPDATE FibUpdate
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
+        sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+
+        verify(intentSynchronizer);
+    }
+
+    /**
+     * Tests updating a FIB entry.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is submitted to the IntentService.
+     */
+    @Test
+    public void testFibUpdate() {
+        // Firstly add a route
+        testFibAdd();
+
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+
+        // Start to construct a new route entry and new intent
+        FibEntry fibEntryUpdate = new FibEntry(prefix,
+                Ip4Address.valueOf("192.168.20.1"),
+                MacAddress.valueOf("00:00:00:00:00:02"));
+
+        // Construct a new MultiPointToSinglePointIntent intent
+        TrafficSelector.Builder selectorBuilderNew =
+                DefaultTrafficSelector.builder();
+        selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+                fibEntryUpdate.prefix());
+
+        TrafficTreatment.Builder treatmentBuilderNew =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
+
+        Set<ConnectPoint> ingressPointsNew = new HashSet<>();
+        ingressPointsNew.add(SW1_ETH1);
+        ingressPointsNew.add(SW3_ETH1);
+        ingressPointsNew.add(SW4_ETH1);
+
+        MultiPointToSinglePointIntent intentNew =
+                MultiPointToSinglePointIntent.builder()
+                        .appId(APPID)
+                        .key(Key.of(prefix.toString(), APPID))
+                        .selector(selectorBuilderNew.build())
+                        .treatment(treatmentBuilderNew.build())
+                        .ingressPoints(ingressPointsNew)
+                        .egressPoint(SW2_ETH1)
+                        .constraints(SdnIpFib.CONSTRAINTS)
+                        .build();
+
+        // Set up test expectation
+        reset(intentSynchronizer);
+
+        // Setup the expected intents
+        intentSynchronizer.submit(eqExceptId(intentNew));
+        replay(intentSynchronizer);
+
+        // Send in the UPDATE FibUpdate
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
+                fibEntryUpdate);
+        sdnipFib.update(Collections.singletonList(fibUpdate),
+                Collections.emptyList());
+
+        verify(intentSynchronizer);
+    }
+
+    /**
+     * Tests deleting a FIB entry.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is withdrawn from the IntentService.
+     */
+    @Test
+    public void testFibDelete() {
+        // Firstly add a route
+        testFibAdd();
+
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+
+        // Construct the existing route entry
+        FibEntry fibEntry = new FibEntry(prefix, null, null);
+
+        // Construct the existing MultiPointToSinglePoint intent
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+                fibEntry.prefix());
+
+        TrafficTreatment.Builder treatmentBuilder =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
+
+        Set<ConnectPoint> ingressPoints = new HashSet<>();
+        ingressPoints.add(SW2_ETH1);
+        ingressPoints.add(SW3_ETH1);
+        ingressPoints.add(SW4_ETH1);
+
+        MultiPointToSinglePointIntent addedIntent =
+                MultiPointToSinglePointIntent.builder()
+                        .appId(APPID)
+                        .key(Key.of(prefix.toString(), APPID))
+                        .selector(selectorBuilder.build())
+                        .treatment(treatmentBuilder.build())
+                        .ingressPoints(ingressPoints)
+                        .egressPoint(SW1_ETH1)
+                        .constraints(SdnIpFib.CONSTRAINTS)
+                        .build();
+
+        // Set up expectation
+        reset(intentSynchronizer);
+        // Setup the expected intents
+        intentSynchronizer.withdraw(eqExceptId(addedIntent));
+        replay(intentSynchronizer);
+
+        // Send in the DELETE FibUpdate
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
+        sdnipFib.update(Collections.emptyList(), Collections.singletonList(fibUpdate));
+
+        verify(intentSynchronizer);
+    }
+}
index 69b18aa..7f825e8 100644 (file)
@@ -17,7 +17,6 @@ package org.onosproject.sdnip;
 
 import org.easymock.IArgumentMatcher;
 import org.onosproject.net.intent.Intent;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
 
 import static org.easymock.EasyMock.reportMatcher;
 
@@ -53,8 +52,6 @@ public final class TestIntentServiceHelper {
      * the solution is to use an EasyMock matcher that verifies that all the
      * value properties of the provided intent match the expected values, but
      * ignores the intent ID when testing equality.
-     *
-     * FIXME this currently does not take key into account
      */
     private static final class IdAgnosticIntentMatcher implements
                 IArgumentMatcher {
@@ -86,9 +83,7 @@ public final class TestIntentServiceHelper {
             Intent providedIntent = (Intent) object;
             providedString = providedIntent.toString();
 
-            IntentKey thisIntentKey = new IntentKey(intent);
-            IntentKey providedIntentKey = new IntentKey(providedIntent);
-            return thisIntentKey.equals(providedIntentKey);
+            return IntentUtils.equals(intent, providedIntent);
         }
     }
 
index ef9d444..8fdf81a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,24 +18,26 @@ package org.onosproject.segmentrouting;
 import com.google.common.collect.Lists;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
+import org.onosproject.incubator.net.config.basics.ConfigException;
+import org.onosproject.incubator.net.config.basics.InterfaceConfig;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
+import org.onosproject.segmentrouting.config.SegmentRoutingConfig.AdjacencySid;
 import org.onosproject.segmentrouting.grouphandler.DeviceProperties;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
-import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig;
-import org.onosproject.segmentrouting.config.NetworkConfigManager;
-import org.onosproject.segmentrouting.config.SegmentRouterConfig;
-import org.onosproject.segmentrouting.config.SegmentRouterConfig.Subnet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Segment Routing configuration component that reads the
@@ -50,7 +52,6 @@ public class DeviceConfiguration implements DeviceProperties {
             .getLogger(DeviceConfiguration.class);
     private final List<Integer> allSegmentIds = new ArrayList<>();
     private final HashMap<DeviceId, SegmentRouterInfo> deviceConfigMap = new HashMap<>();
-    private final NetworkConfigManager configService;
 
     private class SegmentRouterInfo {
         int nodeSid;
@@ -60,48 +61,60 @@ public class DeviceConfiguration implements DeviceProperties {
         boolean isEdge;
         HashMap<PortNumber, Ip4Address> gatewayIps;
         HashMap<PortNumber, Ip4Prefix> subnets;
-        List<SegmentRouterConfig.AdjacencySid> adjacencySids;
+        List<AdjacencySid> adjacencySids;
     }
 
     /**
      * Constructor. Reads all the configuration for all devices of type
      * Segment Router and organizes into various maps for easier access.
-     *
-     * @param configService handle to network configuration manager
-     * component from where the relevant configuration is retrieved.
      */
-    public DeviceConfiguration(NetworkConfigManager configService) {
-        this.configService = checkNotNull(configService);
-        List<SwitchConfig> allSwitchCfg =
-                this.configService.getConfiguredAllowedSwitches();
-        for (SwitchConfig cfg : allSwitchCfg) {
-            if (!(cfg instanceof SegmentRouterConfig)) {
-                continue;
-            }
+    public DeviceConfiguration(NetworkConfigRegistry cfgService) {
+        // Read config from device subject, excluding gatewayIps and subnets.
+        Set<DeviceId> deviceSubjects =
+                cfgService.getSubjects(DeviceId.class, SegmentRoutingConfig.class);
+        deviceSubjects.forEach(subject -> {
+            SegmentRoutingConfig config =
+                cfgService.getConfig(subject, SegmentRoutingConfig.class);
             SegmentRouterInfo info = new SegmentRouterInfo();
-            info.nodeSid = ((SegmentRouterConfig) cfg).getNodeSid();
-            info.deviceId = ((SegmentRouterConfig) cfg).getDpid();
-            info.mac = MacAddress.valueOf(((
-                    SegmentRouterConfig) cfg).getRouterMac());
-            String routerIp = ((SegmentRouterConfig) cfg).getRouterIp();
-            Ip4Prefix prefix = checkNotNull(IpPrefix.valueOf(routerIp).getIp4Prefix());
-            info.ip = prefix.address();
-            info.isEdge = ((SegmentRouterConfig) cfg).isEdgeRouter();
-            info.subnets = new HashMap<>();
+            info.deviceId = subject;
+            info.nodeSid = config.getSid();
+            info.ip = config.getIp();
+            info.mac = config.getMac();
+            info.isEdge = config.isEdgeRouter();
+            info.adjacencySids = config.getAdjacencySids();
             info.gatewayIps = new HashMap<>();
-            for (Subnet s: ((SegmentRouterConfig) cfg).getSubnets()) {
-                info.subnets.put(PortNumber.portNumber(s.getPortNo()),
-                                 Ip4Prefix.valueOf(s.getSubnetIp()));
-                String gatewayIp = s.getSubnetIp().
-                        substring(0, s.getSubnetIp().indexOf('/'));
-                info.gatewayIps.put(PortNumber.portNumber(s.getPortNo()),
-                                    Ip4Address.valueOf(gatewayIp));
-            }
-            info.adjacencySids = ((SegmentRouterConfig) cfg).getAdjacencySids();
+            info.subnets = new HashMap<>();
+
             this.deviceConfigMap.put(info.deviceId, info);
             this.allSegmentIds.add(info.nodeSid);
-
-        }
+        });
+
+        // Read gatewayIps and subnets from port subject.
+        Set<ConnectPoint> portSubjects =
+            cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class);
+        portSubjects.forEach(subject -> {
+            InterfaceConfig config =
+                    cfgService.getConfig(subject, InterfaceConfig.class);
+            Set<Interface> networkInterfaces;
+            try {
+                networkInterfaces = config.getInterfaces();
+            } catch (ConfigException e) {
+                log.error("Error loading port configuration");
+                return;
+            }
+            networkInterfaces.forEach(networkInterface -> {
+                DeviceId dpid = networkInterface.connectPoint().deviceId();
+                PortNumber port = networkInterface.connectPoint().port();
+                SegmentRouterInfo info = this.deviceConfigMap.get(dpid);
+
+                Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
+                interfaceAddresses.forEach(interfaceAddress -> {
+                    info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
+                    info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
+                });
+            });
+
+        });
     }
 
     /**
@@ -379,8 +392,8 @@ public class DeviceConfiguration implements DeviceProperties {
      */
     public List<Integer> getPortsForAdjacencySid(DeviceId deviceId, int sid) {
         if (deviceConfigMap.get(deviceId) != null) {
-            for (SegmentRouterConfig.AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) {
-                if (asid.getAdjSid() == sid) {
+            for (AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) {
+                if (asid.getAsid() == sid) {
                     return asid.getPorts();
                 }
             }
@@ -402,9 +415,9 @@ public class DeviceConfiguration implements DeviceProperties {
             if (deviceConfigMap.get(deviceId).adjacencySids.isEmpty()) {
                 return false;
             } else {
-                for (SegmentRouterConfig.AdjacencySid asid:
+                for (AdjacencySid asid:
                         deviceConfigMap.get(deviceId).adjacencySids) {
-                    if (asid.getAdjSid() == sid) {
+                    if (asid.getAsid() == sid) {
                         return true;
                     }
                 }
@@ -414,4 +427,4 @@ public class DeviceConfiguration implements DeviceProperties {
 
         return false;
     }
-}
+}
\ No newline at end of file
index 0f8fa59..f65f03e 100644 (file)
@@ -111,10 +111,10 @@ public class IcmpHandler {
         icmpReplyIpv4.setChecksum((short) 0);
 
         ICMP icmpReply = new ICMP();
+        icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload());
         icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY);
         icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY);
         icmpReply.setChecksum((short) 0);
-
         icmpReplyIpv4.setPayload(icmpReply);
 
         icmpReplyEth.setPayload(icmpReplyIpv4);
index 874faab..0566312 100644 (file)
@@ -27,6 +27,12 @@ import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.event.Event;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
 import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
 import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey;
@@ -50,7 +56,6 @@ import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.topology.TopologyService;
-import org.onosproject.segmentrouting.config.NetworkConfigManager;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
 import org.onosproject.store.service.StorageService;
@@ -133,7 +138,21 @@ public class SegmentRoutingManager implements SegmentRoutingService {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected StorageService storageService;
 
-    private NetworkConfigManager networkConfigService = new NetworkConfigManager();;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry cfgService;
+
+    private final InternalConfigListener cfgListener =
+            new InternalConfigListener(this);
+
+    private final ConfigFactory cfgFactory =
+            new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY,
+                              SegmentRoutingConfig.class,
+                              "segmentrouting") {
+                @Override
+                public SegmentRoutingConfig createConfig() {
+                    return new SegmentRoutingConfig();
+                }
+            };
 
     private Object threadSchedulerLock = new Object();
     private static int numOfEventsQueued = 0;
@@ -192,44 +211,17 @@ public class SegmentRoutingManager implements SegmentRoutingService {
                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
                 .build();
 
-        networkConfigService.init();
-        deviceConfiguration = new DeviceConfiguration(networkConfigService);
-        arpHandler = new ArpHandler(this);
-        icmpHandler = new IcmpHandler(this);
-        ipHandler = new IpHandler(this);
-        routingRulePopulator = new RoutingRulePopulator(this);
-        defaultRoutingHandler = new DefaultRoutingHandler(this);
-        tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
-                groupHandlerMap, tunnelStore);
-        policyHandler = new PolicyHandler(appId, deviceConfiguration,
-                flowObjectiveService, tunnelHandler, policyStore);
-
-        packetService.addProcessor(processor, PacketProcessor.director(2));
-        linkService.addListener(new InternalLinkListener());
-        deviceService.addListener(new InternalDeviceListener());
-
-        for (Device device : deviceService.getDevices()) {
-            //Irrespective whether the local is a MASTER or not for this device,
-            //create group handler instance and push default TTP flow rules.
-            //Because in a multi-instance setup, instances can initiate
-            //groups for any devices. Also the default TTP rules are needed
-            //to be pushed before inserting any IP table entries for any device
-            DefaultGroupHandler groupHandler = DefaultGroupHandler
-                    .createGroupHandler(device.id(), appId,
-                                        deviceConfiguration, linkService,
-                                        flowObjectiveService,
-                                        nsNextObjStore);
-            groupHandlerMap.put(device.id(), groupHandler);
-            defaultRoutingHandler.populateTtpRules(device.id());
-        }
+        cfgService.addListener(cfgListener);
+        cfgService.registerConfigFactory(cfgFactory);
 
-        defaultRoutingHandler.startPopulationProcess();
         log.info("Started");
-
     }
 
     @Deactivate
     protected void deactivate() {
+        cfgService.removeListener(cfgListener);
+        cfgService.unregisterConfigFactory(cfgFactory);
+
         packetService.removeProcessor(processor);
         processor = null;
         log.info("Stopped");
@@ -512,6 +504,59 @@ public class SegmentRoutingManager implements SegmentRoutingService {
         }
     }
 
+    private class InternalConfigListener implements NetworkConfigListener {
+        SegmentRoutingManager segmentRoutingManager;
+
+        public InternalConfigListener(SegmentRoutingManager srMgr) {
+            this.segmentRoutingManager = srMgr;
+        }
 
+        public void configureNetwork() {
+            deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
+
+            arpHandler = new ArpHandler(segmentRoutingManager);
+            icmpHandler = new IcmpHandler(segmentRoutingManager);
+            ipHandler = new IpHandler(segmentRoutingManager);
+            routingRulePopulator = new RoutingRulePopulator(segmentRoutingManager);
+            defaultRoutingHandler = new DefaultRoutingHandler(segmentRoutingManager);
+
+            tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
+                                              groupHandlerMap, tunnelStore);
+            policyHandler = new PolicyHandler(appId, deviceConfiguration,
+                                              flowObjectiveService,
+                                              tunnelHandler, policyStore);
+
+            packetService.addProcessor(processor, PacketProcessor.director(2));
+            linkService.addListener(new InternalLinkListener());
+            deviceService.addListener(new InternalDeviceListener());
+
+            for (Device device : deviceService.getDevices()) {
+                //Irrespective whether the local is a MASTER or not for this device,
+                //create group handler instance and push default TTP flow rules.
+                //Because in a multi-instance setup, instances can initiate
+                //groups for any devices. Also the default TTP rules are needed
+                //to be pushed before inserting any IP table entries for any device
+                DefaultGroupHandler groupHandler = DefaultGroupHandler
+                        .createGroupHandler(device.id(), appId,
+                                            deviceConfiguration, linkService,
+                                            flowObjectiveService,
+                                            nsNextObjStore);
+                groupHandlerMap.put(device.id(), groupHandler);
+                defaultRoutingHandler.populateTtpRules(device.id());
+            }
 
+            defaultRoutingHandler.startPopulationProcess();
+        }
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+                    event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+                    event.configClass().equals(SegmentRoutingConfig.class)) {
+                log.info("Network configuration change detected. (Re)Configuring...");
+                configureNetwork();
+                return;
+            }
+        }
+    }
 }
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
new file mode 100644 (file)
index 0000000..6dc3f0d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.basics.BasicElementConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Configuration object for Segment Routing Application.
+ */
+public class SegmentRoutingConfig extends Config<DeviceId> {
+    private static final String NAME = "name";
+    private static final String IP = "routerIp";
+    private static final String MAC = "routerMac";
+    private static final String SID = "nodeSid";
+    private static final String EDGE = "isEdgeRouter";
+    private static final String ADJSID = "adjacencySids";
+
+    public Optional<String> getName() {
+        String name = get(NAME, null);
+        return name != null ? Optional.of(name) : Optional.empty();
+    }
+
+    public BasicElementConfig setName(String name) {
+        return (BasicElementConfig) setOrClear(NAME, name);
+    }
+
+    public Ip4Address getIp() {
+        String ip = get(IP, null);
+        return ip != null ? Ip4Address.valueOf(ip) : null;
+    }
+
+    public BasicElementConfig setIp(String ip) {
+        return (BasicElementConfig) setOrClear(IP, ip);
+    }
+
+    public MacAddress getMac() {
+        String mac = get(MAC, null);
+        return mac != null ? MacAddress.valueOf(mac) : null;
+    }
+
+    public BasicElementConfig setMac(String mac) {
+        return (BasicElementConfig) setOrClear(MAC, mac);
+    }
+
+    public int getSid() {
+        return get(SID, -1);
+    }
+
+    public BasicElementConfig setSid(int sid) {
+        return (BasicElementConfig) setOrClear(SID, sid);
+    }
+
+    public boolean isEdgeRouter() {
+        return get(EDGE, false);
+    }
+
+    public BasicElementConfig setEdgeRouter(boolean isEdgeRouter) {
+        return (BasicElementConfig) setOrClear(EDGE, isEdgeRouter);
+    }
+
+    public List<AdjacencySid> getAdjacencySids() {
+        ArrayList<AdjacencySid> adjacencySids = new ArrayList<>();
+
+        if (!object.has(ADJSID)) {
+            return adjacencySids;
+        }
+
+        ArrayNode adjacencySidNodes = (ArrayNode) object.path(ADJSID);
+        adjacencySidNodes.forEach(adjacencySidNode -> {
+            int asid = adjacencySidNode.path(AdjacencySid.ASID).asInt();
+
+            ArrayList<Integer> ports = new ArrayList<Integer>();
+            ArrayNode portsNodes = (ArrayNode) adjacencySidNode.path(AdjacencySid.PORTS);
+            portsNodes.forEach(portNode -> {
+                ports.add(portNode.asInt());
+            });
+
+            AdjacencySid adjacencySid = new AdjacencySid(asid, ports);
+            adjacencySids.add(adjacencySid);
+        });
+
+        return adjacencySids;
+    }
+
+    public class AdjacencySid {
+        private static final String ASID = "adjSid";
+        private static final String PORTS = "ports";
+
+        int asid;
+        List<Integer> ports;
+
+        public AdjacencySid(int asid, List<Integer> ports) {
+            this.asid = asid;
+            this.ports = ports;
+        }
+
+        public int getAsid() {
+            return asid;
+        }
+
+        public List<Integer> getPorts() {
+            return ports;
+        }
+    }
+}
\ No newline at end of file
index fdae9c9..95f7e24 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 816ca7b..497f525 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 12c8140..d93ad78 100644 (file)
@@ -92,7 +92,8 @@ public class CounterTestIncrementCommand extends AbstractShellCommand {
         } catch (InterruptedException e) {
             return;
         } catch (ExecutionException | TimeoutException e) {
-            e.printStackTrace();
+            print("Error executing command");
+            log.error("Error executing command counter-test-increment", e);
         }
     }
 }
index de9e9f2..ad3236e 100644 (file)
@@ -179,8 +179,8 @@ public class IntentPerfInstaller {
         workers = Executors.newFixedThreadPool(DEFAULT_NUM_WORKERS, groupedThreads("onos/intent-perf", "worker-%d"));
 
         // disable flow backups for testing
-        configService.setProperty("org.onosproject.store.flow.impl.DistributedFlowRuleStore",
-                                  "backupEnabled", "false");
+        configService.setProperty("org.onosproject.store.flow.impl.NewDistributedFlowRuleStore",
+                                  "backupEnabled", "true");
 
         // TODO: replace with shared executor
         messageHandlingExecutor = Executors.newSingleThreadExecutor(
diff --git a/framework/src/onos/apps/vtn/app/app.xml b/framework/src/onos/apps/vtn/app/app.xml
new file mode 100644 (file)
index 0000000..a0efd7f
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<app name="org.onosproject.vtn" origin="ON.Lab" version="${project.version}"
+     featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
+     features="${project.artifactId}">
+    <description>${project.description}</description>
+
+    <artifact>mvn:${project.groupId}/onos-app-vtn-mgr/${project.version}</artifact>
+    <artifact>mvn:${project.groupId}/onos-app-vtn-web/${project.version}</artifact>
+    <artifact>mvn:${project.groupId}/onos-app-vtn-rsc/${project.version}</artifact>
+</app>
diff --git a/framework/src/onos/apps/vtn/app/features.xml b/framework/src/onos/apps/vtn/app/features.xml
new file mode 100644 (file)
index 0000000..c82b41d
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+    <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository>
+    <feature name="${project.artifactId}" version="${project.version}"
+             description="${project.description}">
+        <feature>onos-api</feature>
+        <feature>onos-drivers</feature>
+        <bundle>mvn:${project.groupId}/onos-app-vtn-mgr/${project.version}</bundle>
+        <bundle>mvn:${project.groupId}/onos-app-vtn-web/${project.version}</bundle>
+        <bundle>mvn:${project.groupId}/onos-app-vtn-rsc/${project.version}</bundle>
+    </feature>
+</features>
diff --git a/framework/src/onos/apps/vtn/app/pom.xml b/framework/src/onos/apps/vtn/app/pom.xml
new file mode 100644 (file)
index 0000000..4ed6617
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ~ Copyright 2014 Open Networking Laboratory ~ ~ Licensed under the Apache 
+       License, Version 2.0 (the "License"); ~ you may not use this file except 
+       in compliance with the License. ~ You may obtain a copy of the License at 
+       ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable 
+       law or agreed to in writing, software ~ distributed under the License is 
+       distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY 
+       KIND, either express or implied. ~ See the License for the specific language 
+       governing permissions and ~ limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.onosproject</groupId>
+               <artifactId>onos-app-vtn</artifactId>
+               <version>1.4.0-SNAPSHOT</version>
+               <relativePath>../pom.xml</relativePath>
+       </parent>
+
+       <artifactId>onos-app-vtn-onosfw</artifactId>
+       <packaging>pom</packaging>
+
+       <description>ONOS framework applications</description>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.onosproject</groupId>
+                       <artifactId>onos-app-vtn-rsc</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.onosproject</groupId>
+                       <artifactId>onos-app-vtn-web</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.onosproject</groupId>
+                       <artifactId>onos-app-vtn-mgr</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+       </dependencies>
+
+</project>
index fb8fcb1..c2cfe2b 100644 (file)
@@ -1,6 +1,6 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
-  ~ Copyright 2015 Open Networking Laboratory
+  ~ Copyright 2014 Open Networking Laboratory
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<project
-        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
-        xmlns="http://maven.apache.org/POM/4.0.0"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
+
     <parent>
         <groupId>org.onosproject</groupId>
         <artifactId>onos-apps</artifactId>
     </parent>
 
     <artifactId>onos-app-vtn</artifactId>
-    <packaging>bundle</packaging>
+    <packaging>pom</packaging>
 
+    <description>ONOS framework applications</description>
 
-    <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <onos.app.name>org.onosproject.vtn</onos.app.name>
-    </properties>
-    <dependencies>
-        <dependency>
-            <groupId>javax.ws.rs</groupId>
-            <artifactId>jsr311-api</artifactId>
-            <version>1.1.1</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-incubator-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-core-serializers</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-vtnrsc</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-    </dependencies>
+    <modules>
+        <module>vtnrsc</module>
+        <module>vtnmgr</module>
+        <module>vtnweb</module>
+        <module>app</module>
+       </modules>
 </project>
diff --git a/framework/src/onos/apps/vtn/vtnmgr/pom.xml b/framework/src/onos/apps/vtn/vtnmgr/pom.xml
new file mode 100644 (file)
index 0000000..03e6670
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        xmlns="http://maven.apache.org/POM/4.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-app-vtn</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-app-vtn-mgr</artifactId>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-serializers</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-vtn-rsc</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java
new file mode 100644 (file)
index 0000000..a20f852
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtn;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.Host;
+
+/**
+ * VTN application that applies configuration and flows to the device.
+ */
+public interface VTNService {
+
+    /**
+     * Creates a vxlan tunnel and creates the ovs when a ovs controller node is detected.
+     *
+     * @param device controller-type device
+     */
+    void onServerDetected(Device device);
+
+    /**
+     * Drops a vxlan tunnel and drops the ovs when a ovs controller node is vanished.
+     *
+     * @param device controller-type device
+     */
+    void onServerVanished(Device device);
+
+    /**
+     * Applies default forwarding flows when a ovs is detected.
+     *
+     * @param device switch-type device
+     */
+    void onOvsDetected(Device device);
+
+    /**
+     * Remove default forwarding flows when a ovs is vanished.
+     *
+     * @param device switch-type device
+     */
+    void onOvsVanished(Device device);
+
+    /**
+     * Applies multicast flows and tunnel flows when a VM is detected.
+     *
+     * @param host a VM
+     */
+    void onHostDetected(Host host);
+
+    /**
+     * Remove multicast flows and tunnel flows when a VM is vanished.
+     *
+     * @param host a VM
+     */
+    void onHostVanished(Host host);
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java
new file mode 100644 (file)
index 0000000..090ef0f
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtn.impl;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Collectors;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.behaviour.DefaultTunnelDescription;
+import org.onosproject.net.behaviour.IpTunnelEndPoint;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.behaviour.TunnelConfig;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.behaviour.TunnelEndPoint;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vtn.VTNService;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Provides implementation of VTNService.
+ */
+@Component(immediate = true)
+@Service
+public class VTNManager implements VTNService {
+    private final Logger log = getLogger(getClass());
+
+    private static final String APP_ID = "org.onosproject.app.vtn";
+    private ScheduledExecutorService backgroundService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TenantNetworkService tenantNetworkService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VirtualPortService virtualPortService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DriverService driverService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveStore flowObjectiveStore;
+    protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+    private EventuallyConsistentMap<HostId, SegmentationId> binding;
+    private ApplicationId appId;
+    private HostListener hostListener = new InnerHostListener();
+    private DeviceListener deviceListener = new InnerDeviceListener();
+    private static final String IFACEID = "ifaceid";
+    private static final String PORT_HEAD = "vxlan";
+    private static final String DEFAULT_BRIDGE_NAME = "br-int";
+    private static final String CONTROLLER_IP_KEY = "ipaddress";
+    private static final int DEFAULT_MAC_PRIORITY = 0x0000;
+    private static final int MAC_PRIORITY = 0xffff;
+    private static final int DEFAULT_PORT_PRIORITY = 0x0000;
+    private static final int PORT_PRIORITY = 0xffff;
+    private static final String SWITCH_CHANNEL_ID = "channelId";
+    private static final String DRIVER_NAME = "onosfw";
+
+    @Activate
+    public void activate() {
+        KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
+                .register(KryoNamespaces.API);
+        appId = coreService.registerApplication(APP_ID);
+        deviceService.addListener(deviceListener);
+        hostService.addListener(hostListener);
+        backgroundService = newSingleThreadScheduledExecutor(groupedThreads("onos-apps/vtn",
+                                                                            "manager-background"));
+        binding = storageService
+                .<HostId, SegmentationId>eventuallyConsistentMapBuilder()
+                .withName("all_tunnel").withSerializer(serializer)
+                .withTimestampProvider((k, v) -> new WallClockTimestamp())
+                .build();
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        backgroundService.shutdown();
+        binding.destroy();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void onServerDetected(Device device) {
+        Iterable<Device> devices = deviceService.getAvailableDevices();
+        DriverHandler handler = driverService.createHandler(device.id());
+        BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
+        bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME));
+        String ipAddress = device.annotations().value(CONTROLLER_IP_KEY);
+        IpAddress ip = IpAddress.valueOf(ipAddress);
+        Sets.newHashSet(devices).stream()
+                .filter(d -> Device.Type.CONTROLLER == d.type())
+                .filter(d -> !device.id().equals(d.id())).forEach(d -> {
+                        String ipAddress1 = d.annotations()
+                                .value(CONTROLLER_IP_KEY);
+                        IpAddress ip1 = IpAddress.valueOf(ipAddress1);
+                        applyTunnelConfig(ip, ip1, handler);
+                        DriverHandler handler1 = driverService
+                                .createHandler(d.id());
+                        applyTunnelConfig(ip1, ip, handler1);
+
+                });
+    }
+
+    @Override
+    public void onServerVanished(Device device) {
+        Iterable<Device> devices = deviceService.getAvailableDevices();
+        String ipAddress = device.annotations().value(CONTROLLER_IP_KEY);
+        IpAddress dst = IpAddress.valueOf(ipAddress);
+        Sets.newHashSet(devices).stream()
+                .filter(d -> d.type() == Device.Type.CONTROLLER)
+                .filter(d -> !device.id().equals(d.id())).forEach(d -> {
+                    String ipAddress1 = d.annotations()
+                            .value(CONTROLLER_IP_KEY);
+                    DriverHandler handler = driverService.createHandler(d.id());
+                    IpAddress src = IpAddress.valueOf(ipAddress1);
+                    removeTunnelConfig(src, dst, handler);
+                });
+    }
+
+    private void applyTunnelConfig(IpAddress src, IpAddress dst,
+                                   DriverHandler handler) {
+        TunnelEndPoint tunnelAsSrc = IpTunnelEndPoint.ipTunnelPoint(src);
+        TunnelEndPoint tunnelAsDst = IpTunnelEndPoint.ipTunnelPoint(dst);
+        TunnelDescription tunnel = new DefaultTunnelDescription(
+                                                                tunnelAsSrc,
+                                                                tunnelAsDst,
+                                                                TunnelDescription.Type.VXLAN,
+                                                                null);
+        TunnelConfig config = handler.behaviour(TunnelConfig.class);
+        config.createTunnel(tunnel);
+    }
+
+    private void removeTunnelConfig(IpAddress src, IpAddress dst,
+                                    DriverHandler handler) {
+        TunnelEndPoint tunnelAsSrc = IpTunnelEndPoint.ipTunnelPoint(src);
+        TunnelEndPoint tunnelAsDst = IpTunnelEndPoint.ipTunnelPoint(dst);
+        TunnelDescription tunnel = new DefaultTunnelDescription(
+                                                                tunnelAsSrc,
+                                                                tunnelAsDst,
+                                                                TunnelDescription.Type.VXLAN,
+                                                                null);
+        TunnelConfig config = handler.behaviour(TunnelConfig.class);
+        config.removeTunnel(tunnel);
+    }
+
+    @Override
+    public void onOvsDetected(Device device) {
+        programMacDefaultRules(device.id(), appId, Objective.Operation.ADD);
+        programPortDefaultRules(device.id(), appId, Objective.Operation.ADD);
+    }
+
+    @Override
+    public void onOvsVanished(Device device) {
+        programMacDefaultRules(device.id(), appId, Objective.Operation.REMOVE);
+        programPortDefaultRules(device.id(), appId, Objective.Operation.REMOVE);
+    }
+
+    @Override
+    public void onHostDetected(Host host) {
+        String ifaceId = host.annotations().value(IFACEID);
+        DeviceId deviceId = host.location().deviceId();
+        String currentControllerIp = getControllerIpOfSwitch(deviceId);
+        Iterable<Device> devices = deviceService.getAvailableDevices();
+        VirtualPortId portId = VirtualPortId.portId(ifaceId);
+        VirtualPort port = virtualPortService.getPort(portId);
+        TenantNetwork network = tenantNetworkService
+                .getNetwork(port.networkId());
+        String tunnelName = "vxlan-" + currentControllerIp;
+        binding.put(host.id(), network.segmentationId());
+        List<Port> allPorts = deviceService.getPorts(deviceId);
+        PortNumber inPort = host.location().port();
+        List<PortNumber> localVmPorts = getLocalPorts(deviceId, ifaceId);
+        List<PortNumber> localTunnelPorts = new ArrayList<>();
+        Sets.newHashSet(allPorts.iterator()).stream()
+        .filter(p -> !p.number().equals(PortNumber.LOCAL)).forEach(p -> {
+            if (p.annotations().value("portName").startsWith(PORT_HEAD)) {
+                localTunnelPorts.add(p.number());
+            }
+        });
+
+        localVmPorts.forEach(lp -> programLocalBcastRules(deviceId, network.segmentationId(), lp, localVmPorts,
+                                                          localTunnelPorts, appId, Objective.Operation.ADD));
+        programLocalOut(deviceId, network.segmentationId(), inPort, host.mac(),
+                        appId, Objective.Operation.ADD);
+        localTunnelPorts
+                .forEach(tp -> programTunnelFloodOut(deviceId,
+                                                     network.segmentationId(),
+                                                     tp, localVmPorts,
+                                                     appId,
+                                                     Objective.Operation.ADD));
+        Sets.newHashSet(devices).stream()
+                .filter(d -> d.type() == Device.Type.CONTROLLER).forEach(d -> {
+                    DriverHandler handler = driverService.createHandler(d.id());
+                    BridgeConfig bridgeConfig = handler
+                            .behaviour(BridgeConfig.class);
+                    Collection<BridgeDescription> bridgeDescriptions = bridgeConfig
+                            .getBridges();
+
+                    Iterator<BridgeDescription> it = bridgeDescriptions
+                            .iterator();
+                    if (it.hasNext()) {
+                        BridgeDescription sw = it.next();
+                        Set<PortNumber> ports = bridgeConfig.getPortNumbers();
+                        ports.stream()
+                                .filter(p -> p.name()
+                                        .equalsIgnoreCase(tunnelName))
+                                .forEach(p -> programTunnelOut(sw.deviceId(),
+                                                 network.segmentationId(), p,
+                                                 host.mac(), appId,
+                                                 Objective.Operation.ADD));
+                    }
+                });
+        programLocalIn(deviceId, network.segmentationId(), inPort, host.mac(),
+                       appId, Objective.Operation.ADD);
+        localTunnelPorts
+                .forEach(tp -> programTunnelIn(deviceId,
+                                               network.segmentationId(),
+                                               tp, inPort, host.mac(),
+                                               appId, Objective.Operation.ADD));
+
+    }
+
+    @Override
+    public void onHostVanished(Host host) {
+        String ifaceId = host.annotations().value(IFACEID);
+        SegmentationId segId = binding.remove(host.id());
+        DeviceId deviceId = host.location().deviceId();
+        String currentControllerIp = getControllerIpOfSwitch(deviceId);
+        Iterable<Device> devices = deviceService.getAvailableDevices();
+
+        String tunnelName = "vxlan-" + currentControllerIp;
+        List<Port> allPorts = deviceService.getPorts(deviceId);
+        PortNumber inPort = host.location().port();
+
+        List<PortNumber> localTunnelPorts = new ArrayList<>();
+        Sets.newHashSet(allPorts.iterator()).stream()
+        .filter(p -> !p.number().equals(PortNumber.LOCAL)).forEach(p -> {
+            if (p.annotations().value("portName").startsWith(PORT_HEAD)) {
+                localTunnelPorts.add(p.number());
+            }
+        });
+
+        List<PortNumber> localVmPorts = getLocalPorts(deviceId, ifaceId);
+        localVmPorts.add(inPort);
+        localVmPorts.forEach(lp -> programLocalBcastRules(deviceId, segId, lp, localVmPorts,
+                                                          localTunnelPorts, appId, Objective.Operation.REMOVE));
+        programLocalOut(deviceId, segId, inPort, host.mac(),
+                        appId, Objective.Operation.REMOVE);
+        localTunnelPorts
+                .forEach(tp -> programTunnelFloodOut(deviceId,
+                                                     segId,
+                                                     tp, localVmPorts,
+                                                     appId,
+                                                     Objective.Operation.REMOVE));
+        Sets.newHashSet(devices).stream()
+                .filter(d -> d.type() == Device.Type.CONTROLLER).forEach(d -> {
+                    DriverHandler handler = driverService.createHandler(d.id());
+                    BridgeConfig bridgeConfig = handler
+                            .behaviour(BridgeConfig.class);
+                    Collection<BridgeDescription> bridgeDescriptions = bridgeConfig
+                            .getBridges();
+
+                    Iterator<BridgeDescription> it = bridgeDescriptions
+                            .iterator();
+                    if (it.hasNext()) {
+                        BridgeDescription sw = it.next();
+                        Set<PortNumber> ports = bridgeConfig.getPortNumbers();
+                        ports.stream()
+                                .filter(p -> p.name()
+                                        .equalsIgnoreCase(tunnelName))
+                                .forEach(p -> programTunnelOut(sw.deviceId(),
+                                                 segId, p,
+                                                 host.mac(), appId,
+                                                 Objective.Operation.REMOVE));
+                    }
+                });
+        programLocalIn(deviceId, segId, inPort, host.mac(),
+                       appId, Objective.Operation.REMOVE);
+        localTunnelPorts
+                .forEach(tp -> programTunnelIn(deviceId,
+                                               segId,
+                                               tp, inPort, host.mac(),
+                                               appId, Objective.Operation.REMOVE));
+    }
+
+    private class InnerDeviceListener implements DeviceListener {
+
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            if (Device.Type.CONTROLLER == device.type()
+                    && DeviceEvent.Type.DEVICE_ADDED == event.type()) {
+                backgroundService.execute(() -> onServerDetected(device));
+            } else if (Device.Type.CONTROLLER == device.type()
+                    && DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED == event
+                            .type()) {
+                backgroundService.execute(() -> onServerVanished(device));
+            } else if (Device.Type.SWITCH == device.type()
+                    && DeviceEvent.Type.DEVICE_ADDED == event.type()) {
+                backgroundService.execute(() -> onOvsDetected(device));
+            } else if (Device.Type.SWITCH == device.type()
+                    && DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED == event
+                            .type()) {
+                backgroundService.execute(() -> onOvsVanished(device));
+            } else {
+                log.info("Do nothing for this device type");
+            }
+        }
+
+    }
+
+    private class InnerHostListener implements HostListener {
+
+        @Override
+        public void event(HostEvent event) {
+            Host host = event.subject();
+            if (HostEvent.Type.HOST_ADDED == event.type()) {
+                backgroundService.execute(() -> onHostDetected(host));
+            } else if (HostEvent.Type.HOST_REMOVED == event.type()) {
+                backgroundService.execute(() -> onHostVanished(host));
+            } else if (HostEvent.Type.HOST_UPDATED == event.type()) {
+                backgroundService.execute(() -> {
+                    onHostVanished(host);
+                    onHostDetected(host);
+                });
+            }
+        }
+
+    }
+
+    // Used to forward the flows to the local VM.
+    private void programLocalOut(DeviceId dpid, SegmentationId segmentationId,
+                                 PortNumber outPort, MacAddress sourceMac,
+                                 ApplicationId appid,
+                                 Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchTunnelId(Long.parseLong(segmentationId.toString()))
+                .matchEthDst(sourceMac).build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(outPort).build();
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment).withSelector(selector)
+                .fromApp(appId).withFlag(Flag.SPECIFIC)
+                .withPriority(MAC_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(dpid, objective.add());
+        } else {
+            flowServiceForward(dpid, objective.remove());
+        }
+
+    }
+
+    // Used to forward the flows into the VXLAN tunnel.
+    private void programTunnelOut(DeviceId dpid, SegmentationId segmentationId,
+                                  PortNumber tunnelOutPort, MacAddress dstMac,
+                                  ApplicationId appid,
+                                  Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthDst(dstMac).add(Criteria.matchTunnelId(Long
+                        .parseLong(segmentationId.toString())))
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+
+        .setOutput(tunnelOutPort).build();
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment).withSelector(selector)
+                .fromApp(appId).withFlag(Flag.SPECIFIC)
+                .withPriority(MAC_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(dpid, objective.add());
+        } else {
+            flowServiceForward(dpid, objective.remove());
+        }
+
+    }
+
+    // Used to forward multicast flows to remote VMs of the same tenant via
+    // VXLAN tunnel.
+    private void programTunnelFloodOut(DeviceId deviceId,
+                                       SegmentationId segmentationId,
+                                       PortNumber ofPortOut,
+                                       List<PortNumber> localVmPorts,
+                                       ApplicationId appid,
+                                       Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(ofPortOut)
+
+                .add(Criteria.matchTunnelId(Long.parseLong(segmentationId
+                             .toString()))).matchEthDst(MacAddress.BROADCAST)
+                .build();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+
+        for (PortNumber outPort : localVmPorts) {
+                treatment.setOutput(outPort);
+        }
+
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment.build())
+                .withSelector(selector).fromApp(appId).makePermanent()
+                .withFlag(Flag.SPECIFIC).withPriority(MAC_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(deviceId, objective.add());
+        } else {
+            flowServiceForward(deviceId, objective.remove());
+        }
+    }
+
+    // Applies default flows to mac table.
+    private void programMacDefaultRules(DeviceId dpid, ApplicationId appid,
+                                        Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder().build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder().drop()
+                .build();
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment).withSelector(selector)
+                .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC)
+                .withPriority(DEFAULT_MAC_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(dpid, objective.add());
+        } else {
+            flowServiceForward(dpid, objective.remove());
+        }
+    }
+
+    // Used to forward the flows to the local VMs with the same tenant.
+    private void programLocalBcastRules(DeviceId deviceId,
+                                        SegmentationId segmentationId,
+                                        PortNumber inPort,
+                                        List<PortNumber> localVmPorts,
+                                        List<PortNumber> localTunnelPorts,
+                                        ApplicationId appid,
+                                        Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(inPort).matchEthDst(MacAddress.BROADCAST)
+                .add(Criteria.matchTunnelId(Long
+                        .parseLong(segmentationId.toString())))
+                .build();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        for (PortNumber outPort : localVmPorts) {
+            if (inPort != outPort) {
+                treatment.setOutput(outPort);
+            }
+        }
+        for (PortNumber outport : localTunnelPorts) {
+                treatment.setOutput(outport);
+        }
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment.build())
+                .withSelector(selector).fromApp(appId).makePermanent()
+                .withFlag(Flag.SPECIFIC).withPriority(MAC_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(deviceId, objective.add());
+        } else {
+            flowServiceForward(deviceId, objective.remove());
+        }
+    }
+
+    // Used to apply local entry flow.
+    private void programLocalIn(DeviceId dpid, SegmentationId segmentationId,
+                                PortNumber inPort, MacAddress srcMac,
+                                ApplicationId appid, Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(inPort).matchEthSrc(srcMac).build();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        treatment.add(Instructions.modTunnelId(Long.parseLong(segmentationId
+                .toString())));
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment.build())
+                .withSelector(selector).fromApp(appId).makePermanent()
+                .withFlag(Flag.SPECIFIC).withPriority(PORT_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(dpid, objective.add());
+        } else {
+            flowServiceForward(dpid, objective.remove());
+        }
+    }
+
+    // Used to forward the flows from the egress tunnel to the VM.
+    private void programTunnelIn(DeviceId dpid, SegmentationId segmentationId,
+                                 PortNumber tunnelInPort, PortNumber outPort,
+                                 MacAddress sourceMac, ApplicationId appid,
+                                 Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(tunnelInPort).add(Criteria.matchTunnelId(Long
+                        .parseLong(segmentationId.toString())))
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment).withSelector(selector)
+                .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC)
+                .withPriority(PORT_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(dpid, objective.add());
+        } else {
+            flowServiceForward(dpid, objective.remove());
+        }
+    }
+
+    // Applies the default flows to port table.
+    private void programPortDefaultRules(DeviceId dpid, ApplicationId appid,
+                                         Objective.Operation type) {
+        TrafficSelector selector = DefaultTrafficSelector.builder().build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+        ForwardingObjective.Builder objective = DefaultForwardingObjective
+                .builder().withTreatment(treatment).withSelector(selector)
+                .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC)
+                .withPriority(DEFAULT_PORT_PRIORITY);
+        if (type.equals(Objective.Operation.ADD)) {
+            flowServiceForward(dpid, objective.add());
+        } else {
+            flowServiceForward(dpid, objective.remove());
+        }
+    }
+
+    // Used to get channelId from the device annotations.
+    private String getControllerIpOfSwitch(DeviceId deviceId) {
+        Device device = deviceService.getDevice(deviceId);
+        String url = device.annotations().value(SWITCH_CHANNEL_ID);
+        return url.substring(0, url.lastIndexOf(":"));
+    }
+
+    private Iterable<String> getIfaceIds(String ifaceId) {
+        VirtualPortId portId = VirtualPortId.portId(ifaceId);
+        VirtualPort port = virtualPortService.getPort(portId);
+        if (port == null) {
+            return Collections.emptyList();
+        }
+
+        TenantNetwork network = tenantNetworkService
+                .getNetwork(port.networkId());
+        if (network == null) {
+            return Collections.emptyList();
+        }
+
+        Collection<VirtualPort> ports = virtualPortService
+                .getPorts(network.id());
+        return ports.stream().map(p -> p.portId().portId())
+                .collect(Collectors.toSet());
+    }
+
+    private List<PortNumber> getLocalPorts(DeviceId deviceId, String ifaceId) {
+        DriverHandler handler = driverService
+                .createHandler(getController(deviceId));
+        BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
+        Iterable<String> ifaceIds = getIfaceIds(ifaceId);
+        return bridgeConfig.getLocalPorts(ifaceIds);
+    }
+
+    private DeviceId getController(DeviceId deviceId) {
+        Iterable<Device> devices = deviceService.getAvailableDevices();
+        for (Device device : devices) {
+            if (device.type() == Device.Type.CONTROLLER && device.id()
+                    .toString().contains(getControllerIpOfSwitch(deviceId))) {
+                return device.id();
+            }
+        }
+        log.info("Can not find controller for device : {}", deviceId);
+        return null;
+    }
+
+    //Used to apply flowRule
+    private void flowServiceForward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+        Driver driver = driverService.getDriver(DRIVER_NAME);
+        Pipeliner pipeLiner = driver.createBehaviour(new DefaultDriverData(driver, deviceId), Pipeliner.class);
+        if (pipeLiner != null) {
+            final PipelinerContext context = new InnerPipelineContext();
+            pipeLiner.init(deviceId, context);
+            pipeLiner.forward(forwardingObjective);
+        }
+    }
+
+    // Processing context for initializing pipeline driver behaviours.
+    private class InnerPipelineContext implements PipelinerContext {
+        @Override
+        public ServiceDirectory directory() {
+            return serviceDirectory;
+        }
+
+        @Override
+        public FlowObjectiveStore store() {
+            return flowObjectiveStore;
+        }
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java
new file mode 100644 (file)
index 0000000..f18dbf8
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN application that applies configuration and flows to the device.
+ */
+package org.onosproject.vtn.impl;
diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java
new file mode 100644 (file)
index 0000000..371466c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN application that applies configuration and flows to the device.
+ */
+package org.onosproject.vtn;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/pom.xml b/framework/src/onos/apps/vtn/vtnrsc/pom.xml
new file mode 100644 (file)
index 0000000..8696295
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+       xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.onosproject</groupId>
+               <artifactId>onos-app-vtn</artifactId>
+               <version>1.4.0-SNAPSHOT</version>
+               <relativePath>../pom.xml</relativePath>
+       </parent>
+
+
+       <artifactId>onos-app-vtn-rsc</artifactId>
+       <packaging>bundle</packaging>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.onosproject</groupId>
+                       <artifactId>onos-api</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.onosproject</groupId>
+                       <artifactId>onos-cli</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.felix</groupId>
+                       <artifactId>org.apache.felix.scr.annotations</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.karaf.shell</groupId>
+                       <artifactId>org.apache.karaf.shell.console</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.onosproject</groupId>
+                       <artifactId>onos-core-serializers</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+       </dependencies>
+</project>
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java
new file mode 100644 (file)
index 0000000..3d6ba8e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * The continuous IP address range between the start address and the end address for the allocation pools.
+ */
+public interface AllocationPool {
+
+    /**
+     * The start address for the allocation pool.
+     *
+     * @return startIp
+     */
+    IpAddress startIp();
+
+    /**
+     * The end address for the allocation pool.
+     *
+     * @return endIp
+     */
+    IpAddress endIp();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java
new file mode 100644 (file)
index 0000000..4e1028d
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+
+/**
+ * Immutable representation of a allowed address pair.
+ */
+public final class AllowedAddressPair {
+    private final IpAddress ip;
+    private final MacAddress mac;
+    // Public construction is prohibited
+    private AllowedAddressPair(IpAddress ip, MacAddress mac) {
+        checkNotNull(ip, "IpAddress cannot be null");
+        checkNotNull(mac, "MacAddress cannot be null");
+        this.ip = ip;
+        this.mac = mac;
+    }
+    /**
+     * Returns the AllowedAddressPair ip address.
+     *
+     * @return ip address
+     */
+    public IpAddress ip() {
+        return ip;
+    }
+
+    /**
+     * Returns the AllowedAddressPair MAC address.
+     *
+     * @return MAC address
+     */
+    public MacAddress mac() {
+        return mac;
+    }
+
+
+    /**
+     * Creates a allowedAddressPair using the supplied ipAddress &amp;
+     * macAddress.
+     *
+     * @param ip IP address
+     * @param mac MAC address
+     * @return AllowedAddressPair
+     */
+    public static AllowedAddressPair allowedAddressPair(IpAddress ip,
+                                                        MacAddress mac) {
+        return new AllowedAddressPair(ip, mac);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ip, mac);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof AllowedAddressPair) {
+            final AllowedAddressPair that = (AllowedAddressPair) obj;
+            return Objects.equals(this.ip, that.ip)
+                    && Objects.equals(this.mac, that.mac);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("ip", ip).add("mac", mac).toString();
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java
new file mode 100644 (file)
index 0000000..c715d08
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+public final class BindingHostId {
+    private final String bindingHostId;
+
+    // Public construction is prohibited
+    private BindingHostId(String bindingHostId) {
+        checkNotNull(bindingHostId, "BindingHosttId cannot be null");
+        this.bindingHostId = bindingHostId;
+    }
+
+    /**
+     * Creates a BindingHostId identifier.
+     *
+     * @param bindingHostId the bindingHostId identifier
+     * @return the bindingHostId identifier
+     */
+    public static BindingHostId bindingHostId(String bindingHostId) {
+        return new BindingHostId(bindingHostId);
+    }
+
+    /**
+     * Returns the bindingHostId identifier.
+     *
+     * @return the bindingHostId identifier
+     */
+    public String bindingHostId() {
+        return bindingHostId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(bindingHostId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof BindingHostId) {
+            final BindingHostId that = (BindingHostId) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.bindingHostId, that.bindingHostId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return bindingHostId;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java
new file mode 100644 (file)
index 0000000..8a48019
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * The continuous IP address range between the start address and the end address
+ * for the allocation pools.
+ */
+public final class DefaultAllocationPool implements AllocationPool {
+
+    private final IpAddress startIp;
+    private final IpAddress endIp;
+
+    /**
+     * Creates an AllocationPool by using the start IP address and the end IP
+     * address.
+     *
+     * @param startIp the start IP address of the allocation pool
+     * @param endIp the end IP address of the allocation pool
+     */
+    public DefaultAllocationPool(IpAddress startIp, IpAddress endIp) {
+        checkNotNull(startIp, "StartIp cannot be null");
+        checkNotNull(endIp, "EndIp cannot be null");
+        this.startIp = startIp;
+        this.endIp = endIp;
+    }
+
+    @Override
+    public IpAddress startIp() {
+        return startIp;
+    }
+
+    @Override
+    public IpAddress endIp() {
+        return endIp;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(startIp, endIp);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultAllocationPool) {
+            final DefaultAllocationPool other = (DefaultAllocationPool) obj;
+            return Objects.equals(this.startIp, other.startIp)
+                    && Objects.equals(this.endIp, other.endIp);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("startIp", startIp).add("endIp", endIp)
+                .toString();
+    }
+}
+
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java
new file mode 100644 (file)
index 0000000..8679100
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Host route dictionaries for the subnet.
+ */
+public final class DefaultHostRoute implements HostRoute {
+
+    private final IpAddress nexthop;
+    private final IpPrefix destination;
+
+    /**
+     *
+     * Creates a DefaultHostRoute by using the next hop and the destination.
+     *
+     * @param nexthop of the DefaultHostRoute
+     * @param destination of the DefaultHostRoute
+     */
+    public DefaultHostRoute(IpAddress nexthop, IpPrefix destination) {
+        this.nexthop = nexthop;
+        this.destination = destination;
+    }
+
+    @Override
+    public IpAddress nexthop() {
+        return nexthop;
+    }
+
+    @Override
+    public IpPrefix destination() {
+        return destination;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("nexthop", nexthop)
+                .add("destination", destination).toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(nexthop, destination);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultHostRoute) {
+            final DefaultHostRoute other = (DefaultHostRoute) obj;
+            return Objects.equals(this.nexthop, other.nexthop)
+                    && Objects.equals(this.destination, other.destination);
+        }
+        return false;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java
new file mode 100644 (file)
index 0000000..6049b55
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Objects;
+import java.util.Set;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Default implementation of Subnet interface .
+ */
+public final class DefaultSubnet implements Subnet {
+    private final SubnetId id;
+    private final String subnetName;
+    private final TenantNetworkId networkId;
+    private final TenantId tenantId;
+    private final Version ipVersion;
+    private final IpPrefix cidr;
+    private final IpAddress gatewayIp;
+    private final boolean dhcpEnabled;
+    private final boolean shared;
+    private final Mode ipV6AddressMode;
+    private final Mode ipV6RaMode;
+    private final Set<HostRoute> hostRoutes;
+    private final Set<AllocationPool> allocationPools;
+
+    /**
+     * Creates a subnet object.
+     *
+     * @param id subnet identifier
+     * @param subnetName the name of subnet
+     * @param networkId network identifier
+     * @param tenantId tenant identifier
+     * @param ipVersion Version of ipv4 or ipv6
+     * @param cidr the cidr
+     * @param gatewayIp gateway ip
+     * @param dhcpEnabled dhcp enabled or not
+     * @param shared indicates whether this network is shared across all
+     *            tenants, By default, only administrative user can change this
+     *            value
+     * @param hostRoutes a collection of host routes
+     * @param ipV6AddressMode ipV6AddressMode
+     * @param ipV6RaMode ipV6RaMode
+     * @param allocationPoolsIt a collection of allocationPools
+     */
+    public DefaultSubnet(SubnetId id, String subnetName,
+                         TenantNetworkId networkId, TenantId tenantId,
+                         Version ipVersion, IpPrefix cidr, IpAddress gatewayIp,
+                         boolean dhcpEnabled, boolean shared,
+                         Set<HostRoute> hostRoutes, Mode ipV6AddressMode,
+                         Mode ipV6RaMode,
+                         Set<AllocationPool> allocationPoolsIt) {
+        this.id = id;
+        this.subnetName = subnetName;
+        this.networkId = networkId;
+        this.tenantId = tenantId;
+        this.ipVersion = ipVersion;
+        this.cidr = cidr;
+        this.gatewayIp = gatewayIp;
+        this.dhcpEnabled = dhcpEnabled;
+        this.shared = shared;
+        this.ipV6AddressMode = ipV6AddressMode;
+        this.ipV6RaMode = ipV6RaMode;
+        this.hostRoutes = hostRoutes;
+        this.allocationPools = allocationPoolsIt;
+    }
+
+    @Override
+    public SubnetId id() {
+        return id;
+    }
+
+    @Override
+    public String subnetName() {
+        return subnetName;
+    }
+
+    @Override
+    public TenantNetworkId networkId() {
+        return networkId;
+    }
+
+    @Override
+    public TenantId tenantId() {
+        return tenantId;
+    }
+
+    @Override
+    public Version ipVersion() {
+        return ipVersion;
+    }
+
+    @Override
+    public IpPrefix cidr() {
+        return cidr;
+    }
+
+    @Override
+    public IpAddress gatewayIp() {
+        return gatewayIp;
+    }
+
+    @Override
+    public boolean dhcpEnabled() {
+        return dhcpEnabled;
+    }
+
+    @Override
+    public boolean shared() {
+        return shared;
+    }
+
+    @Override
+    public Iterable<HostRoute> hostRoutes() {
+        return hostRoutes;
+    }
+
+    @Override
+    public Mode ipV6AddressMode() {
+        return ipV6AddressMode;
+    }
+
+    @Override
+    public Mode ipV6RaMode() {
+        return ipV6RaMode;
+    }
+
+    @Override
+    public Iterable<AllocationPool> allocationPools() {
+        return allocationPools;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, subnetName, ipVersion, cidr, gatewayIp,
+                            dhcpEnabled, shared, tenantId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultSubnet) {
+            final DefaultSubnet that = (DefaultSubnet) obj;
+            return Objects.equals(this.id, that.id)
+                    && Objects.equals(this.subnetName, that.subnetName)
+                    && Objects.equals(this.ipVersion, that.ipVersion)
+                    && Objects.equals(this.cidr, that.cidr)
+                    && Objects.equals(this.shared, that.shared)
+                    && Objects.equals(this.gatewayIp, that.gatewayIp)
+                    && Objects.equals(this.dhcpEnabled, that.dhcpEnabled);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("id", id).add("subnetName", subnetName)
+                .add("ipVersion", ipVersion).add("cidr", cidr)
+                .add("shared", shared).add("gatewayIp", gatewayIp)
+                .add("dhcpEnabled", dhcpEnabled).toString();
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java
new file mode 100644 (file)
index 0000000..8c941ea
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Objects;
+
+/**
+ * Default implementation of  TenantNetwork interface.
+ */
+public final class DefaultTenantNetwork implements TenantNetwork {
+    private final TenantNetworkId id;
+    private final String name;
+    private final boolean adminStateUp;
+    private final State state;
+    private final boolean shared;
+    private final Type type;
+    private final TenantId tenantId;
+    private final boolean routerExternal;
+    private final PhysicalNetwork physicalNetwork;
+    private final SegmentationId segmentationId;
+
+    /**
+     * Creates a neutronNetwork element attributed to the specified provider.
+     *
+     * @param id  network identifier
+     * @param name the network name
+     * @param adminStateUp administrative state of the network
+     * @param state the network state
+     * @param shared indicates whether this network is shared across all
+     *            tenants, By default, only administrative user can change this
+     *            value
+     * @param tenantId tenant identifier
+     * @param routerExternal network routerExternal
+     * @param type the network type
+     * @param physicalNetwork physicalNetwork identifier
+     * @param segmentationId segmentation identifier
+     */
+    public DefaultTenantNetwork(TenantNetworkId id, String name,
+                                boolean adminStateUp, State state,
+                                boolean shared, TenantId tenantId,
+                                boolean routerExternal, Type type,
+                                PhysicalNetwork physicalNetwork,
+                                SegmentationId segmentationId) {
+        this.id = id;
+        this.name = name;
+        this.adminStateUp = adminStateUp;
+        this.state = state;
+        this.shared = shared;
+        this.type = type;
+        this.tenantId = tenantId;
+        this.routerExternal = routerExternal;
+        this.physicalNetwork = physicalNetwork;
+        this.segmentationId = segmentationId;
+    }
+
+    @Override
+    public TenantNetworkId id() {
+        return id;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+
+    @Override
+    public boolean adminStateUp() {
+        return adminStateUp;
+    }
+
+    @Override
+    public State state() {
+        return state;
+    }
+
+    @Override
+    public boolean shared() {
+        return shared;
+    }
+
+    @Override
+    public TenantId tenantId() {
+        return tenantId;
+    }
+
+    @Override
+    public boolean routerExternal() {
+        return routerExternal;
+    }
+
+    @Override
+    public Type type() {
+        return type;
+    }
+
+    @Override
+    public PhysicalNetwork physicalNetwork() {
+        return physicalNetwork;
+    }
+
+    @Override
+    public SegmentationId segmentationId() {
+        return segmentationId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, name, adminStateUp, state, shared, tenantId,
+                            routerExternal, type, physicalNetwork,
+                            segmentationId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultTenantNetwork) {
+            final DefaultTenantNetwork that = (DefaultTenantNetwork) obj;
+            return Objects.equals(this.id, that.id)
+                    && Objects.equals(this.name, that.name)
+                    && Objects.equals(this.adminStateUp, that.adminStateUp)
+                    && Objects.equals(this.state, that.state)
+                    && Objects.equals(this.shared, that.shared)
+                    && Objects.equals(this.tenantId, that.tenantId)
+                    && Objects.equals(this.routerExternal, that.routerExternal)
+                    && Objects.equals(this.type, that.type)
+                    && Objects.equals(this.physicalNetwork,
+                                      that.physicalNetwork)
+                    && Objects.equals(this.segmentationId, that.segmentationId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("id", id).add("name", name)
+                .add("adminStateUp", adminStateUp).add("state", state)
+                .add("shared", shared).add("tenantId", tenantId)
+                .add("routeExternal", routerExternal).add("type", type)
+                .add("physicalNetwork", physicalNetwork)
+                .add("segmentationId", segmentationId).toString();
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java
new file mode 100644 (file)
index 0000000..9ee85da
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Default implementation of VirtualPort interface .
+ */
+public final class DefaultVirtualPort implements VirtualPort {
+    private final VirtualPortId id;
+    private final TenantNetworkId networkId;
+    private final Boolean adminStateUp;
+    private final String name;
+    private final State state;
+    private final MacAddress macAddress;
+    private final TenantId tenantId;
+    private final String deviceOwner;
+    private final DeviceId deviceId;
+    private final Set<FixedIp> fixedIps;
+    private final BindingHostId bindingHostId;
+    private final String bindingVnicType;
+    private final String bindingVifType;
+    private final String bindingVifDetails;
+    private final Set<AllowedAddressPair> allowedAddressPairs;
+    private final Set<SecurityGroup> securityGroups;
+
+    /**
+     * Creates a VirtualPort object.
+     *
+     * @param id the virtual port identifier
+     * @param networkId the network identifier
+     * @param adminStateUp adminStateup true or false
+     * @param strMap the map of properties of virtual port
+     * @param state virtual port state
+     * @param macAddress the MAC address
+     * @param tenantId the tenant identifier
+     * @param deviceId the device identifier
+     * @param fixedIps set of fixed IP
+     * @param bindingHostId the binding host identifier
+     * @param allowedAddressPairs the collection of allowdeAddressPairs
+     * @param securityGroups the collection of securityGroups
+     */
+    public DefaultVirtualPort(VirtualPortId id,
+                              TenantNetworkId networkId,
+                              Boolean adminStateUp,
+                              Map<String, String> strMap,
+                              State state,
+                              MacAddress macAddress,
+                              TenantId tenantId,
+                              DeviceId deviceId,
+                              Set<FixedIp> fixedIps,
+                              BindingHostId bindingHostId,
+                              Set<AllowedAddressPair> allowedAddressPairs,
+                              Set<SecurityGroup> securityGroups) {
+        this.id = id;
+        this.networkId = networkId;
+        this.adminStateUp = adminStateUp;
+        this.name = strMap.get("name");
+        this.state = state;
+        this.macAddress = macAddress;
+        this.tenantId = tenantId;
+        this.deviceOwner = strMap.get("deviceOwner");
+        this.deviceId = deviceId;
+        this.fixedIps = fixedIps;
+        this.bindingHostId = bindingHostId;
+        this.bindingVnicType = strMap.get("bindingVnicType");
+        this.bindingVifType = strMap.get("bindingVifType");
+        this.bindingVifDetails = strMap.get("bindingVifDetails");
+        this.allowedAddressPairs = allowedAddressPairs;
+        this.securityGroups = securityGroups;
+    }
+
+    @Override
+    public VirtualPortId portId() {
+        return id;
+    }
+
+    @Override
+    public TenantNetworkId networkId() {
+        return networkId;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+
+    @Override
+    public boolean adminStateUp() {
+        return adminStateUp;
+    }
+
+    @Override
+    public State state() {
+        return state;
+    }
+
+    @Override
+    public MacAddress macAddress() {
+        return macAddress;
+    }
+
+    @Override
+    public TenantId tenantId() {
+        return tenantId;
+    }
+
+    @Override
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    @Override
+    public String deviceOwner() {
+        return deviceOwner;
+    }
+
+    @Override
+    public Collection<AllowedAddressPair> allowedAddressPairs() {
+        return allowedAddressPairs;
+    }
+
+    @Override
+    public Set<FixedIp> fixedIps() {
+        return fixedIps;
+    }
+
+    @Override
+    public BindingHostId bindingHostId() {
+        return bindingHostId;
+    }
+
+    @Override
+    public String bindingVnicType() {
+        return bindingVifType;
+    }
+
+    @Override
+    public String bindingVifType() {
+        return bindingVifType;
+    }
+
+    @Override
+    public String bindingVifDetails() {
+        return bindingVifDetails;
+    }
+
+    @Override
+    public Collection<SecurityGroup> securityGroups() {
+        return securityGroups;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, networkId, adminStateUp, name, state,
+                            macAddress, tenantId, deviceId, deviceOwner,
+                            allowedAddressPairs, fixedIps, bindingHostId,
+                            bindingVnicType, bindingVifType, bindingVifDetails,
+                            securityGroups);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultVirtualPort) {
+            final DefaultVirtualPort that = (DefaultVirtualPort) obj;
+            return Objects.equals(this.id, that.id)
+                    && Objects.equals(this.networkId, that.networkId)
+                    && Objects.equals(this.adminStateUp, that.adminStateUp)
+                    && Objects.equals(this.state, that.state)
+                    && Objects.equals(this.name, that.name)
+                    && Objects.equals(this.tenantId, that.tenantId)
+                    && Objects.equals(this.macAddress, that.macAddress)
+                    && Objects.equals(this.deviceId, that.deviceId)
+                    && Objects.equals(this.deviceOwner, that.deviceOwner)
+                    && Objects.equals(this.allowedAddressPairs,
+                                      that.allowedAddressPairs)
+                    && Objects.equals(this.fixedIps, that.fixedIps)
+                    && Objects.equals(this.bindingHostId, that.bindingHostId)
+                    && Objects.equals(this.bindingVifDetails,
+                                      that.bindingVifDetails)
+                    && Objects.equals(this.bindingVifType, that.bindingVifType)
+                    && Objects.equals(this.bindingVnicType,
+                                      that.bindingVnicType)
+                    && Objects.equals(this.securityGroups, that.securityGroups);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("id", id).add("network_id", networkId)
+                .add("adminStateUp", adminStateUp).add("state", state)
+                .add("name", name).add("state", state)
+                .add("macAddress", macAddress).add("tenantId", tenantId)
+                .add("deviced", deviceId).add("deviceOwner", deviceOwner)
+                .add("allowedAddressPairs", allowedAddressPairs)
+                .add("fixedIp", fixedIps).add("bindingHostId", bindingHostId)
+                .add("bindingVnicType", bindingVnicType)
+                .add("bindingVifDetails", bindingVifDetails)
+                .add("bindingVifType", bindingVifType)
+                .add("securityGroups", securityGroups).toString();
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java
new file mode 100644 (file)
index 0000000..c6569a7
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * Immutable representation of a IP address for the port, Include the IP address
+ * and subnet identity.
+ */
+public final class FixedIp {
+    private final SubnetId subnetId;
+    private final IpAddress ip;
+    // Public construction is prohibited
+    private FixedIp(SubnetId subnetId, IpAddress ip) {
+        checkNotNull(subnetId, "SubnetId cannot be null");
+        checkNotNull(ip, "IpAddress cannot be null");
+        this.subnetId = subnetId;
+        this.ip = ip;
+    }
+
+    /**
+     * Returns the FixedIp subnet identifier.
+     *
+     * @return subnet identifier
+     */
+    public SubnetId subnetId() {
+        return subnetId;
+    }
+
+    /**
+     * Returns the FixedIp IP address.
+     *
+     * @return IP address
+     */
+    public IpAddress ip() {
+        return ip;
+    }
+
+    /**
+     * Creates a fixed ip using the supplied fixedIp.
+     *
+     * @param subnetId subnet identity
+     * @param ip IP address
+     * @return FixedIp
+     */
+    public static FixedIp fixedIp(SubnetId subnetId, IpAddress ip) {
+        return new FixedIp(subnetId, ip);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(subnetId, ip);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof FixedIp) {
+            final FixedIp that = (FixedIp) obj;
+            return Objects.equals(this.subnetId, that.subnetId)
+                    && Objects.equals(this.ip, that.ip);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("subnetId", subnetId).add("ip", ip)
+                .toString();
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java
new file mode 100644 (file)
index 0000000..b18cb95
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Host route dictionaries for the subnet.
+ */
+public interface HostRoute {
+
+    /**
+     * Returns the next hop address.
+     *
+     * @return next hop address
+     */
+    IpAddress nexthop();
+
+    /**
+     * Returns the destination address.
+     *
+     * @return destination address
+     */
+    IpPrefix destination();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java
new file mode 100644 (file)
index 0000000..e96e666
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a physical network identity.
+ */
+public final class PhysicalNetwork {
+
+    private final String physicalNetwork;
+
+    // Public construction is prohibited
+    private PhysicalNetwork(String physicalNetwork) {
+        checkNotNull(physicalNetwork, "PhysicalNetwork cannot be null");
+        this.physicalNetwork = physicalNetwork;
+    }
+
+    /**
+     * Creates a PhysicalNetwork object.
+     *
+     * @param physicalNetwork physical network
+     * @return physical network
+     */
+    public static PhysicalNetwork physicalNetwork(String physicalNetwork) {
+        return new PhysicalNetwork(physicalNetwork);
+    }
+
+    /**
+     * Returns a physicalNetwork.
+     *
+     * @return physical network
+     */
+    public String physicalNetwork() {
+        return physicalNetwork;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(physicalNetwork);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof PhysicalNetwork) {
+            final PhysicalNetwork that = (PhysicalNetwork) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.physicalNetwork,
+                                      that.physicalNetwork);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return physicalNetwork;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java
new file mode 100644 (file)
index 0000000..9ec1dc6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a security group.
+ */
+public final class SecurityGroup {
+    private final String securityGroup;
+
+    /**
+     * Returns the securityGroup.
+     *
+     * @return securityGroup
+     */
+    public String securityGroup() {
+        return securityGroup;
+    }
+    // Public construction is prohibited
+    private SecurityGroup(String securityGroup) {
+        checkNotNull(securityGroup, "SecurityGroup cannot be null");
+        this.securityGroup = securityGroup;
+    }
+
+    /**
+     * Creates a securityGroup using the supplied securityGroup.
+     *
+     * @param securityGroup security group
+     * @return securityGroup
+     */
+    public static SecurityGroup securityGroup(String securityGroup) {
+        return new SecurityGroup(securityGroup);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(securityGroup);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof SecurityGroup) {
+            final SecurityGroup that = (SecurityGroup) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.securityGroup, that.securityGroup);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("securityGroup", securityGroup)
+                .toString();
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java
new file mode 100644 (file)
index 0000000..a076265
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a Segmentation identifier.
+ */
+public final class SegmentationId {
+
+    private final String segmentationId;
+
+    // Public construction is prohibited
+    private SegmentationId(String segmentationId) {
+        checkNotNull(segmentationId, "SegmentationId cannot be null");
+        this.segmentationId = segmentationId;
+    }
+
+    /**
+     * Creates a  SegmentationId object.
+     *
+     * @param segmentationId segmentation identifier
+     * @return SegmentationId
+     */
+    public static SegmentationId segmentationId(String segmentationId) {
+        return new SegmentationId(segmentationId);
+    }
+
+    /**
+     * Returns the segmentation identifier.
+     *
+     * @return segmentationId
+     */
+    public String segmentationId() {
+        return segmentationId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(segmentationId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof SegmentationId) {
+            final SegmentationId that = (SegmentationId) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.segmentationId, that.segmentationId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return segmentationId;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java
new file mode 100644 (file)
index 0000000..f563a78
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Representation of a subnet.
+ */
+public interface Subnet {
+
+    /**
+     * Coarse classification of the type of the ipV6Mode.
+     */
+    enum Mode {
+        DHCPV6_STATEFUL, DHCPV6_STATELESS, SLAAC
+    }
+
+    /**
+     * Returns the subnet identifier.
+     *
+     * @return identifier
+     */
+    SubnetId id();
+
+    /**
+     * Returns the name of the subnet.
+     *
+     * @return subnetName
+     */
+    String subnetName();
+
+    /**
+     * Returns the network identifier.
+     *
+     * @return the network identifier
+     */
+    TenantNetworkId networkId();
+
+    /**
+     * Returns tenant identifier.
+     *
+     * @return the tenant identifier
+     */
+    TenantId tenantId();
+
+    /**
+     * Returns the IP version, which is 4 or 6.
+     *
+     * @return ipVersion
+     */
+    Version ipVersion();
+
+    /**
+     * Returns the cidr.
+     *
+     * @return cidr
+     */
+    IpPrefix cidr();
+
+    /**
+     * Returns the gateway IP address.
+     *
+     * @return gatewayIp
+     */
+    IpAddress gatewayIp();
+
+    /**
+     * Returns true if DHCP is enabled and return false if DHCP is disabled.
+     *
+     * @return true or false
+     */
+    boolean dhcpEnabled();
+
+    /**
+     * Indicates whether this tenantNetwork is shared across all tenants. By
+     * default, only administrative user can change this value.
+     *
+     * @return true or false
+     */
+    boolean shared();
+
+    /**
+     * Returns a collection of hostRoutes.
+     *
+     * @return a collection of hostRoutes
+     */
+    Iterable<HostRoute> hostRoutes();
+
+    /**
+     * Returns the ipV6AddressMode. A valid value is dhcpv6-stateful,
+     * dhcpv6-stateless, or slaac.
+     *
+     * @return ipV6AddressMode whose value is dhcpv6-stateful, dhcpv6-stateless
+     *         or slaac
+     */
+    Mode ipV6AddressMode();
+
+    /**
+     * Returns the ipV6RaMode.A valid value is dhcpv6-stateful,
+     * dhcpv6-stateless, or slaac.
+     *
+     * @return ipV6RaMode whose value is dhcpv6-stateful, dhcpv6-stateless or
+     *         slaac
+     */
+    Mode ipV6RaMode();
+
+    /**
+     * Returns a collection of allocation_pools.
+     *
+     * @return a collection of allocationPools
+     */
+    Iterable<AllocationPool> allocationPools();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java
new file mode 100644 (file)
index 0000000..4bcc332
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+/**
+ * Immutable representation of a subnet identifier.
+ */
+public final class SubnetId {
+
+    private final String subnetId;
+
+    // Public construction is prohibited
+    private SubnetId(String subnetId) {
+        checkNotNull(subnetId, "SubnetId cannot be null");
+        this.subnetId = subnetId;
+    }
+
+    /**
+     * Creates a Subnet identifier.
+     *
+     * @param subnetId the subnet identifier
+     * @return the subnet identifier
+     */
+    public static SubnetId subnetId(String subnetId) {
+        return new SubnetId(subnetId);
+    }
+
+    /**
+     * Returns the subnet identifier.
+     *
+     * @return the subnet identifier
+     */
+    public String subnetId() {
+        return subnetId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(subnetId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof SubnetId) {
+            final SubnetId that = (SubnetId) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.subnetId, that.subnetId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return subnetId;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java
new file mode 100644 (file)
index 0000000..c4d99e4
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a tenant identifier.
+ */
+public final class TenantId {
+
+    private final String tenantId;
+
+    // Public construction is prohibited
+    private TenantId(String tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    /**
+     * Creates a network id using the tenantid.
+     *
+     * @param tenantid network String
+     * @return TenantId
+     */
+    public static TenantId tenantId(String tenantid) {
+        checkNotNull(tenantid, "Tenantid can not be null");
+        return new TenantId(tenantid);
+    }
+
+    /**
+     * Returns the tenant identifier.
+     *
+     * @return the tenant identifier
+     */
+    public String tenantId() {
+        return tenantId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(tenantId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof TenantId) {
+            final TenantId that = (TenantId) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.tenantId, that.tenantId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return tenantId;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java
new file mode 100644 (file)
index 0000000..256352f
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+/**
+ * Representation of the tenantNetwork.
+ */
+public interface TenantNetwork {
+
+    /**
+     * Coarse classification of the state of the tenantNetwork.
+     */
+    enum State {
+        /**
+         * Signifies that a tenantNetwork is currently active.This state means
+         * that this network is available.
+         */
+        ACTIVE,
+        /**
+         * Signifies that a tenantNetwork is currently built.
+         */
+        BUILD,
+        /**
+         * Signifies that a tenantNetwork is currently unavailable.
+         */
+        DOWN,
+        /**
+         * Signifies that a tenantNetwork is currently error.
+         */
+        ERROR
+    }
+
+    /**
+     * Coarse classification of the type of the tenantNetwork.
+     */
+    enum Type {
+        /**
+         * Signifies that a tenantNetwork is local.
+         */
+        LOCAL
+    }
+
+    /**
+     * Returns the tenantNetwork identifier.
+     *
+     * @return tenantNetwork identifier
+     */
+    TenantNetworkId id();
+
+    /**
+     * Returns the tenantNetwork name.
+     *
+     * @return tenantNetwork name
+     */
+    String name();
+
+    /**
+     * Returns the administrative state of the tenantNetwork,which is up(true)
+     * or down(false).
+     *
+     * @return true or false
+     */
+    boolean adminStateUp();
+
+    /**
+     * Returns the tenantNetwork state.
+     *
+     * @return tenant network state
+     */
+    State state();
+
+    /**
+     * Indicates whether this tenantNetwork is shared across all tenants. By
+     * default,only administrative user can change this value.
+     *
+     * @return true or false
+     */
+    boolean shared();
+
+    /**
+     * Returns the UUID of the tenant that will own the tenantNetwork. This
+     * tenant can be different from the tenant that makes the create
+     * tenantNetwork request.
+     *
+     * @return the tenant identifier
+     */
+    TenantId tenantId();
+
+    /**
+     * Returns the routerExternal.Indicates whether this network is externally
+     * accessible.
+     *
+     * @return true or false
+     */
+    boolean routerExternal();
+
+    /**
+     * Returns the tenantNetwork Type.
+     *
+     * @return tenantNetwork Type
+     */
+    Type type();
+
+    /**
+     * Returns the tenantNetwork physical network.
+     *
+     * @return tenantNetwork physical network
+     */
+    PhysicalNetwork physicalNetwork();
+
+    /**
+     * Returns the tenantNetwork segmentation id.
+     *
+     * @return tenantNetwork segmentation id
+     */
+    SegmentationId segmentationId();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java
new file mode 100644 (file)
index 0000000..fbb9e48
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Objects;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Immutable representation of a tenantNetwork identity.
+ */
+public final class TenantNetworkId {
+
+    private final String networkId;
+
+    // Public construction is prohibited
+    private TenantNetworkId(String networkId) {
+        this.networkId = networkId;
+    }
+
+    /**
+     * Creates a TenantNetwork identifier.
+     *
+     * @param networkId tenantNetwork identify string
+     * @return the attached tenantNetwork identifier
+     */
+    public static TenantNetworkId networkId(String networkId) {
+        checkNotNull(networkId, "Networkid cannot be null");
+        return new TenantNetworkId(networkId);
+    }
+
+    /**
+     * Returns tenantNetwork identifier.
+     *
+     * @return the tenantNetwork identifier
+     */
+    public String networkId() {
+        return networkId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(networkId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof TenantNetworkId) {
+            final TenantNetworkId that = (TenantNetworkId) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.networkId, that.networkId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return networkId;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java
new file mode 100644 (file)
index 0000000..d2d7c14
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Representation of the VirtualPort.
+ */
+public interface VirtualPort {
+    /**
+     * Coarse classification of the type of the virtual port.
+     */
+    enum State {
+        /**
+         * Signifies that a virtualPort is currently active,This state mean that
+         * this virtualPort is available.
+         */
+        ACTIVE,
+        /**
+         * Signifies that a virtualPort is currently unavailable.
+         */
+        DOWN;
+    }
+
+    /**
+     * Returns the virtualPort identifier.
+     *
+     * @return virtualPort identifier
+     */
+    VirtualPortId portId();
+
+    /**
+     * Returns the network identifier.
+     *
+     * @return tenantNetwork identifier
+     */
+    TenantNetworkId networkId();
+
+    /**
+     * Returns the symbolic name for the virtualPort.
+     *
+     * @return virtualPort name
+     */
+    String name();
+
+    /**
+     * Returns the administrative status of the port,which is up(true) or
+     * down(false).
+     *
+     * @return true if the administrative status of the port is up
+     */
+    boolean adminStateUp();
+
+    /**
+     * Returns the state.
+     *
+     * @return state
+     */
+    State state();
+
+    /**
+     * Returns the MAC address.
+     *
+     * @return MAC Address
+     */
+    MacAddress macAddress();
+
+    /**
+     * Returns the port tenantId.
+     *
+     * @return port tenantId
+     */
+    TenantId tenantId();
+
+    /**
+     * Returns the device identifier.
+     *
+     * @return deviceId
+     */
+    DeviceId deviceId();
+
+    /**
+     * Returns the identifier of the entity that uses this port.
+     *
+     * @return deviceOwner
+     */
+    String deviceOwner();
+
+    /**
+     * Returns the virtualPort allowedAddressPairs.
+     *
+     * @return virtualPort allowedAddressPairs
+     */
+    Collection<AllowedAddressPair> allowedAddressPairs();
+
+    /**
+     * Returns set of IP addresses for the port, include the IP addresses and subnet
+     * identity.
+     *
+     * @return FixedIps Set of fixedIp
+     */
+    Set<FixedIp> fixedIps();
+
+    /**
+     * Returns the virtualPort bindinghostId.
+     *
+     * @return virtualPort bindinghostId
+     */
+    BindingHostId bindingHostId();
+
+    /**
+     * Returns the virtualPort bindingVnicType.
+     *
+     * @return virtualPort bindingVnicType
+     */
+    String bindingVnicType();
+
+    /**
+     * Returns the virtualPort bindingVifType.
+     *
+     * @return virtualPort bindingVifType
+     */
+    String bindingVifType();
+
+    /**
+     * Returns the virtualPort bindingvifDetail.
+     *
+     * @return virtualPort bindingvifDetail
+     */
+    String bindingVifDetails();
+
+    /**
+     * Returns the security groups.
+     *
+     * @return port security groups
+     */
+    Iterable<SecurityGroup> securityGroups();
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java
new file mode 100644 (file)
index 0000000..3038bdf
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+/**
+ * Immutable representation of a virtual port identifier.
+ */
+public final class VirtualPortId {
+    private final String portId;
+    // Public construction is prohibited
+    private VirtualPortId(String virtualPortId) {
+        checkNotNull(virtualPortId, "VirtualPortId cannot be null");
+        this.portId = virtualPortId;
+    }
+
+    public String portId() {
+        return portId;
+    }
+
+    /**
+     * Creates a virtualPort id using the supplied portId.
+     *
+     * @param portId virtualport identifier
+     * @return VirtualPortId
+     */
+    public static VirtualPortId portId(String portId) {
+        return new VirtualPortId(portId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(portId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof VirtualPortId) {
+            final VirtualPortId that = (VirtualPortId) obj;
+            return this.getClass() == that.getClass()
+                    && Objects.equals(this.portId, that.portId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return portId;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java
new file mode 100644 (file)
index 0000000..bcfdacf
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for creating a TenantNetwork.
+ */
+@Command(scope = "onos", name = "tenantnetwork-create",
+        description = "Supports for creating a TenantNetwork")
+public class TenantNetworkCreateCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "TenantNetwork network id", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Argument(index = 1, name = "tenantID", description = "The tenant id of TenantNetwork",
+            required = true, multiValued = false)
+    String tenantID = null;
+
+    @Argument(index = 2, name = "type", description = "The type of TenantNetwork", required = true,
+            multiValued = false)
+    String type = null;
+
+    @Argument(index = 3, name = "segmentationID", description = "The segmentation id of TenantNetwork",
+            required = true, multiValued = false)
+    String segmentationID = "";
+
+    @Option(name = "-n", aliases = "--name", description = "TenantNetwork name", required = false,
+            multiValued = false)
+    String name = null;
+
+    @Option(name = "-a", aliases = "--adminStateUp", description = "TenantNetwork adminStateUp is true or false",
+            required = false, multiValued = false)
+    boolean adminStateUp = false;
+
+    @Option(name = "-s", aliases = "--state", description = "The state of TenantNetwork",
+            required = false, multiValued = false)
+    String state = null;
+
+    @Option(name = "-d", aliases = "--shared", description = "TenantNetwork is shared or not",
+            required = false, multiValued = false)
+    boolean shared = false;
+
+    @Option(name = "-r", aliases = "--routerExternal",
+            description = "TenantNetwork is routerExternal or not", required = false,
+            multiValued = false)
+    boolean routerExternal = false;
+
+    @Option(name = "-p", aliases = "--physicalNetwork", description = "The physical network of Tenant",
+            required = false, multiValued = false)
+    String physicalNetwork = "";
+
+    @Override
+    protected void execute() {
+        TenantNetworkService service = get(TenantNetworkService.class);
+        TenantNetwork network = new DefaultTenantNetwork(TenantNetworkId.networkId(id), name,
+                                                         adminStateUp,
+                                                         TenantNetwork.State.valueOf(state),
+                                                         shared, TenantId.tenantId(tenantID),
+                                                         routerExternal,
+                                                         TenantNetwork.Type.valueOf(type),
+                                                         PhysicalNetwork.physicalNetwork(physicalNetwork),
+                                                         SegmentationId.segmentationId(segmentationID));
+
+        Set<TenantNetwork> networksSet = Sets.newHashSet(network);
+        service.createNetworks(networksSet);
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java
new file mode 100644 (file)
index 0000000..47ea83c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+/**
+ * Supports for querying TenantNetworks by network id.
+ */
+@Command(scope = "onos", name = "tenantnetworks", description = "Supports for querying"
+        + "tenantNetworks by networkid")
+public class TenantNetworkQueryCommand extends AbstractShellCommand {
+
+    @Option(name = "-i", aliases = "--id", description = "TenantNetwork id", required = false,
+            multiValued = false)
+    String id = null;
+
+    private static final String FMT = "networkId=%s, networkName=%s, segmentationId=%s,"
+            + "tenantId=%s, type=%s, adminStateUp=%s";
+
+    @Override
+    protected void execute() {
+        TenantNetworkService service = get(TenantNetworkService.class);
+        if (id != null) {
+            TenantNetwork network = service.getNetwork(TenantNetworkId.networkId(id));
+            printNetwork(network);
+        } else {
+            Iterable<TenantNetwork> networks = service.getNetworks();
+            for (TenantNetwork network : networks) {
+                printNetwork(network);
+            }
+        }
+    }
+
+    private void printNetwork(TenantNetwork network) {
+        if (network == null) {
+            return;
+        }
+        print(FMT, network.id(), network.name(), network.segmentationId(),
+              network.tenantId(), network.type(), network.adminStateUp());
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java
new file mode 100644 (file)
index 0000000..0ea2285
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for removing a TenantNetwork by network id.
+ */
+@Command(scope = "onos", name = "tenantnetwork-remove", description = "Supports for removing"
+        + " a tenantNetwork by tenantNetworkid")
+public class TenantNetworkRemoveCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "TenantNetwork neutronNetwork Id",
+            required = true, multiValued = false)
+    String id = null;
+
+    @Override
+    protected void execute() {
+        TenantNetworkService service = get(TenantNetworkService.class);
+        Set<TenantNetworkId> networkIds = Sets.newHashSet(TenantNetworkId.networkId(id));
+        service.removeNetworks(networkIds);
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java
new file mode 100644 (file)
index 0000000..2a738f7
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.network;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for updating a TenantNetwork.
+ */
+@Command(scope = "onos", name = "tenantnetwork-update",
+        description = "Supports for updating a TenantNetwork")
+public class TenantNetworkUpdateCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "TenantNetwork network id", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Argument(index = 1, name = "tenantID", description = "The tenant id of TenantNetwork",
+            required = true, multiValued = false)
+    String tenantID = null;
+
+    @Argument(index = 2, name = "type", description = "The type of TenantNetwork", required = true,
+            multiValued = false)
+    String type = null;
+
+    @Argument(index = 3, name = "segmentationID", description = "The segmentation id of TenantNetwork",
+            required = true, multiValued = false)
+    String segmentationID = "";
+
+    @Option(name = "-n", aliases = "--name", description = "TenantNetwork name", required = false,
+            multiValued = false)
+    String name = null;
+
+    @Option(name = "-a", aliases = "--adminStateUp", description = "TenantNetwork adminStateUp is true or false",
+            required = false, multiValued = false)
+    boolean adminStateUp = false;
+
+    @Option(name = "-s", aliases = "--state", description = "The state of TenantNetwork",
+            required = false, multiValued = false)
+    String state = null;
+
+    @Option(name = "-d", aliases = "--shared", description = "TenantNetwork is shared or not",
+            required = false, multiValued = false)
+    boolean shared = false;
+
+    @Option(name = "-r", aliases = "--routerExternal",
+            description = "TenantNetwork is routerExternal or not", required = false,
+            multiValued = false)
+    boolean routerExternal = false;
+
+    @Option(name = "-p", aliases = "--physicalNetwork", description = "The physical network of Tenant",
+            required = false, multiValued = false)
+    String physicalNetwork = "";
+
+    @Override
+    protected void execute() {
+        TenantNetworkService service = get(TenantNetworkService.class);
+        TenantNetwork network = new DefaultTenantNetwork(TenantNetworkId.networkId(id), name,
+                                                         adminStateUp,
+                                                         TenantNetwork.State.valueOf(state),
+                                                         shared, TenantId.tenantId(tenantID),
+                                                         routerExternal,
+                                                         TenantNetwork.Type.valueOf(type),
+                                                         PhysicalNetwork.physicalNetwork(physicalNetwork),
+                                                         SegmentationId.segmentationId(segmentationID));
+
+        Set<TenantNetwork> networksSet = Sets.newHashSet();
+        networksSet.add(network);
+        service.updateNetworks(networksSet);
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java
new file mode 100644 (file)
index 0000000..1622c80
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Command line interface for tenant networks.
+ */
+package org.onosproject.vtnrsc.cli.network;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java
new file mode 100644 (file)
index 0000000..5623640
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.Subnet.Mode;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for creating a subnet.
+ */
+@Command(scope = "onos", name = "subnet-create", description = "Supports for creating a subnet")
+public class SubnetCreateCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "Subnet Id", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Argument(index = 1, name = "subnetName", description = "Subnet String name", required = true,
+            multiValued = false)
+    String subnetName = null;
+
+    @Argument(index = 2, name = "networkId", description = "Subnet Network Id", required = true,
+            multiValued = false)
+    String networkId = null;
+
+    @Argument(index = 3, name = "tenantId", description = "Subnet Tenant Id", required = true,
+            multiValued = false)
+    String tenantId = null;
+
+    @Option(name = "-i", aliases = "--ipVersion", description = "Subnet Version ipVersion",
+            required = false, multiValued = false)
+    Version ipVersion = null;
+
+    @Option(name = "-c", aliases = "--cidr", description = "Subnet IpPrefix cidr",
+            required = false, multiValued = false)
+    String cidr = "0.0.0.0/0";
+
+    @Option(name = "-g", aliases = "--gatewayIp", description = "Subnet IpAddress gatewayIp",
+            required = false, multiValued = false)
+    String gatewayIp = "0.0.0.0";
+
+    @Option(name = "-d", aliases = "--dhcpEnabled", description = "Subnet boolean dhcpEnabled",
+            required = false, multiValued = false)
+    boolean dhcpEnabled = false;
+
+    @Option(name = "-s", aliases = "--shared", description = "Subnet boolean shared",
+            required = false, multiValued = false)
+    boolean shared = false;
+
+    @Option(name = "-m", aliases = "--ipV6AddressMode",
+            description = "Subnet Mode ipV6AddressMode", required = false, multiValued = false)
+    String ipV6AddressMode = null;
+
+    @Option(name = "-r", aliases = "--ipV6RaMode", description = "Subnet Mode ipV6RaMode",
+            required = false, multiValued = false)
+    String ipV6RaMode = null;
+
+    @Option(name = "-h", aliases = "--hostRoutes", description = "Subnet jsonnode hostRoutes",
+            required = false, multiValued = false)
+    Set<HostRoute> hostRoutes = Sets.newHashSet();
+
+    @Option(name = "-a", aliases = "--allocationPools",
+            description = "Subnet jsonnode allocationPools", required = false, multiValued = false)
+    Set<AllocationPool> allocationPools = Sets.newHashSet();
+
+    @Override
+    protected void execute() {
+        SubnetService service = get(SubnetService.class);
+        if (id == null || networkId == null || tenantId == null) {
+            print(null, "id,networkId,tenantId can not be null");
+            return;
+        }
+        Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName,
+                                          TenantNetworkId.networkId(networkId),
+                                          TenantId.tenantId(tenantId), ipVersion,
+                                          cidr == null ? null : IpPrefix.valueOf(cidr),
+                                          gatewayIp == null ? null : IpAddress.valueOf(gatewayIp),
+                                          dhcpEnabled, shared, hostRoutes,
+                                          ipV6AddressMode == null ? null : Mode.valueOf(ipV6AddressMode),
+                                          ipV6RaMode == null ? null : Mode.valueOf(ipV6RaMode),
+                                          allocationPools);
+
+        Set<Subnet> subnetsSet = Sets.newHashSet(subnet);
+        service.createSubnets(subnetsSet);
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java
new file mode 100644 (file)
index 0000000..f5a94f0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+/**
+ * Supports for querying a subnet.
+ */
+@Command(scope = "onos", name = "subnets", description = "Supports for querying a subnet")
+public class SubnetQueryCommand extends AbstractShellCommand {
+
+    @Option(name = "-i", aliases = "--id", description = "Subnet id", required = false,
+            multiValued = false)
+    String id = null;
+
+    private static final String FMT = "subnetId=%s, networkId=%s, subnetName=%s,"
+            + "tenantId=%s, cidr=%s, dhcpEnabled=%s, gatewayIp=%s," + "ipVersion=%s";
+
+    @Override
+    protected void execute() {
+        SubnetService service = get(SubnetService.class);
+        if (id != null) {
+            Subnet subnet = service.getSubnet(SubnetId.subnetId(id));
+            printSubnet(subnet);
+        } else {
+            Iterable<Subnet> subnets = service.getSubnets();
+            if (subnets == null) {
+                return;
+            }
+            for (Subnet subnet : subnets) {
+                printSubnet(subnet);
+            }
+        }
+    }
+
+    private void printSubnet(Subnet subnet) {
+        print(FMT, subnet.id(), subnet.networkId(), subnet.subnetName(),
+              subnet.tenantId(), subnet.cidr(), subnet.dhcpEnabled(), subnet
+                      .gatewayIp(), subnet.ipVersion());
+
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java
new file mode 100644 (file)
index 0000000..241af87
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for removing a subnet.
+ */
+@Command(scope = "onos", name = "subnet-remove", description = "Supports for removing a subnet")
+public class SubnetRemoveCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "Subnet SubnetId Id", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Override
+    protected void execute() {
+        SubnetService service = get(SubnetService.class);
+        Set<SubnetId> subnetsSet = Sets.newHashSet();
+        subnetsSet.add(SubnetId.subnetId(id));
+        service.removeSubnets(subnetsSet);
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java
new file mode 100644 (file)
index 0000000..b0578a1
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.Subnet.Mode;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for updating a subnet.
+ */
+@Command(scope = "onos", name = "subnet-update", description = "Supports for updating a subnet")
+public class SubnetUpdateCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "Subnet Id", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Argument(index = 1, name = "subnetName", description = "Subnet String name", required = true,
+            multiValued = false)
+    String subnetName = null;
+
+    @Argument(index = 2, name = "networkId", description = "Subnet Network Id", required = true,
+            multiValued = false)
+    String networkId = null;
+
+    @Argument(index = 3, name = "tenantId", description = "Subnet Tenant Id", required = true,
+            multiValued = false)
+    String tenantId = null;
+
+    @Option(name = "-i", aliases = "--ipVersion", description = "Subnet Version ipVersion",
+            required = false, multiValued = false)
+    Version ipVersion = null;
+
+    @Option(name = "-c", aliases = "--cidr", description = "Subnet IpPrefix cidr", required = false,
+            multiValued = false)
+    String cidr = "0.0.0.0/0";
+
+    @Option(name = "-g", aliases = "--gatewayIp", description = "Subnet IpAddress gatewayIp",
+            required = false, multiValued = false)
+    String gatewayIp = "0.0.0.0";
+
+    @Option(name = "-d", aliases = "--dhcpEnabled", description = "Subnet boolean dhcpEnabled",
+            required = false, multiValued = false)
+    boolean dhcpEnabled = false;
+
+    @Option(name = "-s", aliases = "--shared", description = "Subnet boolean shared", required = false,
+            multiValued = false)
+    boolean shared = false;
+
+    @Option(name = "-m", aliases = "--ipV6AddressMode", description = "Subnet Mode ipV6AddressMode",
+            required = false, multiValued = false)
+    String ipV6AddressMode = null;
+
+    @Option(name = "-r", aliases = "--ipV6RaMode", description = "Subnet Mode ipV6RaMode",
+            required = false, multiValued = false)
+    String ipV6RaMode = null;
+
+    @Option(name = "-h", aliases = "--hostRoutes", description = "Subnet jsonnode hostRoutes",
+            required = false, multiValued = false)
+    Set<HostRoute> hostRoutes = Sets.newHashSet();
+
+    @Option(name = "-a", aliases = "--allocationPools",
+            description = "Subnet jsonnode allocationPools", required = false, multiValued = false)
+    Set<AllocationPool> allocationPools = Sets.newHashSet();
+
+    @Override
+    protected void execute() {
+        SubnetService service = get(SubnetService.class);
+        if (id == null || networkId == null || tenantId == null) {
+            print(null, "id,networkId,tenantId can not be null");
+            return;
+        }
+        Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName,
+                                          TenantNetworkId.networkId(networkId),
+                                          TenantId.tenantId(tenantId), ipVersion,
+                                          cidr == null ? null : IpPrefix.valueOf(cidr),
+                                          gatewayIp == null ? null : IpAddress.valueOf(gatewayIp),
+                                          dhcpEnabled, shared, hostRoutes,
+                                          ipV6AddressMode == null ? null : Mode.valueOf(ipV6AddressMode),
+                                          ipV6RaMode == null ? null : Mode.valueOf(ipV6RaMode),
+                                          allocationPools);
+        Set<Subnet> subnetsSet = Sets.newHashSet();
+        subnetsSet.add(subnet);
+        service.updateSubnets(subnetsSet);
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java
new file mode 100644 (file)
index 0000000..b3a2ff5
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Command line interface for subnets.
+ */
+package org.onosproject.vtnrsc.cli.subnet;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java
new file mode 100644 (file)
index 0000000..4c555e3
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for creating a virtualPort.
+ */
+@Command(scope = "onos", name = "virtualport-create",
+        description = "Supports for creating a virtualPort.")
+public class VirtualPortCreateCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "virtualPort id.", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Argument(index = 1, name = "networkId", description = "network id.", required = true,
+            multiValued = false)
+    String networkId = null;
+
+    @Argument(index = 2, name = "name", description = "virtualPort name.", required = true,
+            multiValued = false)
+    String name = null;
+
+    @Argument(index = 3, name = "tenantId", description = "tenant id.", required = true,
+            multiValued = false)
+    String tenantId = null;
+
+    @Argument(index = 4, name = "deviceId", description = "device id.", required = true,
+            multiValued = false)
+    String deviceId = null;
+
+    @Option(name = "-a", aliases = "--adminStateUp",
+            description = "administrative status of the virtualPort which is true or false.",
+            required = false, multiValued = false)
+    Boolean adminStateUp = false;
+
+    @Option(name = "-s", aliases = "--state", description = "virtualPort state.", required = false,
+            multiValued = false)
+    String state = null;
+
+    @Option(name = "-m", aliases = "--macAddress", description = "MAC address.", required = false,
+            multiValued = false)
+    String macAddress = "";
+
+    @Option(name = "-d", aliases = "--deviceOwner", description = "ID of the entity that uses this "
+            + "virtualPort.", required = false, multiValued = false)
+    String deviceOwner = null;
+
+    @Option(name = "-f", aliases = "--fixedIp",
+            description = "The IP address for the port,include the IP address "
+                    + "and subnet identity.", required = false, multiValued = false)
+    FixedIp fixedIp = null;
+
+    @Option(name = "-i", aliases = "--bindingHostId", description = "virtualPort bindingHostId.",
+            required = false, multiValued = false)
+    String bindingHostId = null;
+
+    @Option(name = "-t", aliases = "--bindingvnicType", description = "virtualPort bindingvnicType.",
+            required = false, multiValued = false)
+    String bindingvnicType = null;
+
+    @Option(name = "-v", aliases = "--bindingvifType", description = "virtualPort bindingvifType.",
+            required = false, multiValued = false)
+    String bindingvifType = null;
+
+    @Option(name = "-b", aliases = "--bindingvnicDetails",
+            description = "virtualPort bindingvnicDetails.", required = false, multiValued = false)
+    String bindingvnicDetails = null;
+
+    @Option(name = "-l", aliases = "--allowedAddress", description = "virtual allowedAddressPair.",
+            required = false, multiValued = false)
+    Set<AllowedAddressPair> allowedAddressPairs = Sets.newHashSet();
+
+    @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.",
+            required = false, multiValued = false)
+    Set<SecurityGroup> securityGroups = Sets.newHashSet();
+
+    @Override
+    protected void execute() {
+        Map<String, String> strMap = Maps.newHashMap();
+        strMap.putIfAbsent("name", name);
+        strMap.putIfAbsent("deviceOwner", deviceOwner);
+        strMap.putIfAbsent("bindingvnicType", bindingvnicType);
+        strMap.putIfAbsent("bindingvifType", bindingvifType);
+        strMap.putIfAbsent("bindingvnicDetails", bindingvnicDetails);
+        VirtualPortService service = get(VirtualPortService.class);
+        VirtualPort virtualPort = new DefaultVirtualPort(VirtualPortId.portId(id),
+                                       TenantNetworkId.networkId(networkId),
+                                       false, strMap, VirtualPort.State.ACTIVE,
+                                       MacAddress.valueOf(macAddress),
+                                       TenantId.tenantId(tenantId),
+                                       DeviceId.deviceId(deviceId), Sets.newHashSet(fixedIp),
+                                       BindingHostId.bindingHostId(bindingHostId),
+                                       allowedAddressPairs, securityGroups);
+        Set<VirtualPort> virtualPorts = Sets.newHashSet(virtualPort);
+        service.createPorts(virtualPorts);
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java
new file mode 100644 (file)
index 0000000..47126d1
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Collection;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+/**
+ * Supports for querying virtualPorts.
+ */
+@Command(scope = "onos", name = "virtualports", description = "Supports for querying virtualPorts.")
+public class VirtualPortQueryCommand extends AbstractShellCommand {
+
+    @Option(name = "-v", aliases = "--vPortId", description = "virtualPort ID.", required = false,
+            multiValued = false)
+    String vPortId;
+
+    @Option(name = "-n", aliases = "--networkId", description = "network ID.", required = false,
+            multiValued = false)
+    String networkId;
+
+    @Option(name = "-d", aliases = "--deviceId", description = "device ID.", required = false,
+            multiValued = false)
+    String deviceId;
+
+    @Option(name = "-t", aliases = "--tenantId", description = "tenant ID.", required = false,
+            multiValued = false)
+    String tenantId;
+
+    private static final String FMT = "virtualPortId=%s, networkId=%s, name=%s,"
+            + " tenantId=%s, deviceId=%s, adminStateUp=%s, state=%s,"
+            + " macAddress=%s, deviceOwner=%s, fixedIp=%s, bindingHostId=%s,"
+            + " bindingvnicType=%s, bindingvifType=%s, bindingvnicDetails=%s,"
+            + " allowedAddress=%s, securityGroups=%s";
+
+    @Override
+    protected void execute() {
+        VirtualPortService service = get(VirtualPortService.class);
+        if (vPortId != null && networkId == null && deviceId == null && tenantId == null) {
+            VirtualPort port = service.getPort(VirtualPortId.portId(vPortId));
+            printPort(port);
+        } else if (vPortId == null && networkId != null && deviceId == null && tenantId == null) {
+            Collection<VirtualPort> ports = service.getPorts(TenantNetworkId.networkId(networkId));
+            printPorts(ports);
+        } else if (vPortId == null && networkId == null && deviceId != null && tenantId == null) {
+            Collection<VirtualPort> ports = service.getPorts(DeviceId.deviceId(deviceId));
+            printPorts(ports);
+        } else if (vPortId == null && networkId == null && deviceId == null && tenantId != null) {
+            Collection<VirtualPort> ports = service.getPorts(DeviceId.deviceId(tenantId));
+            printPorts(ports);
+        } else if (vPortId == null && networkId == null && deviceId == null && tenantId == null) {
+            Collection<VirtualPort> ports = service.getPorts();
+            printPorts(ports);
+        } else {
+            print("cannot input more than one parameter");
+        }
+
+    }
+
+    private void printPorts(Collection<VirtualPort> ports) {
+        for (VirtualPort port : ports) {
+            printPort(port);
+        }
+    }
+
+    private void printPort(VirtualPort port) {
+        print(FMT, port.portId(), port.networkId(), port.name(), port.tenantId(), port.deviceId(),
+              port.adminStateUp(), port.state(), port.macAddress(), port.deviceOwner(), port
+                      .fixedIps(), port.bindingHostId(), port.bindingVnicType(),
+              port.bindingVifType(), port.bindingVifDetails(), port.allowedAddressPairs(),
+              port.securityGroups());
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java
new file mode 100644 (file)
index 0000000..1a3cb4f
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for removing a virtualPort.
+ */
+@Command(scope = "onos", name = "virtualport-remove",
+        description = "Supports for removing a virtualPort.")
+public class VirtualPortRemoveCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "virtualPort id.", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Override
+    protected void execute() {
+        VirtualPortService service = get(VirtualPortService.class);
+        Set<VirtualPortId> virtualPorts = Sets.newHashSet(VirtualPortId.portId(id));
+        service.removePorts(virtualPorts);
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java
new file mode 100644 (file)
index 0000000..6df4b23
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Supports for updating a virtualPort.
+ */
+@Command(scope = "onos", name = "virtualport-update",
+        description = "Supports for updating a virtualPort.")
+public class VirtualPortUpdateCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "virtualPort id.", required = true,
+            multiValued = false)
+    String id = null;
+
+    @Argument(index = 1, name = "networkId", description = "network id.", required = true,
+            multiValued = false)
+    String networkId = null;
+
+    @Argument(index = 2, name = "name", description = "virtualPort name.", required = true,
+            multiValued = false)
+    String name = null;
+
+    @Argument(index = 3, name = "tenantId", description = "tenant id.", required = true,
+            multiValued = false)
+    String tenantId = null;
+
+    @Argument(index = 4, name = "deviceId", description = "device id.", required = true,
+            multiValued = false)
+    String deviceId = null;
+
+    @Option(name = "-a", aliases = "--adminStateUp",
+            description = "administrative status of the virtualPort which is true or false.",
+            required = false, multiValued = false)
+    Boolean adminStateUp = false;
+
+    @Option(name = "-s", aliases = "--state", description = "virtualPort state.", required = false,
+            multiValued = false)
+    String state = null;
+
+    @Option(name = "-m", aliases = "--macAddress", description = "MAC address.", required = false,
+            multiValued = false)
+    String macAddress = "";
+
+    @Option(name = "-d", aliases = "--deviceOwner",
+            description = "ID of the entity that uses this " + "virtualPort.", required = false,
+            multiValued = false)
+    String deviceOwner = null;
+
+    @Option(name = "-f", aliases = "--fixedIp",
+            description = "The IP address for the port,include the IP address "
+                    + "and subnet identity.", required = false, multiValued = false)
+    FixedIp fixedIp = null;
+
+    @Option(name = "-i", aliases = "--bindingHostId", description = "virtualPort bindingHostId.",
+            required = false, multiValued = false)
+    String bindingHostId = "";
+
+    @Option(name = "-t", aliases = "--bindingvnicType",
+            description = "virtualPort bindingvnicType.", required = false, multiValued = false)
+    String bindingvnicType = null;
+
+    @Option(name = "-v", aliases = "--bindingvifType", description = "virtualPort bindingvifType.",
+            required = false, multiValued = false)
+    String bindingvifType = null;
+
+    @Option(name = "-b", aliases = "--bindingvnicDetails",
+            description = "virtualPort bindingvnicDetails.", required = false, multiValued = false)
+    String bindingvnicDetails = null;
+
+    @Option(name = "-l", aliases = "--allowedAddress", description = "virtual allowedAddressPair.",
+            required = false, multiValued = false)
+    Set<AllowedAddressPair> allowedAddressPairs = Sets.newHashSet();
+
+    @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.",
+            required = false, multiValued = false)
+    Set<SecurityGroup> securityGroups = Sets.newHashSet();
+
+    @Override
+    protected void execute() {
+        VirtualPortService service = get(VirtualPortService.class);
+        Map<String, String> strMap = Maps.newHashMap();
+        strMap.putIfAbsent("name", name);
+        strMap.putIfAbsent("deviceOwner", deviceOwner);
+        strMap.putIfAbsent("bindingvnicType", bindingvnicType);
+        strMap.putIfAbsent("bindingvifType", bindingvifType);
+        strMap.putIfAbsent("bindingvnicDetails", bindingvnicDetails);
+        VirtualPort virtualPort = new DefaultVirtualPort(VirtualPortId.portId(id),
+                                                         TenantNetworkId.networkId(networkId),
+                                                         false, strMap, VirtualPort.State.ACTIVE,
+                                                         MacAddress.valueOf(macAddress),
+                                                         TenantId.tenantId(tenantId),
+                                                         DeviceId.deviceId(deviceId), Sets.newHashSet(fixedIp),
+                                                         BindingHostId.bindingHostId(bindingHostId),
+                                                         allowedAddressPairs, securityGroups);
+        Set<VirtualPort> virtualPorts = Sets.newHashSet(virtualPort);
+        service.updatePorts(virtualPorts);
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java
new file mode 100644 (file)
index 0000000..fac214a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Command line interface for virtual ports.
+ */
+package org.onosproject.vtnrsc.cli.virtualport;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java
new file mode 100644 (file)
index 0000000..b245fb1
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN resources that used by virtual tenant network.
+ */
+package org.onosproject.vtnrsc;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java
new file mode 100644 (file)
index 0000000..82eb961
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.subnet;
+
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+public interface SubnetService {
+    /**
+     * Returns the subnet with the specified identifier.
+     *
+     * @param subnetId subnet identifier
+     * @return true or false
+     */
+    boolean exists(SubnetId subnetId);
+    /**
+     * Returns a collection of the currently known subnets.
+     *
+     * @return iterable collection of subnets
+     */
+    Iterable<Subnet> getSubnets();
+
+    /**
+     * Returns the subnet with the specified identifier.
+     *
+     * @param subnetId subnet identifier
+     * @return subnet or null if one with the given identifier is not known
+     */
+    Subnet getSubnet(SubnetId subnetId);
+    /**
+     * Creates new subnets.
+     *
+     * @param subnets the iterable collection of subnets
+     * @return true  if the identifier subnet has been created right
+     */
+    boolean createSubnets(Iterable<Subnet> subnets);
+
+    /**
+     * Updates existing subnets.
+     *
+     * @param subnets the iterable collection of subnets
+     * @return true if all subnets were updated successfully
+     */
+    boolean updateSubnets(Iterable<Subnet> subnets);
+
+    /**
+     * Administratively removes the specified subnets from the store.
+     *
+     * @param subnetIds the iterable collection of  subnets identifier
+     * @return true if remove identifier subnets successfully
+     */
+    boolean removeSubnets(Iterable<SubnetId> subnetIds);
+
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java
new file mode 100644 (file)
index 0000000..890beb2
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.subnet.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultAllocationPool;
+import org.onosproject.vtnrsc.DefaultHostRoute;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of the Subnet service.
+ */
+@Component(immediate = true)
+@Service
+public class SubnetManager implements SubnetService {
+
+    private static final String SUBNET_ID_NULL = "Subnet ID cannot be null";
+    private static final String SUBNET_NOT_NULL = "Subnet cannot be null";
+    private static final String SUBNET = "vtn-subnet-store";
+    private static final String VTNRSC_APP = "org.onosproject.vtnrsc";
+
+
+    private final Logger log = getLogger(getClass());
+
+    protected Map<SubnetId, Subnet> subnetStore;
+    protected ApplicationId appId;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TenantNetworkService tenantNetworkService;
+
+    @Activate
+    public void activate() {
+
+        appId = coreService.registerApplication(VTNRSC_APP);
+
+        subnetStore = storageService.<SubnetId, Subnet>consistentMapBuilder()
+                .withName(SUBNET)
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+                                                 Subnet.class,
+                                                 SubnetId.class,
+                                                 TenantNetworkId.class,
+                                                 TenantId.class,
+                                                 HostRoute.class,
+                                                 DefaultHostRoute.class,
+                                                 Subnet.Mode.class,
+                                                 AllocationPool.class,
+                                                 DefaultAllocationPool.class,
+                                                 DefaultSubnet.class,
+                                                 IpAddress.Version.class))
+                .build().asJavaMap();
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public Iterable<Subnet> getSubnets() {
+        return Collections.unmodifiableCollection(subnetStore.values());
+    }
+
+    @Override
+    public Subnet getSubnet(SubnetId subnetId) {
+        checkNotNull(subnetId, SUBNET_ID_NULL);
+        return subnetStore.get(subnetId);
+    }
+
+    @Override
+    public boolean exists(SubnetId subnetId) {
+        checkNotNull(subnetId, SUBNET_ID_NULL);
+        return subnetStore.containsKey(subnetId);
+    }
+
+    @Override
+    public boolean createSubnets(Iterable<Subnet> subnets) {
+        checkNotNull(subnets, SUBNET_NOT_NULL);
+        for (Subnet subnet : subnets) {
+            if (!tenantNetworkService.exists(subnet.networkId())) {
+                log.debug("The network identifier that the subnet {} belong to is not exist",
+                          subnet.networkId().toString(), subnet.id().toString());
+                return false;
+            }
+            subnetStore.put(subnet.id(), subnet);
+            if (!subnetStore.containsKey(subnet.id())) {
+                log.debug("The identified subnet whose identifier is {}  create failed",
+                          subnet.id().toString());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean updateSubnets(Iterable<Subnet> subnets) {
+        checkNotNull(subnets, SUBNET_NOT_NULL);
+        if (subnets != null) {
+            for (Subnet subnet : subnets) {
+                if (!subnetStore.containsKey(subnet.id())) {
+                    log.debug("The subnet is not exist whose identifier is {}",
+                              subnet.id().toString());
+                    return false;
+                }
+
+                subnetStore.put(subnet.id(), subnet);
+
+                if (!subnet.equals(subnetStore.get(subnet.id()))) {
+                    log.debug("The subnet is updated failed whose identifier is {}",
+                              subnet.id().toString());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean removeSubnets(Iterable<SubnetId> subnetIds) {
+        checkNotNull(subnetIds, SUBNET_ID_NULL);
+        if (subnetIds != null) {
+            for (SubnetId subnetId : subnetIds) {
+                subnetStore.remove(subnetId);
+                if (subnetStore.containsKey(subnetId)) {
+                    log.debug("The subnet created is failed whose identifier is {}",
+                              subnetId.toString());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java
new file mode 100644 (file)
index 0000000..79040d8
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Provides implementation of the Subnet service.
+ */
+package org.onosproject.vtnrsc.subnet.impl;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java
new file mode 100644 (file)
index 0000000..7b2bdb9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+package org.onosproject.vtnrsc.subnet;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java
new file mode 100644 (file)
index 0000000..e246cc4
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.tenantnetwork;
+
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+
+/**
+ * Service for interacting with the inventory of tenantNetwork.
+ */
+public interface TenantNetworkService {
+
+    /**
+     * Returns if the tenantNetwork is existed.
+     *
+     * @param networkId tenantNetwork identifier
+     * @return true or false if one with the given identifier exists.
+     */
+    boolean exists(TenantNetworkId networkId);
+
+    /**
+     * Returns the number of tenantNetwork known to the system.
+     *
+     * @return number of tenantNetwork.
+     */
+    int getNetworkCount();
+
+    /**
+     * Returns an iterable collection of the currently known tenantNetwork.
+     *
+     * @return collection of tenantNetwork.
+     */
+    Iterable<TenantNetwork> getNetworks();
+
+    /**
+     * Returns the tenantNetwork with the identifier.
+     *
+     * @param networkId TenantNetwork identifier
+     * @return TenantNetwork or null if one with the given identifier is not
+     *         known.
+     */
+    TenantNetwork getNetwork(TenantNetworkId networkId);
+
+    /**
+     * Creates tenantNetworks by networks.
+     *
+     * @param networks the collection of tenantNetworks
+     * @return true if all given identifiers created successfully.
+     */
+    boolean createNetworks(Iterable<TenantNetwork> networks);
+
+    /**
+     * Updates tenantNetworks by tenantNetworks.
+     *
+     * @param networks the collection of tenantNetworks
+     * @return true if all given identifiers updated successfully.
+     */
+    boolean updateNetworks(Iterable<TenantNetwork> networks);
+
+    /**
+     * Deletes tenantNetwork by tenantNetworkIds.
+     *
+     * @param networksIds the collection of tenantNetworkIds
+     * @return true if the specified tenantNetworks deleted successfully.
+     */
+    boolean removeNetworks(Iterable<TenantNetworkId> networksIds);
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java
new file mode 100644 (file)
index 0000000..0dfc99e
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.tenantnetwork.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of the tenantNetworkService.
+ */
+@Component(immediate = true)
+@Service
+public class TenantNetworkManager implements TenantNetworkService {
+
+    private static final String NETWORK_ID_NULL = "Network ID cannot be null";
+    private static final String NETWORK_NOT_NULL = "Network ID cannot be null";
+    private static final String TENANTNETWORK = "vtn-tenant-network-store";
+    private static final String VTNRSC_APP = "org.onosproject.vtnrsc";
+
+    protected Map<TenantNetworkId, TenantNetwork> networkIdAsKeyStore;
+    protected ApplicationId appId;
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+
+    @Activate
+    public void activate() {
+
+        appId = coreService.registerApplication(VTNRSC_APP);
+
+        networkIdAsKeyStore = storageService.<TenantNetworkId, TenantNetwork>consistentMapBuilder()
+                .withName(TENANTNETWORK)
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+                                                 TenantNetworkId.class,
+                                                 DefaultTenantNetwork.class,
+                                                 TenantNetwork.State.class,
+                                                 TenantId.class,
+                                                 TenantNetwork.Type.class,
+                                                 PhysicalNetwork.class,
+                                                 SegmentationId.class))
+                .build().asJavaMap();
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public boolean exists(TenantNetworkId networkId) {
+        checkNotNull(networkId, NETWORK_ID_NULL);
+        return networkIdAsKeyStore.containsKey(networkId);
+    }
+
+    @Override
+    public int getNetworkCount() {
+        return networkIdAsKeyStore.size();
+    }
+
+    @Override
+    public Iterable<TenantNetwork> getNetworks() {
+        return Collections.unmodifiableCollection(networkIdAsKeyStore.values());
+    }
+
+    @Override
+    public TenantNetwork getNetwork(TenantNetworkId networkId) {
+        checkNotNull(networkId, NETWORK_ID_NULL);
+        return networkIdAsKeyStore.get(networkId);
+    }
+
+    @Override
+    public boolean createNetworks(Iterable<TenantNetwork> networks) {
+        checkNotNull(networks, NETWORK_NOT_NULL);
+        for (TenantNetwork network : networks) {
+            networkIdAsKeyStore.put(network.id(), network);
+            if (!networkIdAsKeyStore.containsKey(network.id())) {
+                log.debug("The tenantNetwork is created failed which identifier was {}", network.id()
+                        .toString());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean updateNetworks(Iterable<TenantNetwork> networks) {
+        checkNotNull(networks, NETWORK_NOT_NULL);
+        for (TenantNetwork network : networks) {
+            if (!networkIdAsKeyStore.containsKey(network.id())) {
+                log.debug("The tenantNetwork is not exist whose identifier was {} ",
+                          network.id().toString());
+                return false;
+            }
+
+            networkIdAsKeyStore.put(network.id(), network);
+
+            if (!network.equals(networkIdAsKeyStore.get(network.id()))) {
+                log.debug("The tenantNetwork is updated failed whose identifier was {} ",
+                          network.id().toString());
+                return false;
+            }
+
+        }
+        return true;
+    }
+
+    @Override
+    public boolean removeNetworks(Iterable<TenantNetworkId> networkIds) {
+        checkNotNull(networkIds, NETWORK_NOT_NULL);
+        for (TenantNetworkId networkId : networkIds) {
+            networkIdAsKeyStore.remove(networkId);
+            if (networkIdAsKeyStore.containsKey(networkId)) {
+                log.debug("The tenantNetwork is removed failed whose identifier was {}",
+                          networkId.toString());
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java
new file mode 100644 (file)
index 0000000..f381fda
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of service for interacting with the inventory of tenant networks.
+ */
+package org.onosproject.vtnrsc.tenantnetwork.impl;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java
new file mode 100644 (file)
index 0000000..1489c97
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of tenant networks.
+ */
+package org.onosproject.vtnrsc.tenantnetwork;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java
new file mode 100644 (file)
index 0000000..6f3cf65
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.tunnel;
+
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+public interface TunnelConfigService {
+    /**
+     * Returns the subnet with the specified identifier.
+     *
+     * @param subnetId subnet identifier
+     * @return true or false
+     */
+    boolean exists(SubnetId subnetId);
+    /**
+     * Returns a collection of the currently known subnets.
+     *
+     * @return iterable collection of subnets
+     */
+    Iterable<Subnet> getSubnets();
+
+    /**
+     * Returns the subnet with the specified identifier.
+     *
+     * @param subnetId subnet identifier
+     * @return subnet or null if one with the given identifier is not known
+     */
+    Subnet getSubnet(SubnetId subnetId);
+    /**
+     * Creates new subnets.
+     *
+     * @param subnets the iterable collection of subnets
+     * @return true  if the identifier subnet has been created right
+     */
+    boolean createSubnets(Iterable<Subnet> subnets);
+
+    /**
+     * Updates existing subnets.
+     *
+     * @param subnets the iterable collection of subnets
+     * @return true if all subnets were updated successfully
+     */
+    boolean updateSubnets(Iterable<Subnet> subnets);
+
+    /**
+     * Administratively removes the specified subnets from the store.
+     *
+     * @param subnetIds the iterable collection of  subnets identifier
+     * @return true if remove identifier subnets successfully
+     */
+    boolean removeSubnets(Iterable<SubnetId> subnetIds);
+
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java
new file mode 100644 (file)
index 0000000..3a84e6e
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of subnets.
+ */
+package org.onosproject.vtnrsc.tunnel;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java
new file mode 100644 (file)
index 0000000..05ebccf
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.virtualport;
+
+import java.util.Collection;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+
+/**
+ * Service for interacting with the inventory of virtualPort.
+ */
+public interface VirtualPortService {
+    /**
+     * Returns if the virtualPort is existed.
+     *
+     * @param virtualPortId virtualPort identifier
+     * @return true or false if one with the given identifier is not existed.
+     */
+    boolean exists(VirtualPortId virtualPortId);
+
+    /**
+     * Returns the virtualPort with the identifier.
+     *
+     * @param virtualPortId virtualPort ID
+     * @return VirtualPort or null if one with the given ID is not know.
+     */
+    VirtualPort getPort(VirtualPortId virtualPortId);
+
+    /**
+     * Returns the collection of the currently known virtualPort.
+     * @return collection of VirtualPort.
+     */
+    Collection<VirtualPort> getPorts();
+
+    /**
+     * Returns the collection of the virtualPorts associated with the networkId.
+     *
+     * @param networkId  the network identifer
+     * @return collection of virtualPort.
+     */
+    Collection<VirtualPort> getPorts(TenantNetworkId networkId);
+
+    /**
+     * Returns the collection of the virtualPorts associated with the tenantId.
+     *
+     * @param tenantId   the tenant identifier
+     * @return collection of virtualPorts.
+     */
+    Collection<VirtualPort> getPorts(TenantId tenantId);
+
+    /**
+     * Returns the collection of the virtualPorts associated with the deviceId.
+     *
+     * @param deviceId   the device identifier
+     * @return collection of virtualPort.
+     */
+    Collection<VirtualPort> getPorts(DeviceId deviceId);
+
+    /**
+     * Creates virtualPorts by virtualPorts.
+     *
+     * @param virtualPorts the iterable collection of virtualPorts
+     * @return true if all given identifiers created successfully.
+     */
+    boolean createPorts(Iterable<VirtualPort> virtualPorts);
+
+    /**
+     * Updates virtualPorts by virtualPorts.
+     *
+     * @param virtualPorts the iterable  collection of virtualPorts
+     * @return true if all given identifiers updated successfully.
+     */
+    boolean updatePorts(Iterable<VirtualPort> virtualPorts);
+
+    /**
+     * Deletes virtualPortIds by virtualPortIds.
+     *
+     * @param virtualPortIds the iterable collection of virtualPort identifiers
+     * @return true or false if one with the given identifier to delete is
+     *         successfully.
+     */
+    boolean removePorts(Iterable<VirtualPortId> virtualPortIds);
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java
new file mode 100644 (file)
index 0000000..926809c
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.virtualport.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides implementation of the VirtualPort APIs.
+ */
+@Component(immediate = true)
+@Service
+public class VirtualPortManager implements VirtualPortService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String VIRTUALPORT = "vtn-virtual-port";
+    private static final String VTNRSC_APP = "org.onosproject.vtnrsc";
+
+    private static final String VIRTUALPORT_ID_NULL = "VirtualPort ID cannot be null";
+    private static final String VIRTUALPORT_NOT_NULL = "VirtualPort  cannot be null";
+    private static final String TENANTID_NOT_NULL = "TenantId  cannot be null";
+    private static final String NETWORKID_NOT_NULL = "NetworkId  cannot be null";
+    private static final String DEVICEID_NOT_NULL = "DeviceId  cannot be null";
+
+    protected Map<VirtualPortId, VirtualPort> vPortStore;
+    protected ApplicationId appId;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TenantNetworkService networkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Activate
+    public void activate() {
+
+        appId = coreService.registerApplication(VTNRSC_APP);
+
+        vPortStore = storageService.<VirtualPortId, VirtualPort>consistentMapBuilder()
+                .withName(VIRTUALPORT)
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+                                                 VirtualPortId.class,
+                                                 TenantNetworkId.class,
+                                                 VirtualPort.State.class,
+                                                 TenantId.class,
+                                                 AllowedAddressPair.class,
+                                                 FixedIp.class,
+                                                 BindingHostId.class,
+                                                 SecurityGroup.class,
+                                                 SubnetId.class,
+                                                 IpAddress.class,
+                                                 DefaultVirtualPort.class))
+                .build().asJavaMap();
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        vPortStore.clear();
+        log.info("Stoppped");
+    }
+
+    @Override
+    public boolean exists(VirtualPortId vPortId) {
+        checkNotNull(vPortId, VIRTUALPORT_ID_NULL);
+        return vPortStore.containsKey(vPortId);
+    }
+
+    @Override
+    public VirtualPort getPort(VirtualPortId vPortId) {
+        checkNotNull(vPortId, VIRTUALPORT_ID_NULL);
+        return vPortStore.get(vPortId);
+    }
+
+    @Override
+    public Collection<VirtualPort> getPorts() {
+        return Collections.unmodifiableCollection(vPortStore.values());
+    }
+
+    @Override
+    public Collection<VirtualPort> getPorts(TenantNetworkId networkId) {
+        checkNotNull(networkId, NETWORKID_NOT_NULL);
+        return vPortStore.values().stream().filter(d -> d.networkId().equals(networkId))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Collection<VirtualPort> getPorts(TenantId tenantId) {
+        checkNotNull(tenantId, TENANTID_NOT_NULL);
+        return vPortStore.values().stream().filter(d -> d.tenantId().equals(tenantId))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Collection<VirtualPort> getPorts(DeviceId deviceId) {
+        checkNotNull(deviceId, DEVICEID_NOT_NULL);
+        return vPortStore.values().stream().filter(d -> d.deviceId().equals(deviceId))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public boolean createPorts(Iterable<VirtualPort> vPorts) {
+        checkNotNull(vPorts, VIRTUALPORT_NOT_NULL);
+        for (VirtualPort vPort : vPorts) {
+            log.debug("vPortId is  {} ", vPort.portId().toString());
+            vPortStore.put(vPort.portId(), vPort);
+            if (!vPortStore.containsKey(vPort.portId())) {
+                log.debug("The virtualPort is created failed whose identifier is {} ",
+                          vPort.portId().toString());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean updatePorts(Iterable<VirtualPort> vPorts) {
+        checkNotNull(vPorts, VIRTUALPORT_NOT_NULL);
+        if (vPorts != null) {
+            for (VirtualPort vPort : vPorts) {
+                vPortStore.put(vPort.portId(), vPort);
+                if (!vPortStore.containsKey(vPort.portId())) {
+                    log.debug("The virtualPort is not exist whose identifier is {}",
+                              vPort.portId().toString());
+                    return false;
+                }
+
+                vPortStore.put(vPort.portId(), vPort);
+
+                if (!vPort.equals(vPortStore.get(vPort.portId()))) {
+                    log.debug("The virtualPort is updated failed whose  identifier is {}",
+                              vPort.portId().toString());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean removePorts(Iterable<VirtualPortId> vPortIds) {
+        checkNotNull(vPortIds, VIRTUALPORT_ID_NULL);
+        if (vPortIds != null) {
+            for (VirtualPortId vPortId : vPortIds) {
+                vPortStore.remove(vPortId);
+                if (vPortStore.containsKey(vPortId)) {
+                    log.debug("The virtualPort is removed failed whose identifier is {}",
+                              vPortId.toString());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java
new file mode 100644 (file)
index 0000000..24eb0d3
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of service for interacting with the inventory of virtual ports.
+ */
+package org.onosproject.vtnrsc.virtualport.impl;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java
new file mode 100644 (file)
index 0000000..06a01a0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Service for interacting with the inventory of virtual ports.
+ */
+package org.onosproject.vtnrsc.virtualport;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java
new file mode 100644 (file)
index 0000000..57c97c1
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.AllocationPool;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Subnet AllocationPool codec.
+ */
+public final class AllocationPoolsCodec extends JsonCodec<AllocationPool> {
+
+    @Override
+    public ObjectNode encode(AllocationPool alocPool, CodecContext context) {
+        checkNotNull(alocPool, "AllocationPools cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("start", alocPool.startIp().toString())
+                .put("end", alocPool.endIp().toString());
+        return result;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java
new file mode 100644 (file)
index 0000000..7960808
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * VirtualPort AllowedAddressPair codec.
+ */
+public final class AllowedAddressPairCodec extends JsonCodec<AllowedAddressPair> {
+
+    @Override
+    public ObjectNode encode(AllowedAddressPair alocAddPair, CodecContext context) {
+        checkNotNull(alocAddPair, "AllowedAddressPair cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("ip_address", alocAddPair.ip().toString())
+                .put("mac_address", alocAddPair.mac().toString());
+        return result;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java
new file mode 100644 (file)
index 0000000..96c9bb4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.FixedIp;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * VirtualPort FixedIp codec.
+ */
+public final class FixedIpCodec extends JsonCodec<FixedIp> {
+
+    @Override
+    public ObjectNode encode(FixedIp fixIp, CodecContext context) {
+        checkNotNull(fixIp, "FixedIp cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("subnet_id", fixIp.subnetId().toString())
+                .put("ip_address", fixIp.ip().toString());
+        return result;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java
new file mode 100644 (file)
index 0000000..69ca6b3
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.HostRoute;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Subnet HostRoute codec.
+ */
+public final class HostRoutesCodec extends JsonCodec<HostRoute> {
+
+    @Override
+    public ObjectNode encode(HostRoute hostRoute, CodecContext context) {
+        checkNotNull(hostRoute, "HostRoute cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("nexthop", hostRoute.nexthop().toString())
+                .put("destination", hostRoute.destination().toString());
+        return result;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java
new file mode 100644 (file)
index 0000000..c2ded19
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.SecurityGroup;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Virtualport SecurityGroup codec.
+ */
+public final class SecurityGroupCodec extends JsonCodec<SecurityGroup> {
+
+    @Override
+    public ObjectNode encode(SecurityGroup securGroup, CodecContext context) {
+        checkNotNull(securGroup, "SecurityGroup cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("security_group", securGroup.securityGroup());
+        return result;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java
new file mode 100644 (file)
index 0000000..122b75a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.Subnet;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Subnet JSON codec.
+ */
+public final class SubnetCodec extends JsonCodec<Subnet> {
+    @Override
+    public ObjectNode encode(Subnet subnet, CodecContext context) {
+        checkNotNull(subnet, "Subnet cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("id", subnet.id().toString())
+                .put("gateway_ip", subnet.gatewayIp().toString())
+                .put("network_id", subnet.networkId().toString())
+                .put("name", subnet.subnetName())
+                .put("ip_version", subnet.ipVersion().toString())
+                .put("cidr", subnet.cidr().toString())
+                .put("shared", subnet.shared())
+                .put("enabled_dchp", subnet.dhcpEnabled())
+                .put("tenant_id", subnet.tenantId().toString())
+                .put("ipv6_address_mode", subnet.ipV6AddressMode() == null ? null
+                          : subnet.ipV6AddressMode().toString())
+                .put("ipv6_ra_mode", subnet.ipV6RaMode() == null ? null
+                          : subnet.ipV6RaMode().toString());
+        result.set("allocation_pools", new AllocationPoolsCodec().encode(subnet
+                .allocationPools(), context));
+        result.set("host_routes",
+                   new HostRoutesCodec().encode(subnet.hostRoutes(), context));
+        return result;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java
new file mode 100644 (file)
index 0000000..48ba3b9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.TenantNetwork;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * TenantNetwork JSON codec.
+ */
+public final class TenantNetworkCodec extends JsonCodec<TenantNetwork> {
+
+    @Override
+    public ObjectNode encode(TenantNetwork network, CodecContext context) {
+        checkNotNull(network, "Network cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put("id", network.id().toString())
+                .put("name", network.name())
+                .put("admin_state_up", network.adminStateUp())
+                .put("status", "" + network.state())
+                .put("shared", network.shared())
+                .put("tenant_id", network.tenantId().toString())
+                .put("router:external", network.routerExternal())
+                .put("provider:network_type", "" + network.type())
+                .put("provider:physical_network", network.physicalNetwork().toString())
+                .put("provider:segmentation_id", network.segmentationId().toString());
+        return result;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java
new file mode 100644 (file)
index 0000000..e57d56b
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnrsc.web;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.VirtualPort;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * VirtualPort JSON codec.
+ */
+public final class VirtualPortCodec extends JsonCodec<VirtualPort> {
+    @Override
+    public ObjectNode encode(VirtualPort vPort, CodecContext context) {
+        checkNotNull(vPort, "VPort cannot be null");
+        ObjectNode result = context
+                .mapper()
+                .createObjectNode()
+                .put("id", vPort.portId().toString())
+                .put("network_id", vPort.networkId().toString())
+                .put("admin_state_up", vPort.adminStateUp())
+                .put("name", vPort.name())
+                .put("status", vPort.state().toString())
+                .put("mac_address", vPort.macAddress().toString())
+                .put("tenant_id", vPort.tenantId().toString())
+                .put("device_id", vPort.deviceId().toString())
+                .put("device_owner", vPort.deviceOwner())
+                .put("binding:vnic_type", vPort.bindingVnicType())
+                .put("binding:Vif_type", vPort.bindingVifType())
+                .put("binding:host_id", vPort.bindingHostId().toString())
+                .put("binding:vif_details", vPort.bindingVifDetails());
+        result.set("allowed_address_pairs", new AllowedAddressPairCodec().encode(
+                                                                               vPort.allowedAddressPairs(), context));
+        result.set("fixed_ips", new FixedIpCodec().encode(
+                                                        vPort.fixedIps(), context));
+        result.set("security_groups", new SecurityGroupCodec().encode(
+                                                        vPort.securityGroups(), context));
+        return result;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java
new file mode 100644 (file)
index 0000000..34636a9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Codecs for virtual tenant objects.
+ */
+package org.onosproject.vtnrsc.web;
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644 (file)
index 0000000..c6a9c81
--- /dev/null
@@ -0,0 +1,56 @@
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+  <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+    <command>
+      <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkCreateCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkQueryCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkRemoveCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkUpdateCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.subnet.SubnetCreateCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.subnet.SubnetQueryCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.subnet.SubnetRemoveCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.subnet.SubnetUpdateCommand"/>
+    </command>
+      <command>
+      <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortCreateCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortQueryCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortRemoveCommand"/>
+    </command>
+     <command>
+      <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortUpdateCommand"/>
+    </command>
+  </command-bundle>
+</blueprint>
diff --git a/framework/src/onos/apps/vtn/vtnweb/pom.xml b/framework/src/onos/apps/vtn/vtnweb/pom.xml
new file mode 100644 (file)
index 0000000..bcb71d9
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        xmlns="http://maven.apache.org/POM/4.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-app-vtn</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+
+    <artifactId>onos-app-vtn-web</artifactId>
+    <packaging>bundle</packaging>
+    <properties>
+        <web.context>/onos/vtn</web.context>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-vtn-rsc</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <_wab>src/main/webapp/</_wab>
+                        <Bundle-SymbolicName>
+                            ${project.groupId}.${project.artifactId}
+                        </Bundle-SymbolicName>
+                        <Import-Package>
+                            org.slf4j,
+                            org.osgi.framework,
+                            javax.ws.rs,
+                            javax.ws.rs.core,
+                            com.sun.jersey.api.core,
+                            com.sun.jersey.spi.container.servlet,
+                            com.sun.jersey.server.impl.container.servlet,
+                            com.fasterxml.jackson.databind,
+                            com.fasterxml.jackson.databind.node,
+                            com.fasterxml.jackson.core,
+                            org.apache.karaf.shell.commands,
+                            org.apache.commons.lang.math.*,
+                            com.google.common.*,
+                            org.onlab.packet.*,
+                            org.onlab.rest.*,
+                            org.onosproject.*,
+                            org.onlab.util.*,
+                            org.jboss.netty.util.*
+                        </Import-Package>
+                        <Web-ContextPath>${web.context}</Web-ContextPath>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java
new file mode 100644 (file)
index 0000000..deb9ca3
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpAddress.Version;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.AllocationPool;
+import org.onosproject.vtnrsc.DefaultAllocationPool;
+import org.onosproject.vtnrsc.DefaultHostRoute;
+import org.onosproject.vtnrsc.DefaultSubnet;
+import org.onosproject.vtnrsc.HostRoute;
+import org.onosproject.vtnrsc.Subnet;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.Subnet.Mode;
+import org.onosproject.vtnrsc.subnet.SubnetService;
+import org.onosproject.vtnrsc.web.SubnetCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+@Path("subnets")
+public class SubnetWebResource extends AbstractWebResource {
+    private final Logger log = LoggerFactory.getLogger(SubnetWebResource.class);
+    public static final String SUBNET_NOT_CREATE = "Subnets is failed to create!";
+    public static final String SUBNET_NOT_FOUND = "Subnets is not found";
+    public static final String JSON_NOT_NULL = "JsonNode can not be null";
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response listSubnets() {
+        Iterable<Subnet> subnets = get(SubnetService.class).getSubnets();
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("subnets", new SubnetCodec().encode(subnets, this));
+        return ok(result.toString()).build();
+    }
+
+    @GET
+    @Path("{subnetUUID}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getSubnet(@PathParam("subnetUUID") String id) {
+
+        if (!get(SubnetService.class).exists(SubnetId.subnetId(id))) {
+            return Response.status(NOT_FOUND)
+                    .entity(SUBNET_NOT_FOUND).build();
+        }
+        Subnet sub = nullIsNotFound(get(SubnetService.class)
+                                            .getSubnet(SubnetId.subnetId(id)),
+                                    SUBNET_NOT_FOUND);
+
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("subnet", new SubnetCodec().encode(sub, this));
+        return ok(result.toString()).build();
+    }
+
+    @POST
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response createSubnet(final InputStream input) {
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode subnode = mapper.readTree(input);
+            Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode);
+            Boolean result = nullIsNotFound((get(SubnetService.class)
+                                                    .createSubnets(subnets)),
+                                            SUBNET_NOT_CREATE);
+
+            if (!result) {
+                return Response.status(INTERNAL_SERVER_ERROR)
+                        .entity(SUBNET_NOT_CREATE).build();
+            }
+            return Response.status(202).entity(result.toString()).build();
+        } catch (Exception e) {
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    @PUT
+    @Path("{subnetUUID}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response updateSubnet(@PathParam("id") String id,
+                                 final InputStream input) {
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode subnode = mapper.readTree(input);
+            Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode);
+            Boolean result = nullIsNotFound(get(SubnetService.class)
+                    .updateSubnets(subnets), SUBNET_NOT_FOUND);
+            if (!result) {
+                return Response.status(INTERNAL_SERVER_ERROR)
+                        .entity(SUBNET_NOT_FOUND).build();
+            }
+            return Response.status(203).entity(result.toString()).build();
+        } catch (Exception e) {
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    @Path("{subnetUUID}")
+    @DELETE
+    public Response deleteSingleSubnet(@PathParam("subnetUUID") String id)
+            throws IOException {
+        try {
+            SubnetId subId = SubnetId.subnetId(id);
+            Set<SubnetId> subIds = new HashSet<>();
+            subIds.add(subId);
+            get(SubnetService.class).removeSubnets(subIds);
+            return Response.status(201).entity("SUCCESS").build();
+        } catch (Exception e) {
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    private Iterable<Subnet> createOrUpdateByInputStream(JsonNode subnode) {
+        checkNotNull(subnode, JSON_NOT_NULL);
+        Iterable<Subnet> subnets = null;
+        JsonNode subnetNodes = subnode.get("subnets");
+        if (subnetNodes == null) {
+            subnetNodes = subnode.get("subnet");
+        }
+        log.debug("subnetNodes is {}", subnetNodes.toString());
+        if (subnetNodes.isArray()) {
+            subnets = changeJsonToSubs(subnetNodes);
+        } else {
+            subnets = changeJsonToSub(subnetNodes);
+        }
+        return subnets;
+    }
+
+    /**
+     * Returns a collection of subnets from subnetNodes.
+     *
+     * @param subnetNodes the subnet json node
+     * @return subnets a collection of subnets
+     */
+    public Iterable<Subnet> changeJsonToSubs(JsonNode subnetNodes) {
+        checkNotNull(subnetNodes, JSON_NOT_NULL);
+        Map<SubnetId, Subnet> subMap = new HashMap<>();
+        for (JsonNode subnetNode : subnetNodes) {
+            if (!subnetNode.hasNonNull("id")) {
+                return null;
+            }
+            SubnetId id = SubnetId.subnetId(subnetNode.get("id").asText());
+            String subnetName = subnetNode.get("name").asText();
+            TenantId tenantId = TenantId
+                    .tenantId(subnetNode.get("tenant_id").asText());
+            TenantNetworkId networkId = TenantNetworkId
+                    .networkId(subnetNode.get("network_id").asText());
+            String version = subnetNode.get("ip_version").asText();
+            Version ipVersion;
+            switch (version) {
+            case "4":
+                ipVersion = Version.INET;
+                break;
+            case "6":
+                ipVersion = Version.INET;
+                break;
+            default:
+                throw new IllegalArgumentException("ipVersion should be 4 or 6.");
+            }
+            IpPrefix cidr = IpPrefix.valueOf(subnetNode.get("cidr").asText());
+            IpAddress gatewayIp = IpAddress
+                    .valueOf(subnetNode.get("gateway_ip").asText());
+            Boolean dhcpEnabled = subnetNode.get("enable_dhcp").asBoolean();
+            Boolean shared = subnetNode.get("shared").asBoolean();
+            JsonNode hostRoutes = subnetNode.get("host_routes");
+            Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes);
+            JsonNode allocationPools = subnetNode.get("allocation_pools");
+            Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools);
+            Mode ipV6AddressMode = Mode
+                    .valueOf(subnetNode.get("ipv6_address_mode").asText());
+            Mode ipV6RaMode = Mode
+                    .valueOf(subnetNode.get("ipv6_ra_mode").asText());
+            Subnet subnet = new DefaultSubnet(id, subnetName, networkId,
+                                              tenantId, ipVersion, cidr,
+                                              gatewayIp, dhcpEnabled, shared,
+                                              Sets.newHashSet(hostRoutesIt), ipV6AddressMode,
+                                              ipV6RaMode, Sets.newHashSet(allocationPoolsIt));
+            subMap.put(id, subnet);
+        }
+        return Collections.unmodifiableCollection(subMap.values());
+    }
+
+    /**
+     * Returns a collection of subnets from subnetNodes.
+     *
+     * @param subnetNodes the subnet json node
+     * @return subnets a collection of subnets
+     */
+    public Iterable<Subnet> changeJsonToSub(JsonNode subnetNodes) {
+        checkNotNull(subnetNodes, JSON_NOT_NULL);
+        checkArgument(subnetNodes.get("enable_dhcp").isBoolean(), "enable_dhcp should be boolean");
+        checkArgument(subnetNodes.get("shared").isBoolean(), "shared should be boolean");
+        Map<SubnetId, Subnet> subMap = new HashMap<>();
+        if (!subnetNodes.hasNonNull("id")) {
+            return null;
+        }
+        SubnetId id = SubnetId.subnetId(subnetNodes.get("id").asText());
+        String subnetName = subnetNodes.get("name").asText();
+        TenantId tenantId = TenantId
+                .tenantId(subnetNodes.get("tenant_id").asText());
+        TenantNetworkId networkId = TenantNetworkId
+                .networkId(subnetNodes.get("network_id").asText());
+        String version = subnetNodes.get("ip_version").asText();
+        Version ipVersion;
+        switch (version) {
+        case "4":
+            ipVersion = Version.INET;
+            break;
+        case "6":
+            ipVersion = Version.INET;
+            break;
+        default:
+            throw new IllegalArgumentException("ipVersion should be 4 or 6.");
+        }
+
+        IpPrefix cidr = IpPrefix.valueOf(subnetNodes.get("cidr").asText());
+        IpAddress gatewayIp = IpAddress
+                .valueOf(subnetNodes.get("gateway_ip").asText());
+        Boolean dhcpEnabled = subnetNodes.get("enable_dhcp").asBoolean();
+        Boolean shared = subnetNodes.get("shared").asBoolean();
+        JsonNode hostRoutes = subnetNodes.get("host_routes");
+        Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes);
+        JsonNode allocationPools = subnetNodes.get("allocation_pools");
+        Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools);
+
+        Mode ipV6AddressMode = getMode(subnetNodes.get("ipv6_address_mode")
+                .asText());
+        Mode ipV6RaMode = getMode(subnetNodes.get("ipv6_ra_mode").asText());
+
+        Subnet subnet = new DefaultSubnet(id, subnetName, networkId, tenantId,
+                                          ipVersion, cidr, gatewayIp,
+                                          dhcpEnabled, shared, Sets.newHashSet(hostRoutesIt),
+                                          ipV6AddressMode, ipV6RaMode,
+                                          Sets.newHashSet(allocationPoolsIt));
+        subMap.put(id, subnet);
+        return Collections.unmodifiableCollection(subMap.values());
+    }
+
+    /**
+     * Gets ipv6_address_mode or ipv6_ra_mode type.
+     *
+     * @param mode the String value in JsonNode
+     * @return ipV6Mode Mode of the ipV6Mode
+     */
+    private Mode getMode(String mode) {
+        Mode ipV6Mode;
+        if (mode == null) {
+            return null;
+        }
+        switch (mode) {
+        case "dhcpv6-stateful":
+            ipV6Mode = Mode.DHCPV6_STATEFUL;
+            break;
+        case "dhcpv6-stateless":
+            ipV6Mode = Mode.DHCPV6_STATELESS;
+            break;
+        case "slaac":
+            ipV6Mode = Mode.SLAAC;
+            break;
+        default:
+            ipV6Mode = null;
+        }
+        return ipV6Mode;
+    }
+
+    /**
+     * Changes JsonNode alocPools to a collection of the alocPools.
+     *
+     * @param allocationPools the allocationPools JsonNode
+     * @return a collection of allocationPools
+     */
+    public Iterable<AllocationPool> jsonNodeToAllocationPools(JsonNode allocationPools) {
+        checkNotNull(allocationPools, JSON_NOT_NULL);
+        ConcurrentMap<Integer, AllocationPool> alocplMaps = Maps
+                .newConcurrentMap();
+        Integer i = 0;
+        for (JsonNode node : allocationPools) {
+            IpAddress startIp = IpAddress.valueOf(node.get("start").asText());
+            IpAddress endIp = IpAddress.valueOf(node.get("end").asText());
+            AllocationPool alocPls = new DefaultAllocationPool(startIp, endIp);
+            alocplMaps.putIfAbsent(i, alocPls);
+            i++;
+        }
+        return Collections.unmodifiableCollection(alocplMaps.values());
+    }
+
+    /**
+     * Changes hostRoutes JsonNode to a collection of the hostRoutes.
+     *
+     * @param hostRoutes the hostRoutes json node
+     * @return a collection of hostRoutes
+     */
+    public Iterable<HostRoute> jsonNodeToHostRoutes(JsonNode hostRoutes) {
+        checkNotNull(hostRoutes, JSON_NOT_NULL);
+        ConcurrentMap<Integer, HostRoute> hostRouteMaps = Maps
+                .newConcurrentMap();
+        Integer i = 0;
+        for (JsonNode node : hostRoutes) {
+            IpAddress nexthop = IpAddress.valueOf(node.get("nexthop").asText());
+            IpPrefix destination = IpPrefix.valueOf(node.get("destination")
+                    .asText());
+            HostRoute hostRoute = new DefaultHostRoute(nexthop, destination);
+            hostRouteMaps.putIfAbsent(i, hostRoute);
+            i++;
+        }
+        return Collections.unmodifiableCollection(hostRouteMaps.values());
+    }
+
+    /**
+     * Returns the specified item if that items is null; otherwise throws not
+     * found exception.
+     *
+     * @param item item to check
+     * @param <T> item type
+     * @param message not found message
+     * @return item if not null
+     * @throws org.onlab.util.ItemNotFoundException if item is null
+     */
+    protected <T> T nullIsNotFound(T item, String message) {
+        if (item == null) {
+            throw new ItemNotFoundException(message);
+        }
+        return item;
+    }
+
+}
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java
new file mode 100644 (file)
index 0000000..0b87782
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.OK;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
+import org.onosproject.vtnrsc.PhysicalNetwork;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.TenantNetwork.State;
+import org.onosproject.vtnrsc.TenantNetwork.Type;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.web.TenantNetworkCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+
+/**
+ * REST resource for interacting with the inventory of networks.
+ */
+@Path("networks")
+public class TenantNetworkWebResource extends AbstractWebResource {
+    public static final String NETWORK_NOT_FOUND = "Network is not found";
+    public static final String NETWORK_ID_EXIST = "Network id is existed";
+    public static final String NETWORK_ID_NOT_EXIST = "Network id is not existed";
+    public static final String CREATE_NETWORK = "create network";
+    public static final String UPDATE_NETWORK = "update network";
+    public static final String DELETE_NETWORK = "delete network";
+    public static final String JSON_NOT_NULL = "JsonNode can not be null";
+
+    protected static final Logger log = LoggerFactory
+            .getLogger(TenantNetworkWebResource.class);
+    private final ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps
+            .newConcurrentMap();
+
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON })
+    public Response getNetworks(@QueryParam("id") String queryId,
+                                @QueryParam("name") String queryName,
+                                @QueryParam("admin_state_up") String queryadminStateUp,
+                                @QueryParam("status") String querystate,
+                                @QueryParam("shared") String queryshared,
+                                @QueryParam("tenant_id") String querytenantId,
+                                @QueryParam("router:external") String routerExternal,
+                                @QueryParam("provider:network_type") String type,
+                                @QueryParam("provider:physical_network") String physicalNetwork,
+                                @QueryParam("provider:segmentation_id") String segmentationId) {
+        Iterable<TenantNetwork> networks = get(TenantNetworkService.class)
+                .getNetworks();
+        Iterator<TenantNetwork> networkors = networks.iterator();
+        while (networkors.hasNext()) {
+            TenantNetwork network = networkors.next();
+            if ((queryId == null || queryId.equals(network.id().toString()))
+                    && (queryName == null || queryName.equals(network.name()))
+                    && (queryadminStateUp == null || queryadminStateUp
+                            .equals(network.adminStateUp()))
+                    && (querystate == null || querystate.equals(network.state()
+                            .toString()))
+                    && (queryshared == null || queryshared.equals(network
+                            .shared()))
+                    && (querytenantId == null || querytenantId.equals(network
+                            .tenantId().toString()))
+                    && (routerExternal == null || routerExternal.equals(network
+                            .routerExternal()))
+                    && (type == null || type.equals(network.type()))
+                    && (physicalNetwork == null || physicalNetwork
+                            .equals(network.physicalNetwork()))
+                    && (segmentationId == null || segmentationId.equals(network
+                            .segmentationId()))) {
+                networksMap.putIfAbsent(network.id(), network);
+            }
+        }
+        networks = Collections.unmodifiableCollection(networksMap.values());
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("networks", new TenantNetworkCodec().encode(networks, this));
+
+        return ok(result.toString()).build();
+    }
+
+    private State isState(String state) {
+        if (state.equals("ACTIVE")) {
+            return TenantNetwork.State.ACTIVE;
+        } else if (state.equals("BUILD")) {
+            return TenantNetwork.State.BUILD;
+        } else if (state.equals("DOWN")) {
+            return TenantNetwork.State.DOWN;
+        } else if (state.equals("ERROR")) {
+            return TenantNetwork.State.ERROR;
+        } else {
+            return null;
+        }
+    }
+
+    private Type isType(String type) {
+        if (type.equals("LOCAL")) {
+            return TenantNetwork.Type.LOCAL;
+        } else {
+            return null;
+        }
+    }
+
+    @GET
+    @Path("{id}")
+    @Produces({ MediaType.APPLICATION_JSON })
+    public Response getNetwork(@PathParam("id") String id) {
+
+        if (!get(TenantNetworkService.class).exists(TenantNetworkId
+                                                            .networkId(id))) {
+            return Response.status(NOT_FOUND)
+                    .entity(NETWORK_NOT_FOUND).build();
+        }
+        TenantNetwork network = nullIsNotFound(get(TenantNetworkService.class)
+                .getNetwork(TenantNetworkId.networkId(id)), NETWORK_NOT_FOUND);
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("network", new TenantNetworkCodec().encode(network, this));
+
+        return ok(result.toString()).build();
+
+    }
+
+    @POST
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response createNetworks(InputStream input) {
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            JsonNode nodes = null;
+            Iterable<TenantNetwork> networks = null;
+            if (cfg.get("network") != null) {
+                nodes = cfg.get("network");
+                if (nodes.isArray()) {
+                    networks = changeJson2objs(nodes);
+                } else {
+                    networks = changeJson2obj(CREATE_NETWORK, null, nodes);
+                }
+            } else if (cfg.get("networks") != null) {
+                nodes = cfg.get("networks");
+                networks = changeJson2objs(nodes);
+            }
+            Boolean issuccess = nullIsNotFound((get(TenantNetworkService.class)
+                                                       .createNetworks(networks)),
+                                               NETWORK_NOT_FOUND);
+
+            if (!issuccess) {
+                return Response.status(INTERNAL_SERVER_ERROR)
+                        .entity(NETWORK_ID_EXIST).build();
+            }
+            return Response.status(OK).entity(issuccess.toString()).build();
+        } catch (Exception e) {
+            log.error("Creates tenantNetwork exception {}.", e.toString());
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    @PUT
+    @Path("{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response updateNetworks(@PathParam("id") String id, InputStream input) {
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            JsonNode nodes = null;
+            Iterable<TenantNetwork> networks = null;
+            if (cfg.get("network") != null) {
+                nodes = cfg.get("network");
+                if (nodes.isArray()) {
+                    networks = changeJson2objs(nodes);
+                } else {
+                    networks = changeJson2obj(UPDATE_NETWORK,
+                                              TenantNetworkId.networkId(id),
+                                              nodes);
+                }
+            } else if (cfg.get("networks") != null) {
+                nodes = cfg.get("networks");
+                networks = changeJson2objs(nodes);
+            }
+            Boolean issuccess = nullIsNotFound((get(TenantNetworkService.class)
+                                                       .updateNetworks(networks)),
+                                               NETWORK_NOT_FOUND);
+            if (!issuccess) {
+                return Response.status(INTERNAL_SERVER_ERROR)
+                        .entity(NETWORK_ID_NOT_EXIST).build();
+            }
+            return Response.status(OK).entity(issuccess.toString()).build();
+        } catch (Exception e) {
+            log.error("Updates tenantNetwork failed because of exception {}.",
+                      e.toString());
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    @DELETE
+    @Path("{id}")
+    public Response deleteNetworks(@PathParam("id") String id) {
+        log.debug("Deletes network by identifier {}.", id);
+        Set<TenantNetworkId> networkSet = new HashSet<>();
+        networkSet.add(TenantNetworkId.networkId(id));
+        Boolean issuccess = nullIsNotFound(get(TenantNetworkService.class)
+                .removeNetworks(networkSet), NETWORK_NOT_FOUND);
+        if (!issuccess) {
+            log.debug("Network identifier {} is not existed", id);
+            return Response.status(INTERNAL_SERVER_ERROR)
+                    .entity(NETWORK_ID_NOT_EXIST).build();
+        }
+        return Response.status(OK).entity(issuccess.toString()).build();
+    }
+
+    /**
+     * Returns a collection of tenantNetworks.
+     *
+     * @param flag the flag
+     * @param networkId network identifier
+     * @param node the network json node
+     * @return a collection of tenantNetworks
+     */
+    public Iterable<TenantNetwork> changeJson2obj(String flag,
+                                                  TenantNetworkId networkId,
+                                                  JsonNode node) {
+        checkNotNull(node, JSON_NOT_NULL);
+        TenantNetwork network = null;
+        ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps
+                .newConcurrentMap();
+        if (node != null) {
+            checkArgument(node.get("admin_state_up").isBoolean(), "admin_state_up should be boolean");
+            checkArgument(node.get("shared").isBoolean(), "shared should be boolean");
+            checkArgument(node.get("router:external").isBoolean(), "router:external should be boolean");
+            String name = node.get("name").asText();
+            boolean adminStateUp = node.get("admin_state_up").asBoolean();
+            String state = node.get("status").asText();
+            boolean shared = node.get("shared").asBoolean();
+            String tenantId = node.get("tenant_id").asText();
+            boolean routerExternal = node.get("router:external").asBoolean();
+            String type = node.get("provider:network_type").asText();
+            String physicalNetwork = node.get("provider:physical_network")
+                    .asText();
+            String segmentationId = node.get("provider:segmentation_id")
+                    .asText();
+            TenantNetworkId id = null;
+            if (flag == CREATE_NETWORK) {
+                id = TenantNetworkId.networkId(node.get("id").asText());
+            } else if (flag == UPDATE_NETWORK) {
+                id = networkId;
+            }
+            network = new DefaultTenantNetwork(
+                                               id,
+                                               name,
+                                               adminStateUp,
+                                               isState(state),
+                                               shared,
+                                               TenantId.tenantId(tenantId),
+                                               routerExternal,
+                                               isType(type),
+                                               PhysicalNetwork
+                                                       .physicalNetwork(physicalNetwork),
+                                               SegmentationId
+                                                       .segmentationId(segmentationId));
+            networksMap.putIfAbsent(id, network);
+        }
+        return Collections.unmodifiableCollection(networksMap.values());
+    }
+
+    /**
+     * Returns a collection of tenantNetworks.
+     *
+     * @param nodes the network jsonnodes
+     * @return a collection of tenantNetworks
+     */
+    public Iterable<TenantNetwork> changeJson2objs(JsonNode nodes) {
+        checkNotNull(nodes, JSON_NOT_NULL);
+        TenantNetwork network = null;
+        ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps
+                .newConcurrentMap();
+        if (nodes != null) {
+            for (JsonNode node : nodes) {
+                String id = node.get("id").asText();
+                String name = node.get("name").asText();
+                boolean adminStateUp = node.get("admin_state_up").asBoolean();
+                String state = node.get("status").asText();
+                boolean shared = node.get("shared").asBoolean();
+                String tenantId = node.get("tenant_id").asText();
+                boolean routerExternal = node.get("router:external")
+                        .asBoolean();
+                String type = node.get("provider:network_type").asText();
+                String physicalNetwork = node.get("provider:physical_network")
+                        .asText();
+                String segmentationId = node.get("provider:segmentation_id")
+                        .asText();
+                network = new DefaultTenantNetwork(
+                                                   TenantNetworkId
+                                                           .networkId(id),
+                                                   name,
+                                                   adminStateUp,
+                                                   isState(state),
+                                                   shared,
+                                                   TenantId.tenantId(tenantId),
+                                                   routerExternal,
+                                                   isType(type),
+                                                   PhysicalNetwork
+                                                           .physicalNetwork(physicalNetwork),
+                                                   SegmentationId
+                                                           .segmentationId(segmentationId));
+                networksMap.putIfAbsent(TenantNetworkId.networkId(id), network);
+            }
+        }
+        return Collections.unmodifiableCollection(networksMap.values());
+    }
+
+    /**
+     * Returns the specified item if that items is null; otherwise throws not
+     * found exception.
+     *
+     * @param item item to check
+     * @param <T> item type
+     * @param message not found message
+     * @return item if not null
+     * @throws org.onlab.util.ItemNotFoundException if item is null
+     */
+    protected <T> T nullIsNotFound(T item, String message) {
+        if (item == null) {
+            throw new ItemNotFoundException(message);
+        }
+        return item;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java
new file mode 100644 (file)
index 0000000..03d3a65
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.vtnweb.resources;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.OK;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.DeviceId;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.AllowedAddressPair;
+import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FixedIp;
+import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.SubnetId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPort.State;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.onosproject.vtnrsc.web.VirtualPortCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * REST resource for interacting with the inventory of infrastructure
+ * virtualPort.
+ */
+@Path("ports")
+public class VirtualPortWebResource extends AbstractWebResource {
+    public static final String VPORT_NOT_FOUND = "VirtualPort is not found";
+    public static final String VPORT_ID_EXIST = "VirtualPort id is exist";
+    public static final String VPORT_ID_NOT_EXIST = "VirtualPort id is not exist";
+    public static final String JSON_NOT_NULL = "JsonNode can not be null";
+    protected static final Logger log = LoggerFactory
+            .getLogger(VirtualPortService.class);
+
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON })
+    public Response getPorts() {
+        Iterable<VirtualPort> virtualPorts = get(VirtualPortService.class)
+                .getPorts();
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("ports", new VirtualPortCodec().encode(virtualPorts, this));
+        return ok(result.toString()).build();
+    }
+
+    @GET
+    @Path("{id}")
+    @Produces({ MediaType.APPLICATION_JSON })
+    public Response getportsById(@PathParam("id") String id) {
+
+        if (!get(VirtualPortService.class).exists(VirtualPortId.portId(id))) {
+            return Response.status(NOT_FOUND)
+                    .entity(VPORT_NOT_FOUND).build();
+        }
+        VirtualPort virtualPort = nullIsNotFound(get(VirtualPortService.class)
+                .getPort(VirtualPortId.portId(id)), VPORT_NOT_FOUND);
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("port", new VirtualPortCodec().encode(virtualPort, this));
+        return ok(result.toString()).build();
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createPorts(InputStream input) {
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            Iterable<VirtualPort> vPorts = createOrUpdateByInputStream(cfg);
+            Boolean issuccess = nullIsNotFound(get(VirtualPortService.class)
+                    .createPorts(vPorts), VPORT_NOT_FOUND);
+            if (!issuccess) {
+                return Response.status(INTERNAL_SERVER_ERROR)
+                        .entity(VPORT_ID_NOT_EXIST).build();
+            }
+            return Response.status(OK).entity(issuccess.toString()).build();
+        } catch (Exception e) {
+            log.error("Creates VirtualPort failed because of exception {}",
+                      e.toString());
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    @Path("{portUUID}")
+    @DELETE
+    public Response deletePorts(@PathParam("portUUID") String id) {
+        Set<VirtualPortId> vPortIds = new HashSet<>();
+        try {
+            if (id != null) {
+                vPortIds.add(VirtualPortId.portId(id));
+            }
+            Boolean issuccess = nullIsNotFound(get(VirtualPortService.class)
+                    .removePorts(vPortIds), VPORT_NOT_FOUND);
+            if (!issuccess) {
+                return Response.status(INTERNAL_SERVER_ERROR)
+                        .entity(VPORT_ID_NOT_EXIST).build();
+            }
+            return Response.status(OK).entity(issuccess.toString()).build();
+        } catch (Exception e) {
+            log.error("Deletes VirtualPort failed because of exception {}",
+                      e.toString());
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    @PUT
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updatePorts(@PathParam("id") String id, InputStream input) {
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JsonNode cfg = mapper.readTree(input);
+            Iterable<VirtualPort> vPorts = createOrUpdateByInputStream(cfg);
+            Boolean issuccess = nullIsNotFound(get(VirtualPortService.class)
+                    .updatePorts(vPorts), VPORT_NOT_FOUND);
+            if (!issuccess) {
+                return Response.status(INTERNAL_SERVER_ERROR)
+                        .entity(VPORT_ID_NOT_EXIST).build();
+            }
+            return Response.status(OK).entity(issuccess.toString()).build();
+        } catch (Exception e) {
+            log.error("Updates failed because of exception {}", e.toString());
+            return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString())
+                    .build();
+        }
+    }
+
+    /**
+     * Returns a Object of the currently known infrastructure virtualPort.
+     *
+     * @param vPortNode the virtualPort json node
+     * @return a collection of virtualPorts
+     */
+    public Iterable<VirtualPort> createOrUpdateByInputStream(JsonNode vPortNode) {
+        checkNotNull(vPortNode, JSON_NOT_NULL);
+        JsonNode vPortNodes = vPortNode.get("ports");
+        if (vPortNodes == null) {
+            vPortNodes = vPortNode.get("port");
+        }
+        if (vPortNodes.isArray()) {
+            return changeJsonToPorts(vPortNodes);
+        } else {
+            return changeJsonToPort(vPortNodes);
+        }
+    }
+
+    /**
+     * Returns the iterable collection of virtualports from subnetNodes.
+     *
+     * @param vPortNodes the virtualPort json node
+     * @return virtualPorts a collection of virtualPorts
+     */
+    public Iterable<VirtualPort> changeJsonToPorts(JsonNode vPortNodes) {
+        checkNotNull(vPortNodes, JSON_NOT_NULL);
+        Map<VirtualPortId, VirtualPort> portMap = new HashMap<>();
+        Map<String, String> strMap = new HashMap<>();
+        for (JsonNode vPortnode : vPortNodes) {
+            VirtualPortId id = VirtualPortId.portId(vPortnode.get("id")
+                    .asText());
+            String name = vPortnode.get("name").asText();
+            TenantId tenantId = TenantId.tenantId(vPortnode.get("tenant_id")
+                    .asText());
+            TenantNetworkId networkId = TenantNetworkId.networkId(vPortnode
+                    .get("network_id").asText());
+            checkArgument(vPortnode.get("admin_state_up").isBoolean(), "admin_state_up should be boolean");
+            Boolean adminStateUp = vPortnode.get("admin_state_up").asBoolean();
+            String state = vPortnode.get("status").asText();
+            MacAddress macAddress = MacAddress.valueOf(vPortnode
+                    .get("mac_address").asText());
+            DeviceId deviceId = DeviceId.deviceId(vPortnode.get("device_id")
+                    .asText());
+            String deviceOwner = vPortnode.get("device_owner").asText();
+            JsonNode fixedIpNodes = vPortNodes.get("fixed_ips");
+            Set<FixedIp> fixedIps = new HashSet<>();
+            for (JsonNode fixedIpNode : fixedIpNodes) {
+                FixedIp fixedIp = jsonNodeToFixedIps(fixedIpNode);
+                fixedIps.add(fixedIp);
+            }
+
+            BindingHostId bindingHostId = BindingHostId
+                    .bindingHostId(vPortnode.get("binding:host_id").asText());
+            String bindingVnicType = vPortnode.get("binding:vnic_type")
+                    .asText();
+            String bindingVifType = vPortnode.get("binding:vif_type").asText();
+            String bindingVifDetails = vPortnode.get("binding:vif_details")
+                    .asText();
+            JsonNode allowedAddressPairJsonNode = vPortnode
+                    .get("allowed_address_pairs");
+            Collection<AllowedAddressPair> allowedAddressPairs =
+                    jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode);
+            JsonNode securityGroupNode = vPortnode.get("security_groups");
+            Collection<SecurityGroup> securityGroups = jsonNodeToSecurityGroup(securityGroupNode);
+            strMap.put("name", name);
+            strMap.put("deviceOwner", deviceOwner);
+            strMap.put("bindingVnicType", bindingVnicType);
+            strMap.put("bindingVifType", bindingVifType);
+            strMap.put("bindingVifDetails", bindingVifDetails);
+            VirtualPort vPort = new DefaultVirtualPort(id, networkId,
+                                                       adminStateUp, strMap,
+                                                       isState(state),
+                                                       macAddress, tenantId,
+                                                       deviceId, fixedIps,
+                                                       bindingHostId,
+                                                       Sets.newHashSet(allowedAddressPairs),
+                                                       Sets.newHashSet(securityGroups));
+            portMap.put(id, vPort);
+        }
+        return Collections.unmodifiableCollection(portMap.values());
+    }
+
+    /**
+     * Returns a collection of virtualPorts from subnetNodes.
+     *
+     * @param vPortNodes the virtualPort json node
+     * @return virtualPorts a collection of virtualPorts
+     */
+    public Iterable<VirtualPort> changeJsonToPort(JsonNode vPortNodes) {
+        checkNotNull(vPortNodes, JSON_NOT_NULL);
+        Map<VirtualPortId, VirtualPort> vportMap = new HashMap<>();
+        Map<String, String> strMap = new HashMap<>();
+        VirtualPortId id = VirtualPortId.portId(vPortNodes.get("id").asText());
+        String name = vPortNodes.get("name").asText();
+        TenantId tenantId = TenantId.tenantId(vPortNodes.get("tenant_id")
+                .asText());
+        TenantNetworkId networkId = TenantNetworkId.networkId(vPortNodes
+                .get("network_id").asText());
+        Boolean adminStateUp = vPortNodes.get("admin_state_up").asBoolean();
+        String state = vPortNodes.get("status").asText();
+        MacAddress macAddress = MacAddress.valueOf(vPortNodes
+                .get("mac_address").asText());
+        DeviceId deviceId = DeviceId.deviceId(vPortNodes.get("device_id")
+                .asText());
+        String deviceOwner = vPortNodes.get("device_owner").asText();
+        JsonNode fixedIpNodes = vPortNodes.get("fixed_ips");
+        Set<FixedIp> fixedIps = new HashSet<>();
+        for (JsonNode fixedIpNode : fixedIpNodes) {
+            FixedIp fixedIp = jsonNodeToFixedIps(fixedIpNode);
+            fixedIps.add(fixedIp);
+        }
+
+        BindingHostId bindingHostId = BindingHostId
+                .bindingHostId(vPortNodes.get("binding:host_id").asText());
+        String bindingVnicType = vPortNodes.get("binding:vnic_type").asText();
+        String bindingVifType = vPortNodes.get("binding:vif_type").asText();
+        String bindingVifDetails = vPortNodes.get("binding:vif_details")
+                .asText();
+        JsonNode allowedAddressPairJsonNode = vPortNodes
+                .get("allowed_address_pairs");
+        Collection<AllowedAddressPair> allowedAddressPairs =
+                jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode);
+        JsonNode securityGroupNode = vPortNodes.get("security_groups");
+        Collection<SecurityGroup> securityGroups = jsonNodeToSecurityGroup(securityGroupNode);
+        strMap.put("name", name);
+        strMap.put("deviceOwner", deviceOwner);
+        strMap.put("bindingVnicType", bindingVnicType);
+        strMap.put("bindingVifType", bindingVifType);
+        strMap.put("bindingVifDetails", bindingVifDetails);
+        VirtualPort vPort = new DefaultVirtualPort(id, networkId, adminStateUp,
+                                                   strMap, isState(state),
+                                                   macAddress, tenantId,
+                                                   deviceId, fixedIps,
+                                                   bindingHostId,
+                                                   Sets.newHashSet(allowedAddressPairs),
+                                                   Sets.newHashSet(securityGroups));
+        vportMap.put(id, vPort);
+
+        return Collections.unmodifiableCollection(vportMap.values());
+    }
+
+    /**
+     * Returns a Object of the currently known infrastructure virtualPort.
+     *
+     * @param allowedAddressPairs the allowedAddressPairs json node
+     * @return a collection of allowedAddressPair
+     */
+    public Collection<AllowedAddressPair> jsonNodeToAllowedAddressPair(JsonNode allowedAddressPairs) {
+        checkNotNull(allowedAddressPairs, JSON_NOT_NULL);
+        ConcurrentMap<Integer, AllowedAddressPair> allowMaps = Maps
+                .newConcurrentMap();
+        int i = 0;
+        for (JsonNode node : allowedAddressPairs) {
+            IpAddress ip = IpAddress.valueOf(node.get("ip_address").asText());
+            MacAddress mac = MacAddress.valueOf(node.get("mac_address")
+                    .asText());
+            AllowedAddressPair allows = AllowedAddressPair
+                    .allowedAddressPair(ip, mac);
+            allowMaps.put(i, allows);
+            i++;
+        }
+        log.debug("The jsonNode of allowedAddressPairallow is {}"
+                + allowedAddressPairs.toString());
+        return Collections.unmodifiableCollection(allowMaps.values());
+    }
+
+    /**
+     * Returns a collection of virtualPorts.
+     *
+     * @param securityGroups the virtualPort jsonnode
+     * @return a collection of securityGroups
+     */
+    public Collection<SecurityGroup> jsonNodeToSecurityGroup(JsonNode securityGroups) {
+        checkNotNull(securityGroups, JSON_NOT_NULL);
+        ConcurrentMap<Integer, SecurityGroup> securMaps = Maps
+                .newConcurrentMap();
+        int i = 0;
+        for (JsonNode node : securityGroups) {
+            SecurityGroup securityGroup = SecurityGroup
+                    .securityGroup(node.asText());
+            securMaps.put(i, securityGroup);
+            i++;
+        }
+        return Collections.unmodifiableCollection(securMaps.values());
+    }
+
+    /**
+     * Returns a collection of fixedIps.
+     *
+     * @param fixedIpNode the fixedIp jsonnode
+     * @return a collection of SecurityGroup
+     */
+    public FixedIp jsonNodeToFixedIps(JsonNode fixedIpNode) {
+        SubnetId subnetId = SubnetId.subnetId(fixedIpNode.get("subnet_id")
+                .asText());
+        IpAddress ipAddress = IpAddress.valueOf(fixedIpNode.get("ip_address")
+                .asText());
+        FixedIp fixedIps = FixedIp.fixedIp(subnetId, ipAddress);
+        return fixedIps;
+    }
+
+    /**
+     * Returns VirtualPort State.
+     *
+     * @param state the virtualport state
+     * @return the virtualPort state
+     */
+    private State isState(String state) {
+        if (state.equals("ACTIVE")) {
+            return VirtualPort.State.ACTIVE;
+        } else {
+            return VirtualPort.State.DOWN;
+        }
+
+    }
+
+    /**
+     * Returns the specified item if that items is null; otherwise throws not
+     * found exception.
+     *
+     * @param item item to check
+     * @param <T> item type
+     * @param message not found message
+     * @return item if not null
+     * @throws org.onlab.util.ItemNotFoundException if item is null
+     */
+    protected <T> T nullIsNotFound(T item, String message) {
+        if (item == null) {
+            throw new ItemNotFoundException(message);
+        }
+        return item;
+    }
+}
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java
new file mode 100644 (file)
index 0000000..c81fc3d
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * VTN web that used rest to creat vtn resources.
+ */
+package org.onosproject.vtnweb.resources;
diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..4cc1245
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="ONOS" version="2.5">
+    <display-name>VTNRSC REST API v1.0</display-name>
+
+    <servlet>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
+            <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
+        </init-param>
+        <init-param>
+            <param-name>com.sun.jersey.config.property.classnames</param-name>
+            <param-value>
+                org.onosproject.vtnweb.resources.TenantNetworkWebResource,
+                org.onosproject.vtnweb.resources.SubnetWebResource,
+                org.onosproject.vtnweb.resources.VirtualPortWebResource
+            </param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/framework/src/onos/bgp/api/pom.xml b/framework/src/onos/bgp/api/pom.xml
new file mode 100755 (executable)
index 0000000..6fa1cc7
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-bgp</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-bgp-api</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS BGP controller subsystem API</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-bgpio</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.3</version>
+                <configuration>
+                    <artifactSet>
+                        <excludes>
+                            <exclude>io.netty:netty</exclude>
+                            <exclude>com.google.guava:guava</exclude>
+                            <exclude>org.slf4j:slfj-api</exclude>
+                            <exclude>ch.qos.logback:logback-core</exclude>
+                            <exclude>ch.qos.logback:logback-classic</exclude>
+                            <exclude>com.google.code.findbugs:annotations</exclude>
+                        </excludes>
+                    </artifactSet>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.onosproject.bgp.*,org.onosproject.bgpio.*,org.onosproject.bgp.controller
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPCfg.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPCfg.java
new file mode 100755 (executable)
index 0000000..46165d8
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller;
+
+import java.util.TreeMap;
+
+/**
+ * Abstraction of an BGP configuration. Manages the BGP configuration from CLI to the BGP controller.
+ */
+public interface BGPCfg {
+
+    enum State {
+        /**
+         * Signifies that its just created.
+         */
+        INIT,
+
+        /**
+         * Signifies that only IP Address is configured.
+         */
+        IP_CONFIGURED,
+
+        /**
+         * Signifies that only Autonomous System is configured.
+         */
+        AS_CONFIGURED,
+
+        /**
+         * Signifies that both IP and Autonomous System is configured.
+         */
+        IP_AS_CONFIGURED
+    }
+
+    /**
+     * Returns the status of the configuration based on this state certain operations like connection is handled.
+     *
+     * @return State of the configuration
+     */
+    State getState();
+
+    /**
+     * To set the current state of the configuration.
+     *
+     * @param state Configuration State enum
+     */
+    void setState(State state);
+
+    /**
+     * Get the status of the link state support for this BGP speaker.
+     *
+     * @return  true if the link state is supported else false
+     */
+    boolean getLsCapability();
+
+    /**
+     * Set the link state support to this BGP speaker.
+     *
+     * @param lscapability true value if link state is supported else false
+     */
+    void setLsCapability(boolean lscapability);
+
+    /**
+     * Get the status of the 32 bit AS support for this BGP speaker.
+     *
+     * @return true if the 32 bit AS number is supported else false
+     */
+    boolean getLargeASCapability();
+
+    /**
+     * Set the 32 bit AS support capability to this BGP speaker.
+     *
+     * @param largeAs  true value if the 32 bit AS is supported else false
+     */
+    void setLargeASCapability(boolean largeAs);
+
+    /**
+     * Set the AS number to which this BGP speaker belongs.
+     *
+     * @param localAs 16 or 32 bit AS number, length is dependent on the capability
+     */
+    void setAsNumber(int localAs);
+
+    /**
+     * Get the AS number to which this BGP speaker belongs.
+     *
+     * @return 16 or 32 bit AS number, length is dependent on the capability
+     */
+    int getAsNumber();
+
+    /**
+     * Get the connection retry count number.
+     *
+     * @return connection retry count if there is a connection error
+     */
+    int getMaxConnRetryCount();
+
+    /**
+     * Set the connection retry count.
+     *
+     * @param retryCount number of times to try to connect if there is any error
+     */
+    void setMaxConnRetryCout(int retryCount);
+
+    /**
+     * Get the connection retry time in seconds.
+     *
+     * @return connection retry time in seconds
+     */
+    int getMaxConnRetryTime();
+
+    /**
+     * Set the connection retry time in seconds.
+     *
+     * @param retryTime connection retry times in seconds
+     */
+    void setMaxConnRetryTime(int retryTime);
+
+    /**
+     * Set the keep alive timer for the connection.
+     *
+     * @param holdTime connection hold timer in seconds
+     */
+    void setHoldTime(short holdTime);
+
+    /**
+     * Returns the connection hold timer in seconds.
+     *
+     * @return connection hold timer in seconds
+     */
+    short getHoldTime();
+
+    /**
+     * Returns the maximum number of session supported.
+     *
+     * @return maximum number of session supported
+     */
+    int getMaxSession();
+
+    /**
+     * Set the maximum number of sessions to support.
+     *
+     * @param maxsession maximum number of session
+     */
+    void setMaxSession(int maxsession);
+
+    /**
+     * Returns the Router ID of this BGP speaker.
+     *
+     * @return IP address in string format
+     */
+    String getRouterId();
+
+    /**
+     * Set the Router ID of this BGP speaker.
+     *
+     * @param routerid  IP address in string format
+     */
+    void setRouterId(String routerid);
+
+    /**
+     * Add the BGP peer IP address and the AS number to which it belongs.
+     *
+     * @param routerid IP address in string format
+     * @param remoteAs  AS number to which it belongs
+     *
+     * @return true if added successfully else false
+     */
+    boolean addPeer(String routerid, int remoteAs);
+
+    /**
+     * Add the BGP peer IP address and the keep alive time.
+     *
+     * @param routerid IP address in string format
+     * @param holdTime keep alive time for the connection
+     *
+     * @return true if added successfully else false
+     */
+    boolean addPeer(String routerid, short holdTime);
+
+    /**
+     * Add the BGP peer IP address, the AS number to which it belongs and keep alive time.
+     *
+     * @param routerid IP address in string format
+     * @param remoteAs AS number to which it belongs
+     * @param holdTime keep alive time for the connection
+     *
+     * @return  true if added successfully else false
+     */
+    boolean addPeer(String routerid, int remoteAs, short holdTime);
+
+    /**
+     * Remove the BGP peer with this IP address.
+     *
+     * @param routerid router IP address
+     *
+     * @return true if removed successfully else false
+     */
+    boolean removePeer(String routerid);
+
+    /**
+     * Connect to BGP peer with this IP address.
+     *
+     * @param routerid router IP address
+     *
+     * @return  true of the configuration is found and able to connect else false
+     */
+    boolean connectPeer(String routerid);
+
+    /**
+     * Disconnect this BGP peer with this IP address.
+     *
+     * @param routerid  router IP address in string format
+     *
+     * @return true if the configuration is found and able to disconnect else false
+     */
+    boolean disconnectPeer(String routerid);
+
+    /**
+     * Returns the peer tree information.
+     *
+     * @return return the tree map with IP as key and BGPPeerCfg as object
+     */
+    TreeMap<String, BGPPeerCfg> displayPeers();
+
+    /**
+     * Return the BGP Peer information with this matching IP.
+     *
+     * @param routerid router IP address in string format
+     *
+     * @return BGPPeerCfg object
+     */
+    BGPPeerCfg displayPeers(String routerid);
+
+    /**
+     * Check if this BGP peer is configured.
+     *
+     * @param routerid  router IP address in string format
+     *
+     * @return  true if configured exists else false
+     */
+    boolean isPeerConfigured(String routerid);
+
+    /**
+     * Check if this BGP speaker is having connection with the peer.
+     *
+     * @param routerid router IP address in string format
+     *
+     * @return true if the connection exists else false
+     */
+    boolean isPeerConnected(String routerid);
+
+    /**
+     * Return the peer tree map.
+     *
+     * @return return the tree map with IP as key and BGPPeerCfg as object
+     */
+    TreeMap<String, BGPPeerCfg> getPeerTree();
+
+    /**
+     * Set the current connection state information.
+     *
+     * @param routerid  router IP address in string format
+     * @param state state information
+     */
+    void setPeerConnState(String routerid, BGPPeerCfg.State state);
+
+    /**
+     * Check if the peer can be connected or not.
+     *
+     * @param routerid  router IP address in string format
+     *
+     * @return  true if the peer can be connected else false
+     */
+    boolean isPeerConnectable(String routerid);
+
+    /**
+     * Get the current peer connection state information.
+     *
+     * @param routerid router IP address in string format
+     *
+     * @return state information
+     */
+    BGPPeerCfg.State getPeerConnState(String routerid);
+}
diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPController.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPController.java
new file mode 100755 (executable)
index 0000000..6d75812
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgp.controller;
+
+import org.onosproject.bgpio.protocol.BGPMessage;
+
+/**
+ * Abstraction of an BGP controller. Serves as a one stop shop for obtaining BGP devices and (un)register listeners
+ * on bgp events
+ */
+public interface BGPController {
+
+    /**
+     * Send a message to a particular bgp peer.
+     *
+     * @param bgpId the id of the peer to send message.
+     * @param msg the message to send
+     */
+    void writeMsg(BGPId bgpId, BGPMessage msg);
+
+    /**
+     * Process a message and notify the appropriate listeners.
+     *
+     * @param bgpId id of the peer the message arrived on
+     * @param msg the message to process.
+     */
+    void processBGPPacket(BGPId bgpId, BGPMessage msg);
+
+    /**
+     * Get the BGPConfig class to the caller.
+     *
+     * @return configuration object
+     */
+    BGPCfg getConfig();
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPId.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPId.java
new file mode 100755 (executable)
index 0000000..636e72f
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgp.controller;
+
+import org.onlab.packet.IpAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * The class representing a network peer bgp ip.
+ * This class is immutable.
+ */
+public final class BGPId {
+
+    private static final String SCHEME = "bgp";
+    private static final long UNKNOWN = 0;
+    private final IpAddress ipAddress;
+
+    /**
+     * Private constructor.
+     */
+    private BGPId(IpAddress ipAddress) {
+        this.ipAddress = ipAddress;
+    }
+
+    /**
+     * Create a BGPId from ip address.
+     *
+     * @param ipAddress IP address
+     * @return object of BGPId
+     */
+    public static BGPId bgpId(IpAddress ipAddress) {
+        return new BGPId(ipAddress);
+    }
+
+    /**
+     * Returns the ip address.
+     *
+     * @return ipAddress
+     */
+    public IpAddress ipAddress() {
+        return ipAddress;
+    }
+
+    /**
+     * Convert the BGPId value to a ':' separated hexadecimal string.
+     *
+     * @return the BGPId value as a ':' separated hexadecimal string.
+     */
+    @Override
+    public String toString() {
+        return ipAddress.toString();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof BGPId)) {
+            return false;
+        }
+
+        BGPId otherBGPid = (BGPId) other;
+        return Objects.equals(ipAddress, otherBGPid.ipAddress);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ipAddress);
+    }
+
+    /**
+     * Returns BGPId created from the given device URI.
+     *
+     * @param uri device URI
+     * @return object of BGPId
+     */
+    public static BGPId bgpId(URI uri) {
+        checkArgument(uri.getScheme().equals(SCHEME), "Unsupported URI scheme");
+        return new BGPId(IpAddress.valueOf(uri.getSchemeSpecificPart()));
+    }
+
+    /**
+     * Produces device URI from the given DPID.
+     *
+     * @param bgpId device bgpId
+     * @return device URI
+     */
+    public static URI uri(BGPId bgpId) {
+        return uri(bgpId.ipAddress());
+    }
+
+    /**
+     * Produces device URI from the given DPID long.
+     *
+     * @param ipAddress device ip address
+     * @return device URI
+     */
+    public static URI uri(IpAddress ipAddress) {
+        try {
+            return new URI(SCHEME, ipAddress.toString(), null);
+        } catch (URISyntaxException e) {
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPacketStats.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPacketStats.java
new file mode 100755 (executable)
index 0000000..95f83a2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgp.controller;
+
+/**
+ * A representation of a packet context which allows any provider to view a packet in event, but may block the response
+ * to the event if blocked has been called. This packet context can be used to react to the packet in event with a
+ * packet out.
+ */
+public interface BGPPacketStats {
+    /**
+     * Returns the count for no of packets sent out.
+     *
+     * @return int value of no of packets sent
+     */
+    int outPacketCount();
+
+    /**
+     * Returns the count for no of packets received.
+     *
+     * @return int value of no of packets sent
+     */
+    int inPacketCount();
+
+    /**
+     * Returns the count for no of wrong packets received.
+     *
+     * @return int value of no of wrong packets received
+     */
+    int wrongPacketCount();
+
+    /**
+     * Returns the time.
+     *
+     * @return the time
+     */
+    long getTime();
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPeerCfg.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPeerCfg.java
new file mode 100755 (executable)
index 0000000..87ec031
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller;
+
+/**
+ * BGP Peer configuration information.
+ */
+public interface BGPPeerCfg {
+
+    enum State {
+
+        /**
+         * Signifies that peer connection is idle.
+         */
+        IDLE,
+
+        /**
+         * Signifies that connection is initiated.
+         */
+        CONNECT,
+
+        /**
+         * Signifies that state is active and connection can be established.
+         */
+        ACTIVE,
+
+        /**
+         * Signifies that open is sent and anticipating reply.
+         */
+        OPENSENT,
+
+        /**
+         * Signifies that peer sent the open message as reply.
+         */
+        OPENCONFIRM,
+
+        /**
+         * Signifies that all the negotiation is successful and ready to exchange other messages.
+         */
+        ESTABLISHED,
+
+        /**
+         * Signifies that invalid state.
+         */
+        INVALID
+    }
+
+    /**
+     * Returns the connection State information of the peer.
+     *
+     * @return
+     *          enum state is returned
+     */
+    State getState();
+
+    /**
+     * Set the connection state information of the peer.
+     *
+     * @param state
+     *          enum state
+     */
+    void setState(State state);
+
+    /**
+     * Returns the connection is initiated from us or not.
+     *
+     * @return
+     *          true if the connection is initiated by this peer, false if it has been received.
+     */
+    boolean getSelfInnitConnection();
+
+    /**
+     * Set the connection is initiated from us or not.
+     *
+     * @param selfInit
+     *          true if the connection is initiated by this peer, false if it has been received.
+     */
+    void setSelfInnitConnection(boolean selfInit);
+
+    /**
+     * Returns the AS number to which this peer belongs.
+     *
+     * @return
+     *          AS number
+     */
+    int getAsNumber();
+
+    /**
+     * Set the AS number to which this peer belongs.
+     *
+     * @param asNumber
+     *          AS number
+     */
+    void setAsNumber(int asNumber);
+
+    /**
+     * Get the keep alive timer value configured.
+     *
+     * @return
+     *          keep alive timer value in seconds
+     */
+    short getHoldtime();
+
+    /**
+     * Set the keep alive timer value.
+     *
+     * @param holdTime
+     *          keep alive timer value in seconds
+     */
+    void setHoldtime(short holdTime);
+
+    /**
+     * Return the connection type eBGP or iBGP.
+     *
+     * @return
+     *          true if iBGP, false if it is eBGP
+     */
+    boolean getIsIBgp();
+
+    /**
+     * Set the connection type eBGP or iBGP.
+     *
+     * @param isIBgp
+     *          true if iBGP, false if it is eBGP
+     */
+    void setIsIBgp(boolean isIBgp);
+
+    /**
+     * Return the peer router IP address.
+     *
+     * @return
+     *          IP address in string format
+     */
+    String getPeerRouterId();
+
+    /**
+     * Set the peer router IP address.
+     *
+     * @param peerId
+     *          IP address in string format
+     */
+    void setPeerRouterId(String peerId);
+
+    /**
+     * Set the peer router IP address and AS number.
+     *
+     * @param peerId
+     *          IP address in string format
+     * @param asNumber
+     *          AS number
+     */
+    void setPeerRouterId(String peerId, int asNumber);
+}
diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/package-info.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/package-info.java
new file mode 100755 (executable)
index 0000000..4dd775b
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * BGP controller API.
+ */
+package org.onosproject.bgp.controller;
diff --git a/framework/src/onos/bgp/bgpio/pom.xml b/framework/src/onos/bgp/bgpio/pom.xml
new file mode 100755 (executable)
index 0000000..5d67f18
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-bgp</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-bgpio</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS BGPio Protocol subsystem</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/BGPParseException.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/BGPParseException.java
new file mode 100755 (executable)
index 0000000..62427a4
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.exceptions;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ * Custom Exception for BGP IO.
+ */
+public class BGPParseException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+    private byte errorCode;
+    private byte errorSubCode;
+    private ChannelBuffer data;
+
+    /**
+     * Default constructor to create a new exception.
+     */
+    public BGPParseException() {
+        super();
+    }
+
+    /**
+     * Constructor to create exception from message and cause.
+     *
+     * @param message  the detail of exception in string
+     * @param cause underlying cause of the error
+     */
+    public BGPParseException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructor to create exception from message.
+     *
+     * @param message the detail of exception in string
+     */
+    public BGPParseException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor to create exception from cause.
+     *
+     * @param cause underlying cause of the error
+     */
+    public BGPParseException(final Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor to create exception from error code and error subcode.
+     *
+     * @param errorCode error code of BGP message
+     * @param errorSubCode error subcode of BGP message
+     * @param data error data of BGP message
+     */
+    public BGPParseException(final byte errorCode, final byte errorSubCode, final ChannelBuffer data) {
+        super();
+        this.errorCode = errorCode;
+        this.errorSubCode = errorSubCode;
+        this.data = data;
+    }
+
+    /**
+     * Returns errorcode for this exception.
+     *
+     * @return errorcode for this exception
+     */
+    public byte getErrorCode() {
+        return this.errorCode;
+    }
+
+    /**
+     * Returns error Subcode for this exception.
+     *
+     * @return error Subcode for this exception
+     */
+    public byte getErrorSubCode() {
+        return this.errorSubCode;
+    }
+
+    /**
+     * Returns error data for this exception.
+     *
+     * @return error data for this exception
+     */
+    public ChannelBuffer getData() {
+        return this.data;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/package-info.java
new file mode 100755 (executable)
index 0000000..78b2807
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * BGP custom exceptions.
+ */
+package org.onosproject.bgpio.exceptions;
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPKeepaliveMsg.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPKeepaliveMsg.java
new file mode 100644 (file)
index 0000000..c8aef36
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.types.BGPHeader;
+
+/**
+ * Abstraction of an entity providing BGP Keepalive Message.
+ */
+public interface BGPKeepaliveMsg extends BGPMessage {
+
+    @Override
+    BGPVersion getVersion();
+
+    @Override
+    BGPType getType();
+
+    @Override
+    void writeTo(ChannelBuffer channelBuffer);
+
+    @Override
+    BGPHeader getHeader();
+
+    /**
+     * Builder interface with get and set functions to build Keepalive message.
+     */
+    interface Builder extends BGPMessage.Builder {
+
+        @Override
+        BGPKeepaliveMsg build();
+
+        @Override
+        BGPVersion getVersion();
+
+        @Override
+        BGPType getType();
+
+        @Override
+        Builder setHeader(BGPHeader bgpMsgHeader);
+
+        @Override
+        BGPHeader getHeader();
+    }
+}
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessage.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessage.java
new file mode 100644 (file)
index 0000000..a5d8154
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.types.BGPHeader;
+
+/**
+ * Abstraction of an entity providing BGP Messages.
+ */
+public interface BGPMessage extends Writeable {
+    /**
+     * Returns BGP Header of BGP Message.
+     *
+     * @return BGP Header of BGP Message
+     */
+    BGPHeader getHeader();
+
+    /**
+     * Returns version of BGP Message.
+     *
+     * @return version of BGP Message
+     */
+    BGPVersion getVersion();
+
+    /**
+     * Returns BGP Type of BGP Message.
+     *
+     * @return BGP Type of BGP Message
+     */
+    BGPType getType();
+
+    @Override
+    void writeTo(ChannelBuffer cb) throws BGPParseException;
+
+    /**
+     * Builder interface with get and set functions to build BGP Message.
+     */
+    interface Builder {
+        /**
+         * Builds BGP Message.
+         *
+         * @return BGP Message
+         * @throws BGPParseException while building bgp message
+         */
+        BGPMessage build() throws BGPParseException;
+
+        /**
+         * Returns BGP Version of BGP Message.
+         *
+         * @return BGP Version of BGP Message
+         */
+        BGPVersion getVersion();
+
+        /**
+         * Returns BGP Type of BGP Message.
+         *
+         * @return BGP Type of BGP Message
+         */
+        BGPType getType();
+
+        /**
+         * Returns BGP Header of BGP Message.
+         *
+         * @return BGP Header of BGP Message
+         */
+        BGPHeader getHeader();
+
+        /**
+         * Sets BgpHeader and return its builder.
+         *
+         * @param bgpMsgHeader BGP Message Header
+         * @return builder by setting BGP message header
+         */
+        Builder setHeader(BGPHeader bgpMsgHeader);
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageReader.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageReader.java
new file mode 100755 (executable)
index 0000000..18b8f58
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.types.BGPHeader;
+
+/**
+ * Abstraction of an entity providing BGP Message Reader.
+ */
+public interface BGPMessageReader<T> {
+
+    /**
+     * Reads the Objects in the BGP Message and Returns BGP Message.
+     *
+     * @param cb Channel Buffer
+     * @param bgpHeader BGP message header
+     * @return BGP Message
+     * @throws BGPParseException while parsing BGP message.
+     */
+    T readFrom(ChannelBuffer cb, BGPHeader bgpHeader) throws BGPParseException;
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageWriter.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageWriter.java
new file mode 100644 (file)
index 0000000..11f161c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+
+/**
+ * Abstraction of an entity providing BGP Message Writer.
+ */
+public interface BGPMessageWriter<T> {
+
+    /**
+     * Writes the Objects of the BGP Message into Channel Buffer.
+     *
+     * @param cb Channel Buffer
+     * @param message BGP Message
+     * @throws BGPParseException
+     *                     While writing message
+     */
+     void write(ChannelBuffer cb, T message) throws BGPParseException;
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java
new file mode 100644 (file)
index 0000000..c41e5eb
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol;
+
+import java.util.LinkedList;
+
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.types.BGPHeader;
+import org.onosproject.bgpio.types.BGPValueType;
+
+/**
+ * Abstraction of an entity providing BGP Open Message.
+ */
+public interface BGPOpenMsg extends BGPMessage {
+
+    @Override
+    BGPHeader getHeader();
+
+    @Override
+    BGPVersion getVersion();
+
+    @Override
+    BGPType getType();
+
+    /**
+     * Returns hold time of Open Message.
+     *
+     * @return hold time of Open Message
+     */
+    short getHoldTime();
+
+    /**
+     * Returns AS Number of Open Message.
+     *
+     * @return AS Number of Open Message
+     */
+    short getAsNumber();
+
+    /**
+     * Returns BGP Identifier of Open Message.
+     *
+     * @return BGP Identifier of Open Message
+     */
+    int getBgpId();
+
+    /**
+     * Returns capabilities of Open Message.
+     *
+     * @return capabilities of Open Message
+     */
+    LinkedList<BGPValueType> getCapabilityTlv();
+
+    /**
+     * Builder interface with get and set functions to build Open message.
+     */
+    interface Builder extends BGPMessage.Builder {
+
+        @Override
+        BGPOpenMsg build() throws BGPParseException;
+
+        @Override
+        BGPHeader getHeader();
+
+        @Override
+        BGPVersion getVersion();
+
+        @Override
+        BGPType getType();
+
+        /**
+         * Returns hold time of Open Message.
+         *
+         * @return hold time of Open Message
+         */
+        short getHoldTime();
+
+        /**
+         * Sets hold time in Open Message and return its builder.
+         *
+         * @param holdtime
+         *           hold timer value in open message
+         * @return builder by setting hold time
+         */
+        Builder setHoldTime(short holdtime);
+
+        /**
+         * Returns as number of Open Message.
+         *
+         * @return as number of Open Message
+         */
+        short getAsNumber();
+
+        /**
+         * Sets AS number in Open Message and return its builder.
+         *
+         * @param asNumber
+         *           as number in open message
+         * @return builder by setting asNumber
+         */
+        Builder setAsNumber(short asNumber);
+
+        /**
+         * Returns BGP Identifier of Open Message.
+         *
+         * @return BGP Identifier of Open Message
+         */
+        int getBgpId();
+
+        /**
+         * Sets BGP Identifier in Open Message and return its builder.
+         *
+         * @param bgpId
+         *           BGP Identifier in open message
+         * @return builder by setting BGP Identifier
+         */
+        Builder setBgpId(int bgpId);
+
+        /**
+         * Returns capabilities of Open Message.
+         *
+         * @return capabilities of Open Message
+         */
+        LinkedList<BGPValueType> getCapabilityTlv();
+
+        /**
+         * Sets capabilities in Open Message and return its builder.
+         *
+         * @param capabilityTlv
+         *           capabilities in open message
+         * @return builder by setting capabilities
+         */
+        Builder setCapabilityTlv(LinkedList<BGPValueType> capabilityTlv);
+
+        @Override
+        Builder setHeader(BGPHeader bgpMsgHeader);
+    }
+}
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPType.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPType.java
new file mode 100755 (executable)
index 0000000..d334915
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol;
+
+/**
+ * Enum to Provide the Different types of BGP messages.
+ */
+public enum BGPType {
+
+    NONE(0), OPEN(1), UPDATE(2), NOTIFICATION(3), KEEP_ALIVE(4);
+
+    int value;
+
+    /**
+     * Assign value with the value val as the types of BGP message.
+     *
+     * @param val type of BGP message
+     */
+    BGPType(int val) {
+        value = val;
+    }
+
+    /**
+     * Returns value as type of BGP message.
+     *
+     * @return value type of BGP message
+     */
+    public byte getType() {
+        return (byte) value;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPVersion.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPVersion.java
new file mode 100755 (executable)
index 0000000..97bc7dc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol;
+
+/**
+ * Enum to provide BGP Message Version.
+ */
+public enum BGPVersion {
+
+    BGP_4(4);
+
+    public final int packetVersion;
+
+    /**
+     * Assign BGP PacketVersion with specified packetVersion.
+     *
+     * @param packetVersion version of BGP
+     */
+    BGPVersion(final int packetVersion) {
+        this.packetVersion = packetVersion;
+    }
+
+    /**
+     * Returns Packet version of BGP Message.
+     *
+     * @return packetVersion
+     */
+    public int getPacketVersion() {
+        return packetVersion;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/IGPRouterID.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/IGPRouterID.java
new file mode 100644 (file)
index 0000000..377d12b
--- /dev/null
@@ -0,0 +1,23 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.bgpio.protocol;\r
+\r
+/**\r
+ * Provides Abstraction of IGP RouterID TLV.\r
+ */\r
+public interface IGPRouterID {\r
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/Writeable.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/Writeable.java
new file mode 100755 (executable)
index 0000000..72df7f3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+
+/**
+ * Abstraction of an entity providing functionality to write byte streams of
+ * Messages to channel buffer.
+ */
+public interface Writeable {
+
+    /**
+     * Writes byte streams of messages to channel buffer.
+     *
+     * @param cb channelBuffer
+     * @throws BGPParseException when error occurs while writing BGP message to channel buffer
+     */
+    void writeTo(ChannelBuffer cb) throws BGPParseException;
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/BGPPrefixLSIdentifier.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/BGPPrefixLSIdentifier.java
new file mode 100644 (file)
index 0000000..4fef47f
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol.link_state;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.types.BGPErrorType;
+import org.onosproject.bgpio.types.BGPValueType;
+import org.onosproject.bgpio.types.IPReachabilityInformationTlv;
+import org.onosproject.bgpio.types.OSPFRouteTypeTlv;
+import org.onosproject.bgpio.types.attr.BgpAttrNodeMultiTopologyId;
+import org.onosproject.bgpio.util.UnSupportedAttribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides Implementation of Local node descriptors and prefix descriptors.
+ */
+public class BGPPrefixLSIdentifier {
+
+    protected static final Logger log = LoggerFactory.getLogger(BGPPrefixLSIdentifier.class);
+    public static final int TYPE_AND_LEN = 4;
+    private NodeDescriptors localNodeDescriptors;
+    private LinkedList<BGPValueType> prefixDescriptor;
+
+    /**
+     * Resets parameters.
+     */
+    public BGPPrefixLSIdentifier() {
+        this.localNodeDescriptors = null;
+        this.prefixDescriptor = null;
+    }
+
+    /**
+     * Constructor to initialize parameters.
+     *
+     * @param localNodeDescriptors Local node descriptors
+     * @param prefixDescriptor Prefix Descriptors
+     */
+    public BGPPrefixLSIdentifier(NodeDescriptors localNodeDescriptors, LinkedList<BGPValueType> prefixDescriptor) {
+        this.localNodeDescriptors = localNodeDescriptors;
+        this.prefixDescriptor = prefixDescriptor;
+    }
+
+    /**
+     * Reads the channel buffer and parses Prefix Identifier.
+     *
+     * @param cb ChannelBuffer
+     * @param protocolId protocol ID
+     * @return object of this class
+     * @throws BGPParseException while parsing Prefix Identifier
+     */
+    public static BGPPrefixLSIdentifier parsePrefixIdendifier(ChannelBuffer cb, byte protocolId)
+            throws BGPParseException {
+        //Parse Local Node descriptor
+        NodeDescriptors localNodeDescriptors = new NodeDescriptors();
+        localNodeDescriptors = parseLocalNodeDescriptors(cb, protocolId);
+
+        //Parse Prefix descriptor
+        LinkedList<BGPValueType> prefixDescriptor = new LinkedList<>();
+        prefixDescriptor = parsePrefixDescriptors(cb);
+        return new BGPPrefixLSIdentifier(localNodeDescriptors, prefixDescriptor);
+    }
+
+    /**
+     * Parse local node descriptors.
+     *
+     * @param cb ChannelBuffer
+     * @param protocolId protocol identifier
+     * @return LocalNodeDescriptors
+     * @throws BGPParseException while parsing local node descriptors
+     */
+    public static NodeDescriptors parseLocalNodeDescriptors(ChannelBuffer cb, byte protocolId)
+                                                                 throws BGPParseException {
+        ChannelBuffer tempBuf = cb;
+        short type = cb.readShort();
+        short length = cb.readShort();
+        if (cb.readableBytes() < length) {
+            //length + 4 implies data contains type, length and value
+            throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR,
+                    tempBuf.readBytes(cb.readableBytes() + TYPE_AND_LEN));
+        }
+        NodeDescriptors localNodeDescriptors = new NodeDescriptors();
+        ChannelBuffer tempCb = cb.readBytes(length);
+
+        if (type == NodeDescriptors.LOCAL_NODE_DES_TYPE) {
+            localNodeDescriptors = NodeDescriptors.read(tempCb, length, type, protocolId);
+        } else {
+            throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                                           BGPErrorType.MALFORMED_ATTRIBUTE_LIST, null);
+        }
+        return localNodeDescriptors;
+    }
+
+    /**
+     * Parse list of prefix descriptors.
+     *
+     * @param cb ChannelBuffer
+     * @return list of prefix descriptors
+     * @throws BGPParseException while parsing list of prefix descriptors
+     */
+    public static LinkedList<BGPValueType> parsePrefixDescriptors(ChannelBuffer cb) throws BGPParseException {
+        LinkedList<BGPValueType> prefixDescriptor = new LinkedList<>();
+        BGPValueType tlv = null;
+        boolean isIpReachInfo = false;
+        ChannelBuffer tempCb;
+        int count = 0;
+
+        while (cb.readableBytes() > 0) {
+            ChannelBuffer tempBuf = cb;
+            short type = cb.readShort();
+            short length = cb.readShort();
+            if (cb.readableBytes() < length) {
+                //length + 4 implies data contains type, length and value
+                throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR,
+                        tempBuf.readBytes(cb.readableBytes() + TYPE_AND_LEN));
+            }
+            tempCb = cb.readBytes(length);
+            switch (type) {
+            case OSPFRouteTypeTlv.TYPE:
+                tlv = OSPFRouteTypeTlv.read(tempCb);
+                break;
+            case IPReachabilityInformationTlv.TYPE:
+                tlv = IPReachabilityInformationTlv.read(tempCb, length);
+                isIpReachInfo = true;
+                break;
+            case BgpAttrNodeMultiTopologyId.ATTRNODE_MULTITOPOLOGY:
+                tlv = BgpAttrNodeMultiTopologyId.read(tempCb);
+                count = count + 1;
+                if (count > 1) {
+                    //length + 4 implies data contains type, length and value
+                    throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                           BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR, tempBuf.readBytes(length + TYPE_AND_LEN));
+                }
+                break;
+            default:
+                UnSupportedAttribute.skipBytes(tempCb, length);
+            }
+            prefixDescriptor.add(tlv);
+        }
+
+        if (!isIpReachInfo) {
+            throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR,
+                    null);
+        }
+        return prefixDescriptor;
+    }
+
+    /**
+     * Returns local node descriptors.
+     *
+     * @return local node descriptors
+     */
+    public NodeDescriptors getLocalNodeDescriptors() {
+        return this.localNodeDescriptors;
+    }
+
+    /**
+     * Returns Prefix descriptors.
+     *
+     * @return Prefix descriptors
+     */
+    public LinkedList<BGPValueType> getPrefixdescriptor() {
+        return this.prefixDescriptor;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(prefixDescriptor.hashCode(), localNodeDescriptors);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof BGPPrefixLSIdentifier) {
+            int countObjSubTlv = 0;
+            int countOtherSubTlv = 0;
+            boolean isCommonSubTlv = true;
+            BGPPrefixLSIdentifier other = (BGPPrefixLSIdentifier) obj;
+
+            Iterator<BGPValueType> objListIterator = other.prefixDescriptor.iterator();
+            countOtherSubTlv = other.prefixDescriptor.size();
+            countObjSubTlv = prefixDescriptor.size();
+            if (countObjSubTlv != countOtherSubTlv) {
+                return false;
+            } else {
+                while (objListIterator.hasNext() && isCommonSubTlv) {
+                    BGPValueType subTlv = objListIterator.next();
+                    isCommonSubTlv = Objects.equals(prefixDescriptor.contains(subTlv),
+                            other.prefixDescriptor.contains(subTlv));
+                }
+                return isCommonSubTlv && Objects.equals(this.localNodeDescriptors, other.localNodeDescriptors);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("localNodeDescriptors", localNodeDescriptors)
+                .add("prefixDescriptor", prefixDescriptor)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/NodeDescriptors.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/NodeDescriptors.java
new file mode 100644 (file)
index 0000000..a03b2ba
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol.link_state;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.types.AreaIDTlv;
+import org.onosproject.bgpio.types.AutonomousSystemTlv;
+import org.onosproject.bgpio.types.BGPErrorType;
+import org.onosproject.bgpio.types.BGPLSIdentifierTlv;
+import org.onosproject.bgpio.types.BGPValueType;
+import org.onosproject.bgpio.types.IsIsNonPseudonode;
+import org.onosproject.bgpio.types.IsIsPseudonode;
+import org.onosproject.bgpio.types.OSPFNonPseudonode;
+import org.onosproject.bgpio.types.OSPFPseudonode;
+import org.onosproject.bgpio.util.UnSupportedAttribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides Local and Remote NodeDescriptors which contains Node Descriptor Sub-TLVs.
+ */
+public class NodeDescriptors {
+
+    /*
+     *Reference :draft-ietf-idr-ls-distribution-11
+          0                   1                   2                   3
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |              Type             |             Length            |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |                                                               |
+         //              Node Descriptor Sub-TLVs (variable)            //
+         |                                                               |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                   Figure : Local or Remote Node Descriptors TLV format
+     */
+
+    protected static final Logger log = LoggerFactory.getLogger(NodeDescriptors.class);
+
+    public static final short LOCAL_NODE_DES_TYPE = 256;
+    public static final short REMOTE_NODE_DES_TYPE = 257;
+    public static final short IGP_ROUTERID_TYPE = 515;
+    public static final short IS_IS_LEVEL_1_PROTOCOL_ID = 1;
+    public static final short IS_IS_LEVEL_2_PROTOCOL_ID = 2;
+    public static final short OSPF_V2_PROTOCOL_ID = 3;
+    public static final short OSPF_V3_PROTOCOL_ID = 6;
+    public static final int TYPE_AND_LEN = 4;
+    public static final int ISISNONPSEUDONODE_LEN = 6;
+    public static final int ISISPSEUDONODE_LEN = 7;
+    public static final int OSPFNONPSEUDONODE_LEN = 4;
+    public static final int OSPFPSEUDONODE_LEN = 8;
+    private LinkedList<BGPValueType> subTlvs;
+    private short deslength;
+    private short desType;
+
+    /**
+     * Resets parameters.
+     */
+    public NodeDescriptors() {
+        this.subTlvs = null;
+        this.deslength = 0;
+        this.desType = 0;
+    }
+
+    /**
+     * Constructor to initialize parameters.
+     *
+     * @param subTlvs list of subTlvs
+     * @param deslength Descriptors length
+     * @param desType local node descriptor or remote node descriptor type
+     */
+    public NodeDescriptors(LinkedList<BGPValueType> subTlvs, short deslength, short desType) {
+        this.subTlvs = subTlvs;
+        this.deslength = deslength;
+        this.desType = desType;
+    }
+
+    /**
+     * Returns list of subTlvs.
+     *
+     * @return subTlvs list of subTlvs
+     */
+    public LinkedList<BGPValueType> getSubTlvs() {
+        return subTlvs;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(subTlvs.hashCode());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof NodeDescriptors) {
+            int countObjSubTlv = 0;
+            int countOtherSubTlv = 0;
+            boolean isCommonSubTlv = true;
+            NodeDescriptors other = (NodeDescriptors) obj;
+            Iterator<BGPValueType> objListIterator = other.subTlvs.iterator();
+            countOtherSubTlv = other.subTlvs.size();
+            countObjSubTlv = subTlvs.size();
+            if (countObjSubTlv != countOtherSubTlv) {
+                return false;
+            } else {
+                while (objListIterator.hasNext() && isCommonSubTlv) {
+                    BGPValueType subTlv = objListIterator.next();
+                    isCommonSubTlv = Objects.equals(subTlvs.contains(subTlv), other.subTlvs.contains(subTlv));
+                }
+                return isCommonSubTlv;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Reads node descriptors Sub-TLVs.
+     *
+     * @param cb ChannelBuffer
+     * @param desLength node descriptor length
+     * @param desType local node descriptor or remote node descriptor type
+     * @param protocolId protocol ID
+     * @return object of NodeDescriptors
+     * @throws BGPParseException while parsing node descriptors
+     */
+    public static NodeDescriptors read(ChannelBuffer cb, short desLength, short desType, byte protocolId)
+            throws BGPParseException {
+        LinkedList<BGPValueType> subTlvs;
+        subTlvs = new LinkedList<>();
+        BGPValueType tlv = null;
+
+        while (cb.readableBytes() > 0) {
+            ChannelBuffer tempBuf = cb;
+            short type = cb.readShort();
+            short length = cb.readShort();
+            if (cb.readableBytes() < length) {
+                throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR,
+                        tempBuf.readBytes(cb.readableBytes() + TYPE_AND_LEN));
+            }
+            ChannelBuffer tempCb = cb.readBytes(length);
+            switch (type) {
+            case AutonomousSystemTlv.TYPE:
+                tlv = AutonomousSystemTlv.read(tempCb);
+                break;
+            case BGPLSIdentifierTlv.TYPE:
+                tlv = BGPLSIdentifierTlv.read(tempCb);
+                break;
+            case AreaIDTlv.TYPE:
+                tlv = AreaIDTlv.read(tempCb);
+                break;
+            case IGP_ROUTERID_TYPE:
+                if (protocolId == IS_IS_LEVEL_1_PROTOCOL_ID || protocolId == IS_IS_LEVEL_2_PROTOCOL_ID) {
+                    if (length == ISISNONPSEUDONODE_LEN) {
+                        tlv = IsIsNonPseudonode.read(tempCb);
+                    } else if (length == ISISPSEUDONODE_LEN) {
+                        tlv = IsIsPseudonode.read(tempCb);
+                    }
+                } else if (protocolId == OSPF_V2_PROTOCOL_ID || protocolId == OSPF_V3_PROTOCOL_ID) {
+                    if (length == OSPFNONPSEUDONODE_LEN) {
+                        tlv = OSPFNonPseudonode.read(tempCb);
+                    } else if (length == OSPFPSEUDONODE_LEN) {
+                        tlv = OSPFPseudonode.read(tempCb);
+                    }
+                }
+                break;
+            default:
+                UnSupportedAttribute.skipBytes(tempCb, length);
+            }
+            subTlvs.add(tlv);
+        }
+        return new NodeDescriptors(subTlvs, desLength, desType);
+    }
+
+    /**
+     * Returns node descriptors length.
+     *
+     * @return node descriptors length
+     */
+    public short getLength() {
+        return this.deslength;
+    }
+
+    /**
+     * Returns node descriptors type.
+     *
+     * @return node descriptors type
+     */
+    public short getType() {
+        return this.desType;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("desType", desType)
+                .add("deslength", deslength)
+                .add("subTlvs", subTlvs)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/package-info.java
new file mode 100755 (executable)
index 0000000..d2a2ccf
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * BGP Protocol specific link state details.
+ */
+package org.onosproject.bgpio.protocol.link_state;
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/package-info.java
new file mode 100755 (executable)
index 0000000..723b31b
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * BGP Protocol specific components.
+ */
+package org.onosproject.bgpio.protocol;
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPKeepaliveMsgVer4.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPKeepaliveMsgVer4.java
new file mode 100644 (file)
index 0000000..a6668b3
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.protocol.ver4;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.protocol.BGPKeepaliveMsg;
+import org.onosproject.bgpio.protocol.BGPMessageReader;
+import org.onosproject.bgpio.protocol.BGPMessageWriter;
+import org.onosproject.bgpio.types.BGPHeader;
+import org.onosproject.bgpio.protocol.BGPType;
+import org.onosproject.bgpio.protocol.BGPVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides BGP keep alive message.
+ */
+class BGPKeepaliveMsgVer4 implements BGPKeepaliveMsg {
+
+    /*
+    <Keepalive Message>::= <Common Header>
+    A KEEPALIVE message consists of only the message header and has a
+    length of 19 octets.
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    +                                                               +
+    |                                                               |
+    +                                                               +
+    |                           Marker                              |
+    +                                                               +
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |          Length               |      Type     |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    REFERENCE : RFC 4271
+    */
+
+    protected static final Logger log = LoggerFactory
+            .getLogger(BGPKeepaliveMsgVer4.class);
+
+    private BGPHeader bgpMsgHeader;
+    public static final byte PACKET_VERSION = 4;
+    public static final int PACKET_MINIMUM_LENGTH = 19;
+    public static final int MARKER_LENGTH = 16;
+    public static final BGPType MSG_TYPE = BGPType.KEEP_ALIVE;
+    public static byte[] marker = new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+                                              (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+                                              (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+                                              (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+
+    public static final BGPKeepaliveMsgVer4.Reader READER = new Reader();
+
+    /**
+     * Reader class for reading BGP keepalive message from channel buffer.
+     */
+    static class Reader implements BGPMessageReader<BGPKeepaliveMsg> {
+
+        @Override
+        public BGPKeepaliveMsg readFrom(ChannelBuffer cb, BGPHeader bgpHeader)
+                throws BGPParseException {
+
+            /* bgpHeader is not required in case of keepalive message and
+            Header is already read and no other fields except header in keepalive message.*/
+            return new BGPKeepaliveMsgVer4();
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    BGPKeepaliveMsgVer4() {
+    }
+
+    /**
+     * Builder class for BGP keepalive message.
+     */
+    static class Builder implements BGPKeepaliveMsg.Builder {
+        BGPHeader bgpMsgHeader;
+
+        @Override
+        public BGPVersion getVersion() {
+            return BGPVersion.BGP_4;
+        }
+
+        @Override
+        public BGPType getType() {
+            return BGPType.KEEP_ALIVE;
+        }
+
+        @Override
+        public BGPHeader getHeader() {
+            return this.bgpMsgHeader;
+        }
+
+        @Override
+        public Builder setHeader(BGPHeader bgpMsgHeader) {
+            this.bgpMsgHeader = bgpMsgHeader;
+            return this;
+        }
+
+        @Override
+        public BGPKeepaliveMsg build() {
+            return new BGPKeepaliveMsgVer4();
+        }
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer cb) {
+        WRITER.write(cb, this);
+    }
+
+    static final Writer WRITER = new Writer();
+
+    /**
+     * Writer class for writing the BGP keepalive message to channel buffer.
+     */
+    static class Writer implements BGPMessageWriter<BGPKeepaliveMsgVer4> {
+
+        @Override
+        public void write(ChannelBuffer cb, BGPKeepaliveMsgVer4 message) {
+
+            // write marker
+            cb.writeBytes(marker, 0, MARKER_LENGTH);
+
+            // write length of header
+            cb.writeShort(PACKET_MINIMUM_LENGTH);
+
+            // write the type of message
+            cb.writeByte(MSG_TYPE.getType());
+        }
+    }
+
+    @Override
+    public BGPVersion getVersion() {
+        return BGPVersion.BGP_4;
+    }
+
+    @Override
+    public BGPType getType() {
+        return MSG_TYPE;
+    }
+
+    @Override
+    public BGPHeader getHeader() {
+        return this.bgpMsgHeader;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).toString();
+    }
+}
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java
new file mode 100644 (file)
index 0000000..1348ecf
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol.ver4;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.protocol.BGPMessageReader;
+import org.onosproject.bgpio.protocol.BGPMessageWriter;
+import org.onosproject.bgpio.protocol.BGPOpenMsg;
+import org.onosproject.bgpio.protocol.BGPType;
+import org.onosproject.bgpio.protocol.BGPVersion;
+import org.onosproject.bgpio.types.BGPErrorType;
+import org.onosproject.bgpio.types.BGPHeader;
+import org.onosproject.bgpio.types.BGPValueType;
+import org.onosproject.bgpio.util.Validation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides BGP open message.
+ */
+public class BGPOpenMsgVer4 implements BGPOpenMsg {
+
+    /*
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+
+       |    Version    |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |    My Autonomous System       |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |         Hold Time             |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                  BGP Identifier                             |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       | Opt Parm Len  |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |             Optional Parameters (variable)                  |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       OPEN Message Format
+       REFERENCE : RFC 4271
+    */
+
+    protected static final Logger log = LoggerFactory.getLogger(BGPOpenMsgVer4.class);
+
+    public static final byte PACKET_VERSION = 4;
+    public static final int OPEN_MSG_MINIMUM_LENGTH = 10;
+    public static final int MSG_HEADER_LENGTH = 19;
+    public static final int MARKER_LENGTH  = 16;
+    public static final int DEFAULT_HOLD_TIME = 120;
+    public static final int OPT_PARA_TYPE_CAPABILITY = 2;
+    public static final BGPType MSG_TYPE = BGPType.OPEN;
+    public static final byte[] MARKER = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+    public static final BGPHeader DEFAULT_OPEN_HEADER = new BGPHeader(MARKER,
+        (short) OPEN_MSG_MINIMUM_LENGTH, (byte) 0X01);
+    private BGPHeader bgpMsgHeader;
+    private byte version;
+    private short asNumber;
+    private short holdTime;
+    private int bgpId;
+    private LinkedList<BGPValueType> capabilityTlv;
+
+    public static final BGPOpenMsgVer4.Reader READER = new Reader();
+
+    /**
+     * reset variables.
+     */
+    public BGPOpenMsgVer4() {
+        this.bgpMsgHeader = null;
+        this.version = 0;
+        this.holdTime = 0;
+        this.asNumber = 0;
+        this.bgpId = 0;
+        this.capabilityTlv = null;
+    }
+
+    /**
+     * Constructor to initialize all variables of BGP Open message.
+     *
+     * @param bgpMsgHeader
+     *           BGP Header in open message
+     * @param version
+     *           BGP version in open message
+     * @param holdTime
+     *           hold time in open message
+     * @param asNumber
+     *           AS number in open message
+     * @param bgpId
+     *           BGP identifier in open message
+     * @param capabilityTlv
+     *           capabilities in open message
+     */
+    public BGPOpenMsgVer4(BGPHeader bgpMsgHeader, byte version, short asNumber, short holdTime,
+             int bgpId, LinkedList<BGPValueType> capabilityTlv) {
+        this.bgpMsgHeader = bgpMsgHeader;
+        this.version = version;
+        this.asNumber = asNumber;
+        this.holdTime = holdTime;
+        this.bgpId = bgpId;
+        this.capabilityTlv = capabilityTlv;
+    }
+
+    @Override
+    public BGPHeader getHeader() {
+        return this.bgpMsgHeader;
+    }
+
+    @Override
+    public BGPVersion getVersion() {
+        return BGPVersion.BGP_4;
+    }
+
+    @Override
+    public BGPType getType() {
+        return MSG_TYPE;
+    }
+
+    @Override
+    public short getHoldTime() {
+        return this.holdTime;
+    }
+
+    @Override
+    public short getAsNumber() {
+        return this.asNumber;
+    }
+
+    @Override
+    public int getBgpId() {
+        return this.bgpId;
+    }
+
+    @Override
+    public LinkedList<BGPValueType> getCapabilityTlv() {
+        return this.capabilityTlv;
+    }
+
+    /**
+     * Reader class for reading BGP open message from channel buffer.
+     */
+    public static class Reader implements BGPMessageReader<BGPOpenMsg> {
+
+        @Override
+        public BGPOpenMsg readFrom(ChannelBuffer cb, BGPHeader bgpHeader) throws BGPParseException {
+
+            byte version;
+            short holdTime;
+            short asNumber;
+            int bgpId;
+            byte optParaLen = 0;
+            byte optParaType;
+            byte capParaLen = 0;
+            LinkedList<BGPValueType> capabilityTlv = new LinkedList<>();
+
+            if (cb.readableBytes() < OPEN_MSG_MINIMUM_LENGTH) {
+                log.error("[readFrom] Invalid length: Packet size is less than the minimum length ");
+                Validation.validateLen(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_MESSAGE_LENGTH,
+                        cb.readableBytes());
+            }
+
+            // Read version
+            version = cb.readByte();
+            if (version != PACKET_VERSION) {
+                log.error("[readFrom] Invalid version: " + version);
+                throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR,
+                        BGPErrorType.UNSUPPORTED_VERSION_NUMBER, null);
+            }
+
+            // Read AS number
+            asNumber = cb.readShort();
+
+            // Read Hold timer
+            holdTime = cb.readShort();
+
+            // Read BGP Identifier
+            bgpId = cb.readInt();
+
+            // Read optional parameter length
+            optParaLen = cb.readByte();
+
+            // Read Capabilities if optional parameter length is greater than 0
+            if (optParaLen != 0) {
+                // Read optional parameter type
+                optParaType = cb.readByte();
+
+                // Read optional parameter length
+                capParaLen = cb.readByte();
+
+                if (cb.readableBytes() < capParaLen) {
+                    throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, (byte) 0, null);
+                }
+
+                ChannelBuffer capaCb = cb.readBytes(capParaLen);
+
+                // Parse capabilities only if optional parameter type is 2
+                if ((optParaType == OPT_PARA_TYPE_CAPABILITY) && (capParaLen != 0)) {
+                    capabilityTlv = parseCapabilityTlv(capaCb);
+                } else {
+                    throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR,
+                        BGPErrorType.UNSUPPORTED_OPTIONAL_PARAMETER, null);
+                }
+            }
+            return new BGPOpenMsgVer4(bgpHeader, version, asNumber, holdTime, bgpId, capabilityTlv);
+        }
+    }
+
+    /**
+     * Parsing capabilities.
+     *
+     * @param cb of type channel buffer
+     * @return capabilityTlv of open message
+     * @throws BGPParseException while parsing capabilities
+     */
+    protected static LinkedList<BGPValueType> parseCapabilityTlv(ChannelBuffer cb) throws BGPParseException {
+
+        LinkedList<BGPValueType> capabilityTlv = new LinkedList<>();
+
+        // TODO: Capability parsing
+        return capabilityTlv;
+    }
+
+    /**
+     * Builder class for BGP open message.
+     */
+    static class Builder implements BGPOpenMsg.Builder {
+
+        private boolean isHeaderSet = false;
+        private BGPHeader bgpMsgHeader;
+        private boolean isHoldTimeSet = false;
+        private short  holdTime;
+        private boolean isAsNumSet = false;
+        private short asNumber;
+        private boolean isBgpIdSet = false;
+        private int bgpId;
+
+        LinkedList<BGPValueType> capabilityTlv = new LinkedList<>();
+
+        @Override
+        public BGPOpenMsg build() throws BGPParseException {
+            BGPHeader bgpMsgHeader = this.isHeaderSet ? this.bgpMsgHeader : DEFAULT_OPEN_HEADER;
+            short holdTime = this.isHoldTimeSet ? this.holdTime : DEFAULT_HOLD_TIME;
+
+            if (!this.isAsNumSet) {
+                throw new BGPParseException("BGP AS number is not set (mandatory)");
+            }
+
+            if (!this.isBgpIdSet) {
+                throw new BGPParseException("BGPID  is not set (mandatory)");
+            }
+
+            // TODO: capabilities build
+
+            return new BGPOpenMsgVer4(bgpMsgHeader, PACKET_VERSION, this.asNumber, holdTime, this.bgpId,
+                       this.capabilityTlv);
+        }
+
+        @Override
+        public BGPHeader getHeader() {
+            return this.bgpMsgHeader;
+        }
+
+        @Override
+        public Builder setHeader(BGPHeader bgpMsgHeader) {
+            this.bgpMsgHeader = bgpMsgHeader;
+            return this;
+        }
+
+        @Override
+        public BGPVersion getVersion() {
+            return BGPVersion.BGP_4;
+        }
+
+        @Override
+        public BGPType getType() {
+            return MSG_TYPE;
+        }
+
+        @Override
+        public short getHoldTime() {
+            return this.holdTime;
+        }
+
+        @Override
+        public short getAsNumber() {
+            return this.asNumber;
+        }
+
+        @Override
+        public int getBgpId() {
+            return this.bgpId;
+        }
+
+        @Override
+        public LinkedList<BGPValueType> getCapabilityTlv() {
+            return this.capabilityTlv;
+        }
+
+        @Override
+        public Builder setHoldTime(short holdTime) {
+            this.holdTime = holdTime;
+            this.isHoldTimeSet = true;
+            return this;
+        }
+
+        @Override
+        public Builder setAsNumber(short asNumber) {
+            this.asNumber = asNumber;
+            this.isAsNumSet = true;
+            return this;
+        }
+
+        @Override
+        public Builder setBgpId(int bgpId) {
+            this.bgpId = bgpId;
+            this.isBgpIdSet = true;
+            return this;
+        }
+
+        @Override
+        public Builder setCapabilityTlv(LinkedList<BGPValueType> capabilityTlv) {
+            this.capabilityTlv = capabilityTlv;
+            return this;
+        }
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer cb) {
+        try {
+            WRITER.write(cb, this);
+        } catch (BGPParseException e) {
+            log.debug("[writeTo] Error: " + e.toString());
+        }
+    }
+
+    public static final Writer WRITER = new Writer();
+
+    /**
+     * Writer class for writing BGP open message to channel buffer.
+     */
+    public static class Writer implements BGPMessageWriter<BGPOpenMsgVer4> {
+
+        @Override
+        public void write(ChannelBuffer cb, BGPOpenMsgVer4 message) throws BGPParseException {
+
+            int optParaLen = 0;
+
+            int startIndex = cb.writerIndex();
+
+            // write common header and get msg length index
+            int msgLenIndex = message.bgpMsgHeader.write(cb);
+
+            if (msgLenIndex <= 0) {
+                throw new BGPParseException("Unable to write message header.");
+            }
+
+            // write version in 1-octet
+            cb.writeByte(message.version);
+
+            // TODO : Write AS number based on capabilities
+            cb.writeShort(message.asNumber);
+
+            // write HoldTime in next 2-octet
+            cb.writeShort(message.holdTime);
+
+            // write BGP Identifier in next 4-octet
+            cb.writeInt(message.bgpId);
+
+            // store the index of Optional parameter length
+            int optParaLenIndex = cb.writerIndex();
+
+            // set optional parameter length as 0
+            cb.writeByte(0);
+
+            // Pack capability TLV
+            optParaLen = message.packCapabilityTlv(cb, message);
+
+            if (optParaLen != 0) {
+                // Update optional parameter length
+                cb.setByte(optParaLenIndex, (byte) (optParaLen + 2)); //+2 for optional parameter type.
+            }
+
+            // write OPEN Object Length
+            int length = cb.writerIndex() - startIndex;
+            cb.setShort(msgLenIndex, (short) length);
+            message.bgpMsgHeader.setLength((short) length);
+        }
+    }
+
+    /**
+     * returns length of capability tlvs.
+     *
+     * @param cb of type channel buffer
+     * @param message of type BGPOpenMsgVer4
+     * @return capParaLen of open message
+     */
+    protected int packCapabilityTlv(ChannelBuffer cb, BGPOpenMsgVer4 message) {
+        int startIndex = cb.writerIndex();
+        int capParaLen = 0;
+        int capParaLenIndex = 0;
+
+        LinkedList<BGPValueType> capabilityTlv = message.capabilityTlv;
+        ListIterator<BGPValueType> listIterator = capabilityTlv.listIterator();
+
+        if (listIterator.hasNext()) {
+            // Set optional parameter type as 2
+            cb.writeByte(OPT_PARA_TYPE_CAPABILITY);
+
+            // Store the index of capability parameter length and update length at the end
+            capParaLenIndex = cb.writerIndex();
+
+            // Set capability parameter length as 0
+            cb.writeByte(0);
+
+            // Update the startIndex to know the length of capability tlv
+            startIndex = cb.writerIndex();
+        }
+
+        while (listIterator.hasNext()) {
+            BGPValueType tlv = listIterator.next();
+            if (tlv == null) {
+                log.debug("Warning: tlv is null from CapabilityTlv list");
+                continue;
+            }
+            tlv.write(cb);
+        }
+
+        capParaLen = cb.writerIndex() - startIndex;
+
+        if (capParaLen != 0) {
+            // Update capability parameter length
+            cb.setByte(capParaLenIndex, (byte) capParaLen);
+        }
+        return capParaLen;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+            .add("bgpMsgHeader", bgpMsgHeader)
+            .add("version", version)
+            .add("holdTime", holdTime)
+            .add("asNumber", asNumber)
+            .add("bgpId", bgpId)
+            .add("capabilityTlv", capabilityTlv)
+            .toString();
+    }
+}
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/package-info.java
new file mode 100755 (executable)
index 0000000..fb8c67c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * BGP Protocol specific details of version 4.
+ */
+package org.onosproject.bgpio.protocol.ver4;
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AreaIDTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AreaIDTlv.java
new file mode 100644 (file)
index 0000000..52bae46
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides AreaID Tlv which contains opaque value (32 Bit Area-ID).
+ */
+public class AreaIDTlv implements BGPValueType {
+
+    /* Reference :draft-ietf-idr-ls-distribution-11
+     *  0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |           Type= 514            |             Length=4         |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                    opaque value (32 Bit Area-ID)              |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     */
+
+    protected static final Logger log = LoggerFactory.getLogger(AreaIDTlv.class);
+
+    public static final short TYPE = 514;
+    public static final short LENGTH = 4;
+
+    private final int areaID;
+
+    /**
+     * Constructor to initialize areaID.
+     *
+     * @param areaID of BGP AreaID Tlv
+     */
+    public AreaIDTlv(int areaID) {
+        this.areaID = areaID;
+    }
+
+    /**
+     * Returns object of this class with specified areaID.
+     *
+     * @param areaID opaque value of area id
+     * @return object of AreaIDTlv
+     */
+    public static AreaIDTlv of(final int areaID) {
+        return new AreaIDTlv(areaID);
+    }
+
+    /**
+     * Returns opaque value of area id.
+     *
+     * @return opaque value of area id
+     */
+    public int getAreaID() {
+        return areaID;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(areaID);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof AreaIDTlv) {
+            AreaIDTlv other = (AreaIDTlv) obj;
+            return Objects.equals(areaID, other.areaID);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeInt(areaID);
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads the channel buffer and returns object of AreaIDTlv.
+     *
+     * @param cb ChannelBuffer
+     * @return object of AreaIDTlv
+     */
+    public static AreaIDTlv read(ChannelBuffer cb) {
+        return AreaIDTlv.of(cb.readInt());
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("Value", areaID)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AutonomousSystemTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AutonomousSystemTlv.java
new file mode 100644 (file)
index 0000000..5d8a919
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides Autonomous System Tlv which contains opaque value (32 Bit AS Number).
+ */
+public class AutonomousSystemTlv implements BGPValueType {
+
+    /* Reference :draft-ietf-idr-ls-distribution-11
+     *  0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |           Type= 512            |             Length=4         |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                    opaque value (32 Bit AS Number)            |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     */
+
+    protected static final Logger log = LoggerFactory.getLogger(AutonomousSystemTlv.class);
+
+    public static final short TYPE = 512;
+    public static final short LENGTH = 4;
+
+    private final int asNum;
+
+    /**
+     * Constructor to initialize asNum.
+     *
+     * @param asNum 32 Bit AS Number
+     */
+    public AutonomousSystemTlv(int asNum) {
+        this.asNum = asNum;
+    }
+
+    /**
+     * Returns object of this class with specified asNum.
+     *
+     * @param asNum 32 Bit AS Number
+     * @return object of AutonomousSystemTlv
+     */
+    public static AutonomousSystemTlv of(final int asNum) {
+        return new AutonomousSystemTlv(asNum);
+    }
+
+    /**
+     * Returns opaque value of AS Number.
+     *
+     * @return opaque value of AS Number
+     */
+    public int getAsNum() {
+        return asNum;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(asNum);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof AutonomousSystemTlv) {
+            AutonomousSystemTlv other = (AutonomousSystemTlv) obj;
+            return Objects.equals(asNum, other.asNum);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeInt(asNum);
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads the channel buffer and returns object of AutonomousSystemTlv.
+     *
+     * @param c ChannelBuffer
+     * @return object of AutonomousSystemTlv
+     */
+    public static AutonomousSystemTlv read(ChannelBuffer c) {
+        return AutonomousSystemTlv.of(c.readInt());
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("asNum", asNum)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java
new file mode 100644 (file)
index 0000000..f643ae0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.types;
+
+/**
+ * BgpErrorType class defines all errorCodes and error Subcodes required for Notification message.
+ */
+public final class BGPErrorType {
+    private BGPErrorType() {
+    }
+
+    //Error Codes
+    public static final byte MESSAGE_HEADER_ERROR = 1;
+    public static final byte OPEN_MESSAGE_ERROR = 2;
+    public static final byte UPDATE_MESSAGE_ERROR = 3;
+    public static final byte HOLD_TIMER_EXPIRED = 4;
+    public static final byte FINITE_STATE_MACHINE_ERROR = 4;
+    public static final byte CEASE = 5;
+
+    //Message Header Error subcodes
+    public static final byte CONNECTION_NOT_SYNCHRONIZED = 1;
+    public static final byte BAD_MESSAGE_LENGTH = 2;
+    public static final byte BAD_MESSAGE_TYPE = 3;
+
+    //OPEN Message Error subcodes
+    public static final byte UNSUPPORTED_VERSION_NUMBER = 1;
+    public static final byte BAD_PEER_AS = 2;
+    public static final byte BAD_BGP_IDENTIFIER = 3;
+    public static final byte UNSUPPORTED_OPTIONAL_PARAMETER = 4;
+    public static final byte UNACCEPTABLE_HOLD_TIME = 5;
+
+    //UPDATE Message Error subcodes
+    public static final byte MALFORMED_ATTRIBUTE_LIST = 1;
+    public static final byte UNRECOGNIZED_WELLKNOWN_ATTRIBUTE = 2;
+    public static final byte MISSING_WELLKNOWN_ATTRIBUTE = 3;
+    public static final byte ATTRIBUTE_FLAGS_ERROR = 4;
+    public static final byte ATTRIBUTE_LENGTH_ERROR = 5;
+    public static final byte INVALID_ORIGIN_ATTRIBUTE = 6;
+    public static final byte INVALID_NEXTHOP_ATTRIBUTE = 8;
+    public static final byte OPTIONAL_ATTRIBUTE_ERROR = 9;
+    public static final byte INVALID_NETWORK_FIELD = 10;
+    public static final byte MALFORMED_ASPATH = 11;
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPHeader.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPHeader.java
new file mode 100755 (executable)
index 0000000..6acda0d
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides BGP Message Header which is common for all the Messages.
+ */
+
+public class BGPHeader {
+
+    /*      0                   1                   2                   3
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+          |                                                               |
+          +                                                               +
+          |                                                               |
+          +                                                               +
+          |                           Marker                              |
+          +                                                               +
+          |                                                               |
+          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+          |          Length               |      Type     |
+          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    */
+
+    protected static final Logger log = LoggerFactory.getLogger(BGPHeader.class);
+
+    public static final int MARKER_LENGTH = 16;
+    public static final short DEFAULT_HEADER_LENGTH = 19;
+
+    private byte[] marker;
+    private byte type;
+    private short length;
+
+    /**
+     * Reset fields.
+     */
+    public BGPHeader() {
+        this.marker = null;
+        this.length = 0;
+        this.type = 0;
+    }
+
+    /**
+     * Constructors to initialize parameters.
+     *
+     * @param marker field in BGP header
+     * @param length message length
+     * @param type message type
+     */
+    public BGPHeader(byte[] marker, short length, byte type) {
+        this.marker = marker;
+        this.length = length;
+        this.type = type;
+    }
+
+    /**
+     * Sets marker field.
+     *
+     * @param value marker field
+     */
+    public void setMarker(byte[] value) {
+        this.marker = value;
+    }
+
+    /**
+     * Sets message type.
+     *
+     * @param value message type
+     */
+    public void setType(byte value) {
+        this.type = value;
+    }
+
+    /**
+     * Sets message length.
+     *
+     * @param value message length
+     */
+    public void setLength(short value) {
+        this.length = value;
+    }
+
+    /**
+     * Returns message length.
+     *
+     * @return message length
+     */
+    public short getLength() {
+        return this.length;
+    }
+
+    /**
+     * Returns message marker.
+     *
+     * @return message marker
+     */
+    public byte[] getMarker() {
+        return this.marker;
+    }
+
+    /**
+     * Returns message type.
+     *
+     * @return message type
+     */
+    public byte getType() {
+        return this.type;
+    }
+
+    /**
+     * Writes Byte stream of BGP header to channel buffer.
+     *
+     * @param cb ChannelBuffer
+     * @return length index of message header
+     */
+    public int write(ChannelBuffer cb) {
+
+        cb.writeBytes(getMarker(), 0, MARKER_LENGTH);
+
+        int headerLenIndex = cb.writerIndex();
+        cb.writeShort((short) 0);
+        cb.writeByte(type);
+
+        return headerLenIndex;
+    }
+
+    /**
+     * Read from channel buffer and Returns BGP header.
+     *
+     * @param cb ChannelBuffer
+     * @return object of BGPHeader
+     */
+    public static BGPHeader read(ChannelBuffer cb) {
+
+        byte[] marker = new byte[MARKER_LENGTH];
+        byte type;
+        short length;
+        cb.readBytes(marker, 0, MARKER_LENGTH);
+        length = cb.readShort();
+        type = cb.readByte();
+        return new BGPHeader(marker, length, type);
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPLSIdentifierTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPLSIdentifierTlv.java
new file mode 100644 (file)
index 0000000..f723d2c
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides BGPLSIdentifier Tlv which contains opaque value (32 Bit BGPLS-Identifier).
+ */
+public class BGPLSIdentifierTlv implements BGPValueType {
+
+    /* Reference :draft-ietf-idr-ls-distribution-11
+     *  0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |           Type= 513            |             Length=4         |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                    opaque value (32 Bit BGPLS-Identifier)     |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     */
+
+    protected static final Logger log = LoggerFactory.getLogger(BGPLSIdentifierTlv.class);
+
+    public static final short TYPE = 513;
+    public static final short LENGTH = 4;
+
+    private final int bgpLSIdentifier;
+
+    /**
+     * Constructor to initialize bgpLSIdentifier.
+     *
+     * @param bgpLSIdentifier BgpLS-Identifier
+     */
+    public BGPLSIdentifierTlv(int bgpLSIdentifier) {
+        this.bgpLSIdentifier = bgpLSIdentifier;
+    }
+
+    /**
+     * Returns object of this class with specified rbgpLSIdentifier.
+     *
+     * @param bgpLSIdentifier BgpLS-Identifier
+     * @return BgpLS-Identifier
+     */
+    public static BGPLSIdentifierTlv of(final int bgpLSIdentifier) {
+        return new BGPLSIdentifierTlv(bgpLSIdentifier);
+    }
+
+    /**
+     * Returns opaque value of BgpLS-Identifier.
+     *
+     * @return opaque value of BgpLS-Identifier
+     */
+    public int getBgpLSIdentifier() {
+        return bgpLSIdentifier;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(bgpLSIdentifier);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof BGPLSIdentifierTlv) {
+            BGPLSIdentifierTlv other = (BGPLSIdentifierTlv) obj;
+            return Objects.equals(bgpLSIdentifier, other.bgpLSIdentifier);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeInt(bgpLSIdentifier);
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+     /**
+     * Reads the channel buffer and parses BGPLS Identifier TLV.
+     *
+     * @param cb ChannelBuffer
+     * @return object of BGPLSIdentifierTlv
+     */
+    public static BGPLSIdentifierTlv read(ChannelBuffer cb) {
+        return BGPLSIdentifierTlv.of(cb.readInt());
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("Value", bgpLSIdentifier)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPValueType.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPValueType.java
new file mode 100755 (executable)
index 0000000..54a8b43
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.types;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ * Abstraction which Provides the BGP of TLV format.
+ */
+public interface BGPValueType {
+    /**
+     * Returns the Type of BGP Message.
+     *
+     * @return short value of type
+     */
+    short getType();
+
+    /**
+     * Writes the byte Stream of BGP Message to channel buffer.
+     *
+     * @param cb channel buffer
+     * @return length written to channel buffer
+     */
+    int write(ChannelBuffer cb);
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IPReachabilityInformationTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IPReachabilityInformationTlv.java
new file mode 100644 (file)
index 0000000..85fb748
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.bgpio.util.Validation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides IP Reachability InformationTlv Tlv which contains IP Prefix.
+ */
+public class IPReachabilityInformationTlv implements BGPValueType {
+
+    /*
+     * Reference :draft-ietf-idr-ls-distribution-11
+
+      0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |              Type             |             Length            |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     | Prefix Length | IP Prefix (variable)                         //
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+             Figure 14: IP Reachability Information TLV Format
+    */
+
+    protected static final Logger log = LoggerFactory.getLogger(IPReachabilityInformationTlv.class);
+
+    public static final short TYPE = 265;
+    public static final int ONE_BYTE_LEN = 8;
+    private byte prefixLen;
+    private byte[] ipPrefix;
+    public short length;
+
+    /**
+     * Constructor to initialize parameters.
+     *
+     * @param prefixLen length of IP Prefix
+     * @param ipPrefix IP Prefix
+     * @param length length of value field
+     */
+    public IPReachabilityInformationTlv(byte prefixLen, byte[] ipPrefix, short length) {
+        this.ipPrefix = ipPrefix;
+        this.prefixLen = prefixLen;
+        this.length = length;
+    }
+
+    /**
+     * Returns IP Prefix.
+     *
+     * @return IP Prefix
+     */
+    public IpPrefix getPrefixValue() {
+        IpPrefix prefix = Validation.bytesToPrefix(ipPrefix, prefixLen);
+        return prefix;
+    }
+
+    /**
+     * Returns IP Prefix length.
+     *
+     * @return IP Prefix length
+     */
+    public byte getPrefixLen() {
+        return this.prefixLen;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ipPrefix, prefixLen);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof IPReachabilityInformationTlv) {
+            IPReachabilityInformationTlv other = (IPReachabilityInformationTlv) obj;
+            return Objects.equals(prefixLen, other.prefixLen) && Objects.equals(ipPrefix, other.ipPrefix);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer cb) {
+        int iLenStartIndex = cb.writerIndex();
+        cb.writeShort(TYPE);
+        cb.writeShort(length);
+        cb.writeByte(prefixLen);
+        cb.writeBytes(ipPrefix);
+        return cb.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads the channel buffer and returns object of IPReachabilityInformationTlv.
+     *
+     * @param cb ChannelBuffer
+     * @param length of value field
+     * @return object of IPReachabilityInformationTlv
+     */
+    public static IPReachabilityInformationTlv read(ChannelBuffer cb, short length) {
+        byte preficLen = cb.readByte();
+        byte[] prefix;
+        if (preficLen == 0) {
+            prefix = new byte[] {0};
+        } else {
+            int len = preficLen / ONE_BYTE_LEN;
+            int reminder = preficLen % ONE_BYTE_LEN;
+            if (reminder > 0) {
+                len = len + 1;
+            }
+            prefix = new byte[len];
+            cb.readBytes(prefix, 0, len);
+        }
+        return IPReachabilityInformationTlv.of(preficLen, prefix, length);
+    }
+
+    public static IPReachabilityInformationTlv of(final byte preficLen, final byte[] prefix, final short  length) {
+        return new IPReachabilityInformationTlv(preficLen, prefix, length);
+    }
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", length)
+                .add("Prefixlength", getPrefixLen())
+                .add("Prefixvalue", getPrefixValue())
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsNonPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsNonPseudonode.java
new file mode 100644 (file)
index 0000000..d5f3e7f
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.protocol.IGPRouterID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides Implementation of IsIsNonPseudonode Tlv.
+ */
+public class IsIsNonPseudonode implements IGPRouterID, BGPValueType {
+    protected static final Logger log = LoggerFactory.getLogger(IsIsNonPseudonode.class);
+
+    public static final short TYPE = 515;
+    public static final short LENGTH = 6;
+
+    private final byte[] isoNodeID;
+
+    /**
+     * Constructor to initialize isoNodeID.
+     *
+     * @param isoNodeID ISO system-ID
+     */
+    public IsIsNonPseudonode(byte[] isoNodeID) {
+        this.isoNodeID = isoNodeID;
+    }
+
+    /**
+     * Returns object of this class with specified isoNodeID.
+     *
+     * @param isoNodeID ISO system-ID
+     * @return object of IsIsNonPseudonode
+     */
+    public static IsIsNonPseudonode of(final byte[] isoNodeID) {
+        return new IsIsNonPseudonode(isoNodeID);
+    }
+
+    /**
+     * Returns ISO NodeID.
+     *
+     * @return ISO NodeID
+     */
+    public byte[] getISONodeID() {
+        return isoNodeID;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(isoNodeID);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof IsIsNonPseudonode) {
+            IsIsNonPseudonode other = (IsIsNonPseudonode) obj;
+            return Objects.equals(isoNodeID, other.isoNodeID);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeBytes(isoNodeID);
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads the channel buffer and returns object of IsIsNonPseudonode.
+     *
+     * @param cb ChannelBuffer
+     * @return object of IsIsNonPseudonode
+     */
+    public static IsIsNonPseudonode read(ChannelBuffer cb) {
+        byte[] isoNodeID = new byte[LENGTH];
+        cb.readBytes(isoNodeID, 0, LENGTH);
+        return IsIsNonPseudonode.of(isoNodeID);
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("ISONodeID", isoNodeID)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsPseudonode.java
new file mode 100644 (file)
index 0000000..5c742d0
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.protocol.IGPRouterID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides implementation of IsIsPseudonode Tlv.
+ */
+public class IsIsPseudonode implements IGPRouterID, BGPValueType {
+    protected static final Logger log = LoggerFactory.getLogger(IsIsPseudonode.class);
+
+    public static final short TYPE = 515;
+    public static final short LENGTH = 7;
+
+    private final byte[] isoNodeID;
+    private byte psnIdentifier;
+
+    /**
+     * Constructor to initialize isoNodeID.
+     *
+     * @param isoNodeID ISO system-ID
+     * @param psnIdentifier PSN identifier
+     */
+    public IsIsPseudonode(byte[] isoNodeID, byte psnIdentifier) {
+        this.isoNodeID = isoNodeID;
+        this.psnIdentifier = psnIdentifier;
+    }
+
+    /**
+     * Returns object of this class with specified values.
+     *
+     * @param isoNodeID ISO system-ID
+     * @param psnIdentifier PSN identifier
+     * @return object of IsIsPseudonode
+     */
+    public static IsIsPseudonode of(final byte[] isoNodeID, final byte psnIdentifier) {
+        return new IsIsPseudonode(isoNodeID, psnIdentifier);
+    }
+
+    /**
+     * Returns ISO NodeID.
+     *
+     * @return ISO NodeID
+     */
+    public byte[] getISONodeID() {
+        return isoNodeID;
+    }
+
+    /**
+     * Returns PSN Identifier.
+     *
+     * @return PSN Identifier
+     */
+    public byte getPSNIdentifier() {
+        return this.psnIdentifier;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(isoNodeID, psnIdentifier);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof IsIsPseudonode) {
+            IsIsPseudonode other = (IsIsPseudonode) obj;
+            return Objects.equals(isoNodeID, other.isoNodeID) && Objects.equals(psnIdentifier, other.psnIdentifier);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeBytes(isoNodeID);
+        c.writeByte(psnIdentifier);
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads the channel buffer and returns object of IsIsPseudonode.
+     *
+     * @param cb ChannelBuffer
+     * @return object of IsIsPseudonode
+     */
+    public static IsIsPseudonode read(ChannelBuffer cb) {
+        byte[] isoNodeID = new byte[LENGTH - 1];
+        cb.readBytes(isoNodeID, 0, LENGTH - 1);
+        byte psnIdentifier = cb.readByte();
+        return IsIsPseudonode.of(isoNodeID, psnIdentifier);
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("isoNodeID", isoNodeID)
+                .add("psnIdentifier", psnIdentifier)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFNonPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFNonPseudonode.java
new file mode 100644 (file)
index 0000000..6d8282b
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.protocol.IGPRouterID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides implementation of OSPFNonPseudonode Tlv.
+ */
+public class OSPFNonPseudonode implements IGPRouterID, BGPValueType {
+
+    protected static final Logger log = LoggerFactory.getLogger(OSPFNonPseudonode.class);
+
+    public static final short TYPE = 515;
+    public static final short LENGTH = 4;
+
+    private final int routerID;
+
+    /**
+     * Constructor to initialize routerID.
+     *
+     * @param routerID routerID
+     */
+    public OSPFNonPseudonode(int routerID) {
+        this.routerID = routerID;
+    }
+
+    /**
+     * Returns object of this class with specified routerID.
+     *
+     * @param routerID routerID
+     * @return object of OSPFNonPseudonode
+     */
+    public static OSPFNonPseudonode of(final int routerID) {
+        return new OSPFNonPseudonode(routerID);
+    }
+
+    /**
+     * Returns RouterID.
+     *
+     * @return RouterID
+     */
+    public int getrouterID() {
+        return this.routerID;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(routerID);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof OSPFNonPseudonode) {
+            OSPFNonPseudonode other = (OSPFNonPseudonode) obj;
+            return Objects.equals(routerID, other.routerID);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeInt(routerID);
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads the channel buffer and returns object of OSPFNonPseudonode.
+     *
+     * @param cb ChannelBuffer
+     * @return object of OSPFNonPseudonode
+     */
+    public static OSPFNonPseudonode read(ChannelBuffer cb) {
+        return OSPFNonPseudonode.of(cb.readInt());
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("RouterID", routerID)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFPseudonode.java
new file mode 100644 (file)
index 0000000..82a39bd
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onlab.packet.Ip4Address;
+import org.onosproject.bgpio.protocol.IGPRouterID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides implementation of OSPFPseudonode Tlv.
+ */
+public class OSPFPseudonode implements IGPRouterID, BGPValueType {
+
+    protected static final Logger log = LoggerFactory.getLogger(OSPFPseudonode.class);
+
+    public static final short TYPE = 515;
+    public static final short LENGTH = 8;
+
+    private final int routerID;
+    private final Ip4Address drInterface;
+
+    /**
+     * Constructor to initialize parameters.
+     *
+     * @param routerID routerID
+     * @param drInterface IPv4 address of the DR's interface
+     */
+    public OSPFPseudonode(int routerID, Ip4Address drInterface) {
+        this.routerID = routerID;
+        this.drInterface = drInterface;
+    }
+
+    /**
+     * Returns object of this class with specified values.
+     *
+     * @param routerID routerID
+     * @param drInterface IPv4 address of the DR's interface
+     * @return object of OSPFPseudonode
+     */
+    public static OSPFPseudonode of(final int routerID, final Ip4Address drInterface) {
+        return new OSPFPseudonode(routerID, drInterface);
+    }
+
+    /**
+     * Returns RouterID.
+     *
+     * @return RouterID
+     */
+    public int getrouterID() {
+        return this.routerID;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(routerID, drInterface);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof OSPFPseudonode) {
+            OSPFPseudonode other = (OSPFPseudonode) obj;
+            return Objects.equals(routerID, other.routerID) && Objects.equals(drInterface, other.drInterface);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeInt(routerID);
+        c.writeInt(drInterface.toInt());
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads the channel buffer and returns object of OSPFPseudonode.
+     *
+     * @param cb ChannelBuffer
+     * @return object of OSPFPseudonode
+     */
+    public static OSPFPseudonode read(ChannelBuffer cb) {
+        int routerID = cb.readInt();
+        Ip4Address drInterface = Ip4Address.valueOf(cb.readInt());
+        return OSPFPseudonode.of(routerID, drInterface);
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("RouterID", routerID)
+                .add("DRInterface", drInterface)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFRouteTypeTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFRouteTypeTlv.java
new file mode 100644 (file)
index 0000000..20fffbf
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Provides OSPF Route Type Tlv which contains route type.
+ */
+public class OSPFRouteTypeTlv implements BGPValueType {
+
+    /* Reference :draft-ietf-idr-ls-distribution-11
+      0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |              Type             |             Length            |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |  Route Type   |
+     +-+-+-+-+-+-+-+-+
+
+                   Figure : OSPF Route Type TLV Format
+    */
+
+    protected static final Logger log = LoggerFactory.getLogger(OSPFRouteTypeTlv.class);
+
+    public static final short TYPE = 264;
+    public static final short LENGTH = 1;
+    public static final int INTRA_AREA_TYPE = 1;
+    public static final short INTER_AREA_TYPE = 2;
+    public static final short EXTERNAL_TYPE_1 = 3;
+    public static final short EXTERNAL_TYPE_2 = 4;
+    public static final short NSSA_TYPE_1 = 5;
+    public static final short NSSA_TYPE_2 = 6;
+    private final byte routeType;
+
+    /**
+     * Enum for Route Type.
+     */
+    public enum ROUTETYPE {
+        Intra_Area(1), Inter_Area(2), External_1(3), External_2(4), NSSA_1(5), NSSA_2(6);
+        int value;
+        ROUTETYPE(int val) {
+            value = val;
+        }
+        public byte getType() {
+            return (byte) value;
+        }
+    }
+
+    /**
+     * Constructor to initialize routeType.
+     *
+     * @param routeType Route type
+     */
+    public OSPFRouteTypeTlv(byte routeType) {
+        this.routeType = routeType;
+    }
+
+    /**
+     * Returns object of this class with specified routeType.
+     *
+     * @param routeType Route type
+     * @return object of OSPFRouteTypeTlv
+     */
+    public static OSPFRouteTypeTlv of(final byte routeType) {
+        return new OSPFRouteTypeTlv(routeType);
+    }
+
+    /**
+     * Returns RouteType.
+     *
+     * @return RouteType
+     * @throws BGPParseException if routeType is not matched
+     */
+    public ROUTETYPE getValue() throws BGPParseException {
+        switch (routeType) {
+        case INTRA_AREA_TYPE:
+            return ROUTETYPE.Intra_Area;
+        case INTER_AREA_TYPE:
+            return ROUTETYPE.Inter_Area;
+        case EXTERNAL_TYPE_1:
+            return ROUTETYPE.External_1;
+        case EXTERNAL_TYPE_2:
+            return ROUTETYPE.External_2;
+        case NSSA_TYPE_1:
+            return ROUTETYPE.NSSA_1;
+        case NSSA_TYPE_2:
+            return ROUTETYPE.NSSA_2;
+        default:
+            throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, (byte) 0, null);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(routeType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof OSPFRouteTypeTlv) {
+            OSPFRouteTypeTlv other = (OSPFRouteTypeTlv) obj;
+            return Objects.equals(routeType, other.routeType);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer c) {
+        int iLenStartIndex = c.writerIndex();
+        c.writeShort(TYPE);
+        c.writeShort(LENGTH);
+        c.writeByte(routeType);
+        return c.writerIndex() - iLenStartIndex;
+    }
+
+    /**
+     * Reads from ChannelBuffer and parses OSPFRouteTypeTlv.
+     *
+     * @param cb channelBuffer
+     * @return object of OSPFRouteTypeTlv
+     */
+    public static OSPFRouteTypeTlv read(ChannelBuffer cb) {
+        return OSPFRouteTypeTlv.of(cb.readByte());
+    }
+
+    @Override
+    public short getType() {
+        return TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("Type", TYPE)
+                .add("Length", LENGTH)
+                .add("Value", routeType)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeFlagBitTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeFlagBitTlv.java
new file mode 100755 (executable)
index 0000000..ba02f6d
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgpio.types.attr;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.types.BGPErrorType;
+import org.onosproject.bgpio.types.BGPValueType;
+import org.onosproject.bgpio.util.Validation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Implements BGP attribute node flag.
+ */
+public class BgpAttrNodeFlagBitTlv implements BGPValueType {
+
+    protected static final Logger log = LoggerFactory
+            .getLogger(BgpAttrNodeFlagBitTlv.class);
+
+    public static final int ATTRNODE_FLAGBIT = 1024;
+
+    /* Node flag bit TLV */
+    private boolean bOverloadBit;
+    private boolean bAttachedBit;
+    private boolean bExternalBit;
+    private boolean bABRBit;
+
+    public static final int BIT_SET = 1;
+    public static final int FIRST_BIT = 0x80;
+    public static final int SECOND_BIT = 0x40;
+    public static final int THIRD_BIT = 0x20;
+    public static final int FOURTH_BIT = 0x01;
+
+    /**
+     * Constructor to initialize parameters.
+     *
+     * @param bOverloadBit Overload bit
+     * @param bAttachedBit Attached bit
+     * @param bExternalBit External bit
+     * @param bABRBit ABR Bit
+     */
+    BgpAttrNodeFlagBitTlv(boolean bOverloadBit, boolean bAttachedBit,
+                          boolean bExternalBit, boolean bABRBit) {
+        this.bOverloadBit = bOverloadBit;
+        this.bAttachedBit = bAttachedBit;
+        this.bExternalBit = bExternalBit;
+        this.bABRBit = bABRBit;
+    }
+
+    /**
+     * Reads the Node Flag Bits.
+     *
+     * @param cb ChannelBuffer
+     * @return attribute node flag bit tlv
+     * @throws BGPParseException while parsing BgpAttrNodeFlagBitTlv
+     */
+    public static BgpAttrNodeFlagBitTlv read(ChannelBuffer cb)
+            throws BGPParseException {
+        boolean bOverloadBit = false;
+        boolean bAttachedBit = false;
+        boolean bExternalBit = false;
+        boolean bABRBit = false;
+
+        short lsAttrLength = cb.readShort();
+
+        if (lsAttrLength != 1) {
+            Validation.validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                                   BGPErrorType.ATTRIBUTE_LENGTH_ERROR,
+                                   lsAttrLength);
+        }
+
+        byte nodeFlagBits = cb.readByte();
+
+        bOverloadBit = ((nodeFlagBits & (byte) FIRST_BIT) == FIRST_BIT);
+        bAttachedBit = ((nodeFlagBits & (byte) SECOND_BIT) == SECOND_BIT);
+        bExternalBit = ((nodeFlagBits & (byte) THIRD_BIT) == THIRD_BIT);
+        bABRBit = ((nodeFlagBits & (byte) FOURTH_BIT) == FOURTH_BIT);
+
+        return new BgpAttrNodeFlagBitTlv(bOverloadBit, bAttachedBit,
+                                         bExternalBit, bABRBit);
+    }
+
+    /**
+     * Returns Overload Bit.
+     *
+     * @return Overload Bit
+     */
+    boolean getOverLoadBit() {
+        return bOverloadBit;
+    }
+
+    /**
+     * Returns Attached Bit.
+     *
+     * @return Attached Bit
+     */
+    boolean getAttachedBit() {
+        return bAttachedBit;
+    }
+
+    /**
+     * Returns External Bit.
+     *
+     * @return External Bit
+     */
+    boolean getExternalBit() {
+        return bExternalBit;
+    }
+
+    /**
+     * Returns ABR Bit.
+     *
+     * @return ABR Bit
+     */
+    boolean getABRBit() {
+        return bABRBit;
+    }
+
+    @Override
+    public short getType() {
+        return ATTRNODE_FLAGBIT;
+    }
+
+    @Override
+    public int write(ChannelBuffer cb) {
+        // TODO will be implementing it later
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(bOverloadBit, bAttachedBit, bExternalBit, bABRBit);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof BgpAttrNodeFlagBitTlv) {
+            BgpAttrNodeFlagBitTlv other = (BgpAttrNodeFlagBitTlv) obj;
+            return Objects.equals(bOverloadBit, other.bOverloadBit)
+                    && Objects.equals(bAttachedBit, other.bAttachedBit)
+                    && Objects.equals(bExternalBit, other.bExternalBit)
+                    && Objects.equals(bABRBit, other.bABRBit);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("bOverloadBit", bOverloadBit)
+                .add("bAttachedBit", bAttachedBit)
+                .add("bExternalBit", bExternalBit).add("bABRBit", bABRBit)
+                .toString();
+    }
+}
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeMultiTopologyId.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeMultiTopologyId.java
new file mode 100644 (file)
index 0000000..194d6da
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.types.attr;
+
+import java.util.Objects;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+import org.onosproject.bgpio.types.BGPErrorType;
+import org.onosproject.bgpio.types.BGPValueType;
+import org.onosproject.bgpio.util.Validation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * BGP Multi-Topology ID of the LS attribute.
+ */
+public class BgpAttrNodeMultiTopologyId implements BGPValueType {
+
+    private static final Logger log = LoggerFactory
+            .getLogger(BgpAttrNodeMultiTopologyId.class);
+
+    public static final int ATTRNODE_MULTITOPOLOGY = 263;
+
+    /* Opaque Node Attribute */
+    private short[] multiTopologyId;
+
+    /**
+     * Constructor to initialize the Node attribute multi-topology ID.
+     *
+     * @param multiTopologyId multi-topology ID
+     */
+    BgpAttrNodeMultiTopologyId(short[] multiTopologyId) {
+        this.multiTopologyId = multiTopologyId;
+    }
+
+    /**
+     * Reads the Multi-topology ID of Node attribute.
+     *
+     * @param cb ChannelBuffer
+     * @return Constructor of BgpAttrNodeMultiTopologyId
+     * @throws BGPParseException while parsing BgpAttrNodeMultiTopologyId
+     */
+    public static BgpAttrNodeMultiTopologyId read(ChannelBuffer cb)
+            throws BGPParseException {
+
+        log.debug("BgpAttrNodeMultiTopologyId");
+        short lsAttrLength = cb.readShort();
+        int len = lsAttrLength / 2; // Length is 2*n and n is the number of MT-IDs
+
+        if (cb.readableBytes() < lsAttrLength) {
+            Validation.validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR,
+                                   BGPErrorType.ATTRIBUTE_LENGTH_ERROR,
+                                   cb.readableBytes());
+        }
+
+        short[] multiTopologyId;
+        multiTopologyId = new short[len];
+        for (int i = 0; i < len; i++) {
+            multiTopologyId[i] = cb.readShort();
+        }
+
+        return new BgpAttrNodeMultiTopologyId(multiTopologyId);
+    }
+
+    /**
+     * to get the multi-topology ID.
+     *
+     * @return multitopology ID
+     */
+    short[] getAttrMultiTopologyId() {
+        return multiTopologyId;
+    }
+
+    @Override
+    public short getType() {
+        return ATTRNODE_MULTITOPOLOGY;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(multiTopologyId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof BgpAttrNodeMultiTopologyId) {
+            BgpAttrNodeMultiTopologyId other = (BgpAttrNodeMultiTopologyId) obj;
+            return Objects.equals(multiTopologyId, other.multiTopologyId);
+        }
+        return false;
+    }
+
+    @Override
+    public int write(ChannelBuffer cb) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .omitNullValues()
+                .add("multiTopologyId", multiTopologyId)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/package-info.java
new file mode 100755 (executable)
index 0000000..e2a74db
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of BGP Link state attribute Tlvs.
+ */
+package org.onosproject.bgpio.types.attr;
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/package-info.java
new file mode 100755 (executable)
index 0000000..1f2ed95
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of Tlvs, Attributes and Descriptors.
+ */
+package org.onosproject.bgpio.types;
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/UnSupportedAttribute.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/UnSupportedAttribute.java
new file mode 100644 (file)
index 0000000..663b1e9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.util;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides methods to handle UnSupportedAttribute.
+ */
+public final class UnSupportedAttribute {
+    protected static final Logger log = LoggerFactory.getLogger(UnSupportedAttribute.class);
+
+    private UnSupportedAttribute() {
+    }
+
+    /**
+     * Reads channel buffer parses attribute header and skips specified length.
+     *
+     * @param cb channelBuffer
+     */
+    public static void read(ChannelBuffer cb) {
+        Validation parseFlags = Validation.parseAttributeHeader(cb);
+        cb.skipBytes(parseFlags.getLength());
+    }
+
+    /**
+     * Skip specified bytes in channel buffer.
+     *
+     * @param cb channelBuffer
+     * @param length to be skipped
+     */
+    public static void skipBytes(ChannelBuffer cb, short length) {
+        cb.skipBytes(length);
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Validation.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Validation.java
new file mode 100644 (file)
index 0000000..915aa58
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.util;
+
+import java.util.Arrays;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.bgpio.exceptions.BGPParseException;
+
+import com.google.common.primitives.Ints;
+
+/**
+ * Provides methods to parse attribute header, validate length and type.
+ */
+public class Validation {
+    public static final byte FIRST_BIT = (byte) 0x80;
+    public static final byte SECOND_BIT = 0x40;
+    public static final byte THIRD_BIT = 0x20;
+    public static final byte FOURTH_BIT = 0x01;
+    public static final byte IPV4_SIZE = 4;
+    private boolean firstBit;
+    private boolean secondBit;
+    private boolean thirdBit;
+    private boolean fourthBit;
+    private int len;
+    private boolean isShort;
+
+    Validation(boolean firstBit, boolean secondBit, boolean thirdBit, boolean fourthBit, int len, boolean isShort) {
+        this.firstBit = firstBit;
+        this.secondBit = secondBit;
+        this.thirdBit = thirdBit;
+        this.fourthBit = fourthBit;
+        this.len = len;
+        this.isShort = isShort;
+    }
+
+    /**
+     * Parses attribute Header.
+     *
+     * @param cb ChannelBuffer
+     * @return object of Validation
+     */
+    public static Validation parseAttributeHeader(ChannelBuffer cb) {
+
+        boolean firstBit;
+        boolean secondBit;
+        boolean thirdBit;
+        boolean fourthBit;
+        boolean isShort;
+        byte flags = cb.readByte();
+        byte typeCode = cb.readByte();
+        byte temp = flags;
+        //first Bit : Optional (1) or well-known (0)
+        firstBit = ((temp & FIRST_BIT) == FIRST_BIT);
+        //second Bit : Transitive (1) or non-Transitive (0)
+        secondBit = ((temp & SECOND_BIT) == SECOND_BIT);
+        //third Bit : partial (1) or complete (0)
+        thirdBit = ((temp & THIRD_BIT) == THIRD_BIT);
+        //forth Bit(Extended Length bit) : Attribute Length is 1 octects (0) or 2 octects (1)
+        fourthBit = ((temp & FOURTH_BIT) == FOURTH_BIT);
+        int len;
+        if (fourthBit) {
+            isShort = true;
+            short length = cb.readShort();
+            len = length;
+        } else {
+            isShort = false;
+            byte length = cb.readByte();
+            len = length;
+        }
+        return new Validation(firstBit, secondBit, thirdBit, fourthBit, len, isShort);
+    }
+
+    /**
+     * Throws exception if length is not correct.
+     *
+     * @param errorCode Error code
+     * @param subErrCode Sub Error Code
+     * @param length erroneous length
+     * @throws BGPParseException for erroneous length
+     */
+    public static void validateLen(byte errorCode, byte subErrCode, int length) throws BGPParseException {
+        byte[] errLen = Ints.toByteArray(length);
+        ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
+        buffer.writeBytes(errLen);
+        throw new BGPParseException(errorCode, subErrCode, buffer);
+    }
+
+    /**
+     * Throws exception if type is not correct.
+     *
+     * @param errorCode Error code
+     * @param subErrCode Sub Error Code
+     * @param type erroneous type
+     * @throws BGPParseException for erroneous type
+     */
+    public static void validateType(byte errorCode, byte subErrCode, int type) throws BGPParseException {
+        byte[] errType = Ints.toByteArray(type);
+        ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
+        buffer.writeBytes(errType);
+        throw new BGPParseException(errorCode, subErrCode, buffer);
+    }
+
+    /**
+     * Returns first bit in type flags.
+     *
+     * @return first bit in type flags
+     */
+    public boolean getFirstBit() {
+        return this.firstBit;
+    }
+
+    /**
+     * Returns second bit in type flags.
+     *
+     * @return second bit in type flags
+     */
+    public boolean getSecondBit() {
+        return this.secondBit;
+    }
+
+    /**
+     * Returns third bit in type flags.
+     *
+     * @return third bit in type flags
+     */
+    public boolean getThirdBit() {
+        return this.thirdBit;
+    }
+
+    /**
+     * Returns fourth bit in type flags.
+     *
+     * @return fourth bit in type flags
+     */
+    public boolean getFourthBit() {
+        return this.fourthBit;
+    }
+
+    /**
+     * Returns attribute length.
+     *
+     * @return attribute length
+     */
+    public int getLength() {
+        return this.len;
+    }
+
+    /**
+     * Returns whether attribute length read in short or byte.
+     *
+     * @return whether attribute length read in short or byte
+     */
+    public boolean isShort() {
+        return this.isShort;
+    }
+
+    /**
+     * Converts byte array of prefix value to IpPrefix object.
+     *
+     * @param value byte array of prefix value
+     * @param length prefix length in bits
+     * @return object of IpPrefix
+     */
+    public static IpPrefix bytesToPrefix(byte[] value, int length) {
+        if (value.length != IPV4_SIZE) {
+            value = Arrays.copyOf(value, IPV4_SIZE);
+        }
+        IpPrefix ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, length);
+        return ipPrefix;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/package-info.java
new file mode 100755 (executable)
index 0000000..3229d89
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of BGP utility functions.
+ */
+package org.onosproject.bgpio.util;
diff --git a/framework/src/onos/bgp/ctl/pom.xml b/framework/src/onos/bgp/ctl/pom.xml
new file mode 100755 (executable)
index 0000000..5cefd73
--- /dev/null
@@ -0,0 +1,65 @@
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-bgp</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-bgp-ctl</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS BGP controller subsystem API</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-bgp-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java
new file mode 100755 (executable)
index 0000000..942d365
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgp.controller.impl;
+
+import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
+
+/**
+ * Channel handler deals with the bgp peer connection and dispatches messages from peer to the appropriate locations.
+ */
+class BGPChannelHandler extends IdleStateAwareChannelHandler {
+
+    // TODO: implement FSM and session handling mechanism
+    /**
+     * Create a new unconnected BGPChannelHandler.
+     *
+     * @param bgpCtrlImpl bgp controller implementation object
+     */
+    BGPChannelHandler(BGPControllerImpl bgpCtrlImpl) {
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPConfig.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPConfig.java
new file mode 100755 (executable)
index 0000000..56877a1
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller.impl;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.onlab.packet.Ip4Address;
+import org.onosproject.bgp.controller.BGPCfg;
+import org.onosproject.bgp.controller.BGPPeerCfg;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides BGP configuration of this BGP speaker.
+ */
+public class BGPConfig implements BGPCfg {
+
+    protected static final Logger log = LoggerFactory.getLogger(BGPConfig.class);
+
+    private static final short DEFAULT_HOLD_TIMER = 120;
+    private static final short DEFAULT_CONN_RETRY_TIME = 120;
+    private static final short DEFAULT_CONN_RETRY_COUNT = 5;
+
+    private State state = State.INIT;
+    private int localAs;
+    private int maxSession;
+    private boolean lsCapability;
+    private short holdTime;
+    private boolean largeAs = false;
+    private int maxConnRetryTime;
+    private int maxConnRetryCount;
+
+    private Ip4Address routerId = null;
+    private TreeMap<String, BGPPeerCfg> bgpPeerTree = new TreeMap<>();
+
+    /**
+     * Constructor to initialize the values.
+     */
+    public BGPConfig() {
+
+        this.holdTime = DEFAULT_HOLD_TIMER;
+        this.maxConnRetryTime = DEFAULT_CONN_RETRY_TIME;
+        this.maxConnRetryCount = DEFAULT_CONN_RETRY_COUNT;
+    }
+
+    @Override
+    public State getState() {
+        return state;
+    }
+
+    @Override
+    public void setState(State state) {
+        this.state = state;
+    }
+
+    @Override
+    public int getAsNumber() {
+        return this.localAs;
+    }
+
+    @Override
+    public void setAsNumber(int localAs) {
+
+        State localState = getState();
+        this.localAs = localAs;
+
+        /* Set configuration state */
+        if (localState == State.IP_CONFIGURED) {
+            setState(State.IP_AS_CONFIGURED);
+        } else {
+            setState(State.AS_CONFIGURED);
+        }
+    }
+
+    @Override
+    public int getMaxSession() {
+        return this.maxSession;
+    }
+
+    @Override
+    public void setMaxSession(int maxSession) {
+        this.maxSession = maxSession;
+    }
+
+    @Override
+    public boolean getLsCapability() {
+        return this.lsCapability;
+    }
+
+    @Override
+    public void setLsCapability(boolean lsCapability) {
+        this.lsCapability = lsCapability;
+    }
+
+    @Override
+    public String getRouterId() {
+        if (this.routerId != null) {
+            return this.routerId.toString();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setRouterId(String routerId) {
+        State localState = getState();
+        this.routerId = Ip4Address.valueOf(routerId);
+
+        /* Set configuration state */
+        if (localState == State.AS_CONFIGURED) {
+            setState(State.IP_AS_CONFIGURED);
+        } else {
+            setState(State.IP_CONFIGURED);
+        }
+    }
+
+    @Override
+    public boolean addPeer(String routerid, int remoteAs) {
+        return addPeer(routerid, remoteAs, DEFAULT_HOLD_TIMER);
+    }
+
+    @Override
+    public boolean addPeer(String routerid, short holdTime) {
+        return addPeer(routerid, this.getAsNumber(), holdTime);
+    }
+
+    @Override
+    public boolean addPeer(String routerid, int remoteAs, short holdTime) {
+        BGPPeerConfig lspeer = new BGPPeerConfig();
+        if (this.bgpPeerTree.get(routerid) == null) {
+
+            lspeer.setPeerRouterId(routerid);
+            lspeer.setAsNumber(remoteAs);
+            lspeer.setHoldtime(holdTime);
+            lspeer.setState(BGPPeerCfg.State.IDLE);
+            lspeer.setSelfInnitConnection(false);
+
+            if (this.getAsNumber() == remoteAs) {
+                lspeer.setIsIBgp(true);
+            } else {
+                lspeer.setIsIBgp(false);
+            }
+
+            this.bgpPeerTree.put(routerid, lspeer);
+            log.debug("added successfully");
+            return true;
+        } else {
+            log.debug("already exists");
+            return false;
+        }
+    }
+
+    @Override
+    public boolean connectPeer(String routerid) {
+        BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid);
+
+        if (lspeer != null) {
+            lspeer.setSelfInnitConnection(true);
+            // TODO: initiate peer connection
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean removePeer(String routerid) {
+        BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid);
+
+        if (lspeer != null) {
+
+            //TODO DISCONNECT PEER
+            disconnectPeer(routerid);
+            lspeer.setSelfInnitConnection(false);
+            lspeer = this.bgpPeerTree.remove(routerid);
+            log.debug("Deleted : " + routerid + " successfully");
+
+            return true;
+        } else {
+            log.debug("Did not find : " + routerid);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean disconnectPeer(String routerid) {
+        BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid);
+
+        if (lspeer != null) {
+
+            //TODO DISCONNECT PEER
+            lspeer.setState(BGPPeerCfg.State.IDLE);
+            lspeer.setSelfInnitConnection(false);
+            log.debug("Disconnected : " + routerid + " successfully");
+
+            return true;
+        } else {
+            log.debug("Did not find : " + routerid);
+            return false;
+        }
+    }
+
+    @Override
+    public void setPeerConnState(String routerid, BGPPeerCfg.State state) {
+        BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid);
+
+        if (lspeer != null) {
+            lspeer.setState(state);
+            log.debug("Peer : " + routerid + " is not available");
+
+            return;
+        } else {
+            log.debug("Did not find : " + routerid);
+            return;
+        }
+    }
+
+    @Override
+    public BGPPeerCfg.State getPeerConnState(String routerid) {
+        BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid);
+
+        if (lspeer != null) {
+            return lspeer.getState();
+        } else {
+            return BGPPeerCfg.State.INVALID; //No instance
+        }
+    }
+
+    @Override
+    public boolean isPeerConnectable(String routerid) {
+        BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid);
+
+        if ((lspeer != null) && lspeer.getState().equals(BGPPeerCfg.State.IDLE)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public TreeMap<String, BGPPeerCfg> getPeerTree() {
+        return this.bgpPeerTree;
+    }
+
+    @Override
+    public TreeMap<String, BGPPeerCfg> displayPeers() {
+        if (this.bgpPeerTree.isEmpty()) {
+            log.debug("There are no BGP peers");
+        } else {
+            Set<Entry<String, BGPPeerCfg>> set = this.bgpPeerTree.entrySet();
+            Iterator<Entry<String, BGPPeerCfg>> list = set.iterator();
+            BGPPeerCfg lspeer;
+
+            while (list.hasNext()) {
+                Entry<String, BGPPeerCfg> me = list.next();
+                lspeer = me.getValue();
+                log.debug("Peer neighbor IP :" + me.getKey());
+                log.debug(", AS Number : " + lspeer.getAsNumber());
+                log.debug(", Hold Timer : " + lspeer.getHoldtime());
+                log.debug(", Is iBGP : " + lspeer.getIsIBgp());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public BGPPeerCfg displayPeers(String routerid) {
+
+        if (this.bgpPeerTree.isEmpty()) {
+            log.debug("There are no BGP peers");
+        } else {
+            return this.bgpPeerTree.get(routerid);
+        }
+        return null;
+    }
+
+    @Override
+    public void setHoldTime(short holdTime) {
+        this.holdTime = holdTime;
+    }
+
+    @Override
+    public short getHoldTime() {
+        return this.holdTime;
+    }
+
+    @Override
+    public boolean getLargeASCapability() {
+        return this.largeAs;
+    }
+
+    @Override
+    public void setLargeASCapability(boolean largeAs) {
+        this.largeAs = largeAs;
+    }
+
+    @Override
+    public boolean isPeerConfigured(String routerid) {
+        BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid);
+        return (lspeer != null) ? true : false;
+    }
+
+    @Override
+    public boolean isPeerConnected(String routerid) {
+        // TODO: is peer connected
+        return true;
+    }
+
+    @Override
+    public int getMaxConnRetryCount() {
+        return this.maxConnRetryCount;
+    }
+
+    @Override
+    public void setMaxConnRetryCout(int retryCount) {
+        this.maxConnRetryCount = retryCount;
+    }
+
+    @Override
+    public int getMaxConnRetryTime() {
+        return this.maxConnRetryTime;
+    }
+
+    @Override
+    public void setMaxConnRetryTime(int retryTime) {
+        this.maxConnRetryTime = retryTime;
+    }
+}
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPControllerImpl.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPControllerImpl.java
new file mode 100755 (executable)
index 0000000..95eafdb
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgp.controller.impl;
+
+import static org.onlab.util.Tools.groupedThreads;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.bgp.controller.BGPCfg;
+import org.onosproject.bgp.controller.BGPController;
+import org.onosproject.bgp.controller.BGPId;
+import org.onosproject.bgpio.protocol.BGPMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(immediate = true)
+@Service
+public class BGPControllerImpl implements BGPController {
+
+    private static final Logger log = LoggerFactory.getLogger(BGPControllerImpl.class);
+
+    private final ExecutorService executorMsgs = Executors.newFixedThreadPool(32,
+                                                                              groupedThreads("onos/bgp",
+                                                                                      "event-stats-%d"));
+
+    private final ExecutorService executorBarrier = Executors.newFixedThreadPool(4,
+                                                                                 groupedThreads("onos/bgp",
+                                                                                         "event-barrier-%d"));
+
+    final Controller ctrl = new Controller(this);
+
+    private BGPConfig bgpconfig = new BGPConfig();
+
+    @Activate
+    public void activate() {
+        this.ctrl.start();
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        // Close all connected peers
+        this.ctrl.stop();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void writeMsg(BGPId bgpId, BGPMessage msg) {
+        // TODO: Send message
+    }
+
+    @Override
+    public void processBGPPacket(BGPId bgpId, BGPMessage msg) {
+
+        switch (msg.getType()) {
+        case OPEN:
+            // TODO: Process Open message
+            break;
+        case KEEP_ALIVE:
+            // TODO: Process keepalive message
+            break;
+        case NOTIFICATION:
+            // TODO: Process notificatoin message
+            break;
+        case UPDATE:
+            // TODO: Process update message
+            break;
+        default:
+            // TODO: Process other message
+            break;
+        }
+    }
+
+    /**
+     * Get controller instance.
+     *
+     * @return ctrl the controller.
+     */
+    public Controller getController() {
+        return ctrl;
+    }
+
+    @Override
+    public BGPCfg getConfig() {
+        return this.bgpconfig;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageDecoder.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageDecoder.java
new file mode 100755 (executable)
index 0000000..39f2862
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.onosproject.bgpio.protocol.BGPMessage;
+import org.onlab.util.HexDump;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Decode an bgp message from a Channel, for use in a netty pipeline.
+ */
+public class BGPMessageDecoder extends FrameDecoder {
+
+    protected static final Logger log = LoggerFactory.getLogger(BGPMessageDecoder.class);
+
+    @Override
+    protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
+
+        List<BGPMessage> msgList = new LinkedList<BGPMessage>();
+
+        log.debug("MESSAGE IS RECEIVED.");
+        if (!channel.isConnected()) {
+            log.info("Channel is not connected.");
+            return null;
+        }
+
+        HexDump.dump(buffer);
+
+        // TODO: decode bgp messages
+        return msgList;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageEncoder.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageEncoder.java
new file mode 100755 (executable)
index 0000000..f0d38c3
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller.impl;
+
+import java.util.List;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
+import org.onosproject.bgpio.protocol.BGPMessage;
+import org.onlab.util.HexDump;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Encode an bgp message for output into a ChannelBuffer, for use in a
+ * netty pipeline.
+ */
+public class BGPMessageEncoder extends OneToOneEncoder {
+    protected static final Logger log = LoggerFactory.getLogger(BGPMessageEncoder.class);
+
+    @Override
+    protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
+        log.debug("BGPMessageEncoder::encode");
+        if (!(msg instanceof List)) {
+            log.debug("Invalid msg.");
+            return msg;
+        }
+
+        @SuppressWarnings("unchecked")
+        List<BGPMessage> msglist = (List<BGPMessage>) msg;
+
+        ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+
+        log.debug("SENDING MESSAGE");
+        for (BGPMessage pm : msglist) {
+            pm.writeTo(buf);
+        }
+
+        HexDump.dump(buf);
+
+        return buf;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPacketStatsImpl.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPacketStatsImpl.java
new file mode 100755 (executable)
index 0000000..41407dc
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller.impl;
+
+import org.onosproject.bgp.controller.BGPPacketStats;
+
+/**
+ * A representation of a packet context which allows any provider
+ * to view a packet in event, but may block the response to the
+ * event if blocked has been called. This packet context can be used
+ * to react to the packet in event with a packet out.
+ */
+public class BGPPacketStatsImpl implements BGPPacketStats {
+
+    private int inPacketCount;
+    private int outPacketCount;
+    private int wrongPacketCount;
+    private long time;
+
+    /**
+     * Resets parameter.
+     */
+    public BGPPacketStatsImpl() {
+        this.inPacketCount = 0;
+        this.outPacketCount = 0;
+        this.wrongPacketCount = 0;
+        this.time = 0;
+    }
+
+    /**
+     * Get the outgoing packet count number.
+     *
+     * @return
+     *          packet count
+     */
+    public int outPacketCount() {
+        return outPacketCount;
+    }
+
+    /**
+     * Get the incoming packet count number.
+     *
+     * @return
+     *          packet count
+     */
+    public int inPacketCount() {
+        return inPacketCount;
+    }
+
+    /**
+     * Get the wrong packet count number.
+     *
+     * @return
+     *          packet count
+     */
+    public int wrongPacketCount() {
+        return wrongPacketCount;
+    }
+
+    /**
+     * Increments the received packet counter.
+     */
+    public void addInPacket() {
+        this.inPacketCount++;
+    }
+
+    /**
+     * Increments the sent packet counter.
+     */
+    public void addOutPacket() {
+        this.outPacketCount++;
+    }
+
+    /**
+     * Increments the sent packet counter by specified value.
+     *
+     * @param value of no of packets sent
+     */
+    public void addOutPacket(int value) {
+        this.outPacketCount = this.outPacketCount + value;
+    }
+
+    /**
+     * Increments the wrong packet counter.
+     */
+    public void addWrongPacket() {
+        this.wrongPacketCount++;
+    }
+
+    /**
+     * Resets wrong packet count.
+     */
+    public void resetWrongPacket() {
+        this.wrongPacketCount = 0;
+    }
+
+    /**
+     * Get the time.
+     *
+     * @return
+     *          time
+     */
+    public long getTime() {
+        return this.time;
+    }
+
+    /**
+     * Sets the time.
+     *
+     * @param time value to set
+     */
+    public void setTime(long time) {
+        this.time = time;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPeerConfig.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPeerConfig.java
new file mode 100755 (executable)
index 0000000..51b95a4
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller.impl;
+
+import org.onlab.packet.Ip4Address;
+import org.onosproject.bgp.controller.BGPPeerCfg;
+
+/**
+ * BGP Peer configuration information.
+ */
+public class BGPPeerConfig  implements BGPPeerCfg {
+    private int asNumber;
+    private short holdTime;
+    private boolean isIBgp;
+    private Ip4Address peerId = null;
+    private State state;
+    private boolean selfInitiated;
+
+    /**
+     * Constructor to initialize the values.
+     */
+    BGPPeerConfig() {
+        state = State.IDLE;
+        selfInitiated = false;
+    }
+
+    @Override
+    public int getAsNumber() {
+        return this.asNumber;
+    }
+
+    @Override
+    public void setAsNumber(int asNumber) {
+        this.asNumber = asNumber;
+    }
+
+    @Override
+    public short getHoldtime() {
+        return this.holdTime;
+    }
+
+    @Override
+    public void setHoldtime(short holdTime) {
+        this.holdTime = holdTime;
+    }
+
+    @Override
+    public boolean getIsIBgp() {
+        return this.isIBgp;
+    }
+
+    @Override
+    public void setIsIBgp(boolean isIBgp) {
+        this.isIBgp = isIBgp;
+    }
+
+    @Override
+    public String getPeerRouterId() {
+        if (this.peerId != null) {
+            return this.peerId.toString();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setPeerRouterId(String peerId) {
+        this.peerId = Ip4Address.valueOf(peerId);
+    }
+
+    @Override
+    public void setPeerRouterId(String peerId, int asNumber) {
+        this.peerId = Ip4Address.valueOf(peerId);
+        this.asNumber = asNumber;
+    }
+
+    @Override
+    public State getState() {
+        return this.state;
+    }
+
+    @Override
+    public void setState(State state) {
+        this.state = state;
+    }
+
+    @Override
+    public boolean getSelfInnitConnection() {
+        return this.selfInitiated;
+    }
+
+    @Override
+    public void setSelfInnitConnection(boolean selfInit) {
+        this.selfInitiated = selfInit;
+    }
+}
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPipelineFactory.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPipelineFactory.java
new file mode 100755 (executable)
index 0000000..b2ca507
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgp.controller.impl;
+
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
+import org.jboss.netty.util.ExternalResourceReleasable;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timer;
+
+/**
+ * Creates a ChannelPipeline for a server-side bgp channel.
+ */
+public class BGPPipelineFactory
+    implements ChannelPipelineFactory, ExternalResourceReleasable {
+
+    static final Timer TIMER = new HashedWheelTimer();
+    protected ReadTimeoutHandler readTimeoutHandler;
+    BGPControllerImpl bgpCtrlImpl;
+
+    /**
+     * Constructor to initialize the values.
+     *
+     * @param ctrlImpl parent ctrlImpl
+     * @param isServBgp if it is a server or not
+     */
+    public BGPPipelineFactory(BGPControllerImpl ctrlImpl, boolean isServBgp) {
+        super();
+        bgpCtrlImpl = ctrlImpl;
+        /* hold time*/
+        readTimeoutHandler = new ReadTimeoutHandler(TIMER, bgpCtrlImpl.getConfig().getHoldTime());
+    }
+
+    @Override
+    public ChannelPipeline getPipeline() throws Exception {
+        BGPChannelHandler handler = new BGPChannelHandler(bgpCtrlImpl);
+
+        ChannelPipeline pipeline = Channels.pipeline();
+        pipeline.addLast("bgpmessagedecoder", new BGPMessageDecoder());
+        pipeline.addLast("bgpmessageencoder", new BGPMessageEncoder());
+        pipeline.addLast("holdTime", readTimeoutHandler);
+        pipeline.addLast("PassiveHandler", handler);
+
+        return pipeline;
+    }
+
+    @Override
+    public void releaseExternalResources() {
+        TIMER.stop();
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/Controller.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/Controller.java
new file mode 100755 (executable)
index 0000000..402e8c9
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.bgp.controller.impl;
+
+import static org.onlab.util.Tools.groupedThreads;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executors;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.jboss.netty.channel.group.DefaultChannelGroup;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The main controller class. Handles all setup and network listeners - Distributed ownership control of bgp peer
+ * through IControllerRegistryService
+ */
+public class Controller {
+
+    protected static final Logger log = LoggerFactory.getLogger(Controller.class);
+
+    private ChannelGroup cg;
+
+    // Configuration options
+    private static final short BGP_PORT_NUM = 179;
+    private int workerThreads = 16;
+
+    // Start time of the controller
+    protected long systemStartTime;
+
+    private NioServerSocketChannelFactory serverExecFactory;
+
+    // Perf. related configuration
+    protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
+
+    BGPControllerImpl bgpCtrlImpl;
+
+    /**
+     * Constructor to initialize parameter.
+     *
+     * @param bgpCtrlImpl BGP controller Impl instance
+     */
+    public Controller(BGPControllerImpl bgpCtrlImpl) {
+        this.bgpCtrlImpl = bgpCtrlImpl;
+    }
+
+    // ***************
+    // Getters/Setters
+    // ***************
+
+    /**
+     * To get system start time.
+     *
+     * @return system start time in milliseconds
+     */
+    public long getSystemStartTime() {
+        return (this.systemStartTime);
+    }
+
+    // **************
+    // Initialization
+    // **************
+
+    /**
+     * Tell controller that we're ready to accept bgp peer connections.
+     */
+    public void run() {
+
+        try {
+            final ServerBootstrap bootstrap = createServerBootStrap();
+
+            bootstrap.setOption("reuseAddr", true);
+            bootstrap.setOption("child.keepAlive", true);
+            bootstrap.setOption("child.tcpNoDelay", true);
+            bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
+
+            ChannelPipelineFactory pfact = new BGPPipelineFactory(bgpCtrlImpl, true);
+
+            bootstrap.setPipelineFactory(pfact);
+            InetSocketAddress sa = new InetSocketAddress(getBgpPortNum());
+            cg = new DefaultChannelGroup();
+            cg.add(bootstrap.bind(sa));
+            log.info("Listening for Peer connection on {}", sa);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Creates server boot strap.
+     *
+     * @return ServerBootStrap
+     */
+    private ServerBootstrap createServerBootStrap() {
+
+        if (workerThreads == 0) {
+            serverExecFactory = new NioServerSocketChannelFactory(
+                                    Executors.newCachedThreadPool(groupedThreads("onos/bgp", "boss-%d")),
+                                    Executors.newCachedThreadPool(groupedThreads("onos/bgp", "worker-%d")));
+            return new ServerBootstrap(serverExecFactory);
+        } else {
+            serverExecFactory = new NioServerSocketChannelFactory(
+                                    Executors.newCachedThreadPool(groupedThreads("onos/bgp", "boss-%d")),
+                                    Executors.newCachedThreadPool(groupedThreads("onos/bgp", "worker-%d")),
+                                                                  workerThreads);
+            return new ServerBootstrap(serverExecFactory);
+        }
+    }
+
+    /**
+     * Initialize internal data structures.
+     */
+    public void init() {
+        // These data structures are initialized here because other
+        // module's startUp() might be called before ours
+        this.systemStartTime = System.currentTimeMillis();
+    }
+
+    // **************
+    // Utility methods
+    // **************
+
+    public Map<String, Long> getMemory() {
+        Map<String, Long> m = new HashMap<>();
+        Runtime runtime = Runtime.getRuntime();
+        m.put("total", runtime.totalMemory());
+        m.put("free", runtime.freeMemory());
+        return m;
+    }
+
+    public Long getUptime() {
+        RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
+        return rb.getUptime();
+    }
+
+    /**
+     * Starts the BGP controller.
+     */
+    public void start() {
+        log.info("Started");
+        this.init();
+        this.run();
+    }
+
+    /**
+     * Stops the BGP controller.
+     */
+    public void stop() {
+        log.info("Stopped");
+        serverExecFactory.shutdown();
+        cg.close();
+    }
+
+    /**
+     * Returns port number.
+     *
+     * @return port number
+     */
+    public static short getBgpPortNum() {
+        return BGP_PORT_NUM;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/package-info.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/package-info.java
new file mode 100755 (executable)
index 0000000..fd4e950
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the BGP controller IO subsystem.
+ */
+package org.onosproject.bgp.controller.impl;
diff --git a/framework/src/onos/bgp/pom.xml b/framework/src/onos/bgp/pom.xml
new file mode 100755 (executable)
index 0000000..941168b
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-bgp</artifactId>
+    <packaging>pom</packaging>
+
+    <description>ONOS BGP Protocol subsystem</description>
+
+    <modules>
+        <module>api</module>
+        <module>ctl</module>
+        <module>bgpio</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
index b0cbbdd..1df2f04 100644 (file)
@@ -25,6 +25,8 @@ import org.onosproject.net.ElementId;
 import org.onosproject.net.Port;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.group.Group;
+
+import org.onosproject.net.statistic.TypedFlowEntryWithLoad;
 import org.onosproject.net.topology.TopologyCluster;
 
 import java.util.Comparator;
@@ -115,4 +117,12 @@ public final class Comparators {
     public static final Comparator<Interface> INTERFACES_COMPARATOR = (intf1, intf2) ->
             CONNECT_POINT_COMPARATOR.compare(intf1.connectPoint(), intf2.connectPoint());
 
+    public static final Comparator<TypedFlowEntryWithLoad> TYPEFLOWENTRY_WITHLOAD_COMPARATOR =
+            new Comparator<TypedFlowEntryWithLoad>() {
+                @Override
+                public int compare(TypedFlowEntryWithLoad fe1, TypedFlowEntryWithLoad fe2) {
+                    long delta = fe1.load().rate() -  fe2.load().rate();
+                    return delta == 0 ? 0 : (delta > 0 ? -1 : +1);
+                }
+            };
 }
index d99a183..7ce5669 100644 (file)
@@ -35,13 +35,13 @@ import static com.google.common.base.Strings.isNullOrEmpty;
         description = "Manages network configuration")
 public class NetworkConfigCommand extends AbstractShellCommand {
 
-    @Argument(index = 0, name = "subjectKey", description = "Subject key",
+    @Argument(index = 0, name = "subjectClassKey", description = "Subject class key",
             required = false, multiValued = false)
-    String subjectKey = null;
+    String subjectClassKey = null;
 
-    @Argument(index = 1, name = "subject", description = "Subject",
+    @Argument(index = 1, name = "subjectKey", description = "Subject key",
             required = false, multiValued = false)
-    String subject = null;
+    String subjectKey = null;
 
     @Argument(index = 2, name = "configKey", description = "Config key",
             required = false, multiValued = false)
@@ -54,18 +54,18 @@ public class NetworkConfigCommand extends AbstractShellCommand {
     protected void execute() {
         service = get(NetworkConfigService.class);
         JsonNode root = mapper.createObjectNode();
-        if (isNullOrEmpty(subjectKey)) {
+        if (isNullOrEmpty(subjectClassKey)) {
             addAll((ObjectNode) root);
         } else {
-            SubjectFactory subjectFactory = service.getSubjectFactory(subjectKey);
-            if (isNullOrEmpty(subject)) {
+            SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey);
+            if (isNullOrEmpty(subjectKey)) {
                 addSubjectClass((ObjectNode) root, subjectFactory);
             } else {
-                Object s = subjectFactory.createSubject(subject);
+                Object s = subjectFactory.createSubject(subjectKey);
                 if (isNullOrEmpty(configKey)) {
                     addSubject((ObjectNode) root, s);
                 } else {
-                    root = getSubjectConfig(getConfig(s, subjectKey, configKey));
+                    root = getSubjectConfig(getConfig(s, subjectClassKey, configKey));
                 }
             }
         }
@@ -82,14 +82,14 @@ public class NetworkConfigCommand extends AbstractShellCommand {
         service.getSubjectClasses()
                 .forEach(sc -> {
                     SubjectFactory sf = service.getSubjectFactory(sc);
-                    addSubjectClass(newObject(root, sf.subjectKey()), sf);
+                    addSubjectClass(newObject(root, sf.subjectClassKey()), sf);
                 });
     }
 
     @SuppressWarnings("unchecked")
     private void addSubjectClass(ObjectNode root, SubjectFactory sf) {
         service.getSubjects(sf.subjectClass())
-                .forEach(s -> addSubject(newObject(root, s.toString()), s));
+                .forEach(s -> addSubject(newObject(root, sf.subjectKey(s)), s));
     }
 
     private void addSubject(ObjectNode root, Object s) {
index f94967e..cf58877 100644 (file)
@@ -42,7 +42,7 @@ public class NetworkConfigRegistryCommand extends AbstractShellCommand {
 
     private void print(ConfigFactory configFactory) {
         print(shortOnly ? SHORT_FMT : FMT,
-              configFactory.subjectFactory().subjectKey(),
+              configFactory.subjectFactory().subjectClassKey(),
               configFactory.configKey(),
               configFactory.subjectFactory().subjectClass().getName(),
               configFactory.configClass().getName());
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceControllersCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceControllersCommand.java
new file mode 100644 (file)
index 0000000..328d470
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerConfig;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+
+/**
+ * Sets role of the controller node for the given infrastructure device.
+ */
+@Command(scope = "onos", name = "device-controllers",
+        description = "gets the list of controllers for the given infrastructure device")
+public class DeviceControllersCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "uri", description = "Device ID",
+            required = true, multiValued = false)
+    String uri = null;
+    private DeviceId deviceId;
+
+    @Override
+    protected void execute() {
+        DriverService service = get(DriverService.class);
+        deviceId = DeviceId.deviceId(uri);
+        DriverHandler h = service.createHandler(deviceId);
+        ControllerConfig config = h.behaviour(ControllerConfig.class);
+        config.getControllers().forEach(c -> print(c.target()));
+    }
+
+}
index 494273c..e2d3e6d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,8 +22,12 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
+import org.onlab.util.Frequency;
 import org.onosproject.cli.Comparators;
 import org.onosproject.net.Device;
+import org.onosproject.net.OchPort;
+import org.onosproject.net.OduCltPort;
+import org.onosproject.net.OmsPort;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
@@ -41,7 +45,10 @@ import static org.onosproject.net.DeviceId.deviceId;
          description = "Lists all ports or all ports of a device")
 public class DevicePortsListCommand extends DevicesListCommand {
 
-    private static final String FMT = "  port=%s, state=%s, type=%s, speed=%s%s";
+    private static final String FMT = "  port=%s, state=%s, type=%s, speed=%s %s";
+    private static final String FMT_OCH = "  port=%s, state=%s, type=%s, signalType=%s, isTunable=%s %s";
+    private static final String FMT_ODUCLT = "  port=%s, state=%s, type=%s, signalType=%s %s";
+    private static final String FMT_OMS = "  port=%s, state=%s, type=%s, Freqs= %s / %s / %s GHz, totalChannels=%s %s";
 
     @Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports",
             required = false, multiValued = false)
@@ -137,13 +144,34 @@ public class DevicePortsListCommand extends DevicesListCommand {
         List<Port> ports = new ArrayList<>(service.getPorts(device.id()));
         Collections.sort(ports, Comparators.PORT_COMPARATOR);
         for (Port port : ports) {
-            if (isIncluded(port)) {
-                print(FMT, portName(port.number()),
-                      port.isEnabled() ? "enabled" : "disabled",
-                      port.type().toString().toLowerCase(), port.portSpeed(),
-                      annotations(port.annotations()));
+            if (!isIncluded(port)) {
+                continue;
+            }
+            String portName = portName(port.number());
+            Object portIsEnabled = port.isEnabled() ? "enabled" : "disabled";
+            String portType = port.type().toString().toLowerCase();
+            String annotations = annotations(port.annotations());
+            switch (port.type()) {
+                case OCH:
+                     print(FMT_OCH, portName, portIsEnabled, portType,
+                             ((OchPort) port).signalType().toString(),
+                             ((OchPort) port).isTunable() ? "yes" : "no", annotations);
+                     break;
+                case ODUCLT:
+                     print(FMT_ODUCLT, portName, portIsEnabled, portType,
+                            ((OduCltPort) port).signalType().toString(), annotations);
+                     break;
+                case OMS:
+                     print(FMT_OMS, portName, portIsEnabled, portType,
+                                ((OmsPort) port).minFrequency().asHz() / Frequency.ofGHz(1).asHz(),
+                                ((OmsPort) port).maxFrequency().asHz() / Frequency.ofGHz(1).asHz(),
+                                ((OmsPort) port).grid().asHz() / Frequency.ofGHz(1).asHz(),
+                                ((OmsPort) port).totalChannels(), annotations);
+                    break;
+                default:
+                     print(FMT, portName, portIsEnabled, portType, port.portSpeed(), annotations);
+                    break;
             }
         }
     }
-
 }
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceSetControllersCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceSetControllersCommand.java
new file mode 100644 (file)
index 0000000..c369379
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerConfig;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Sets role of the controller node for the given infrastructure device.
+ */
+@Command(scope = "onos", name = "device-setcontrollers",
+        description = "sets the list of controllers for the given infrastructure device")
+public class DeviceSetControllersCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "uri", description = "Device ID",
+            required = true, multiValued = false)
+    String uri = null;
+
+    @Argument(index = 1, name = "controllersListStrings", description = "list of " +
+            "controllers to set for the specified device",
+            required = true, multiValued = true)
+    String[] controllersListStrings = null;
+
+    private DeviceId deviceId;
+    private List<ControllerInfo> newControllers = new ArrayList<>();
+
+    @Override
+    protected void execute() {
+
+        Arrays.asList(controllersListStrings).forEach(
+                cInfoString -> newControllers.add(new ControllerInfo(cInfoString)));
+        DriverService service = get(DriverService.class);
+        deviceId = DeviceId.deviceId(uri);
+        DriverHandler h = service.createHandler(deviceId);
+        ControllerConfig config = h.behaviour(ControllerConfig.class);
+        print("before:");
+        config.getControllers().forEach(c -> print(c.target()));
+
+        config.setControllers(newControllers);
+        print("after:");
+        config.getControllers().forEach(c -> print(c.target()));
+        print("size %d", config.getControllers().size());
+    }
+
+}
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java
new file mode 100644 (file)
index 0000000..cafe87f
--- /dev/null
@@ -0,0 +1,323 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.cli.net;\r
+\r
+import org.apache.karaf.shell.commands.Argument;\r
+import org.apache.karaf.shell.commands.Command;\r
+import org.apache.karaf.shell.commands.Option;\r
+import org.onosproject.cli.AbstractShellCommand;\r
+import org.onosproject.net.ConnectPoint;\r
+import org.onosproject.net.Device;\r
+import org.onosproject.net.DeviceId;\r
+import org.onosproject.net.Port;\r
+import org.onosproject.net.PortNumber;\r
+import org.onosproject.net.device.DeviceService;\r
+import org.onosproject.net.flow.TypedStoredFlowEntry;\r
+import org.onosproject.net.flow.instructions.Instruction;\r
+import org.onosproject.net.statistic.FlowStatisticService;\r
+import org.onosproject.net.statistic.SummaryFlowEntryWithLoad;\r
+import org.onosproject.net.statistic.TypedFlowEntryWithLoad;\r
+\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import static org.onosproject.net.DeviceId.deviceId;\r
+import static org.onosproject.net.PortNumber.portNumber;\r
+\r
+/**\r
+ * Fetches flow statistics with a flow type and instruction type.\r
+ */\r
+@Command(scope = "onos", name = "get-flow-stats",\r
+        description = "Fetches flow stats for a connection point with given flow type and instruction type")\r
+public class GetFlowStatistics extends AbstractShellCommand {\r
+    @Argument(index = 0, name = "devicePort",\r
+            description = "Device[/Port] connectPoint Description",\r
+            required = true, multiValued = false)\r
+    String devicePort = null;\r
+\r
+    @Option(name = "-s", aliases = "--summary",\r
+            description = "Show flow stats summary",\r
+            required = false, multiValued = false)\r
+    boolean showSummary = true; // default summary\r
+\r
+    @Option(name = "-a", aliases = "--all",\r
+            description = "Show flow stats all",\r
+            required = false, multiValued = false)\r
+    boolean showAll = false;\r
+\r
+    @Option(name = "-t", aliases = "--topn",\r
+            description = "Show flow stats topn",\r
+            required = false, multiValued = false)\r
+    String showTopn = null;\r
+\r
+    @Option(name = "-f", aliases = "--flowType",\r
+            description = "Flow live type, It includes IMMEDIATE, SHORT, MID, LONG, UNKNOWN"\r
+                          + ", and is valid with -a or -t option only",\r
+            required = false, multiValued = false)\r
+    String flowLiveType = null;\r
+\r
+    @Option(name = "-i", aliases = "--instructionType",\r
+            description = "Flow instruction type, It includes DROP, OUTPUT, GROUP, L0MODIFICATION, L2MODIFICATION,"\r
+                    + " TABLE, L3MODIFICATION, METADATA"\r
+                    + ", and is valid with -a or -t option only",\r
+            required = false, multiValued = false)\r
+    String instructionType = null;\r
+\r
+    @Override\r
+    protected void execute() {\r
+        DeviceService deviceService = get(DeviceService.class);\r
+        FlowStatisticService flowStatsService = get(FlowStatisticService.class);\r
+\r
+        String deviceURI = getDeviceId(devicePort);\r
+        String portURI = getPortNumber(devicePort);\r
+\r
+        DeviceId ingressDeviceId = deviceId(deviceURI);\r
+        PortNumber ingressPortNumber;\r
+        if (portURI.length() == 0) {\r
+            ingressPortNumber = null;\r
+        } else {\r
+            ingressPortNumber = portNumber(portURI);\r
+        }\r
+\r
+        Device device = deviceService.getDevice(ingressDeviceId);\r
+        if (device == null) {\r
+            error("No such device %s", ingressDeviceId.uri());\r
+            return;\r
+        }\r
+\r
+        if (ingressPortNumber != null) {\r
+            Port port = deviceService.getPort(ingressDeviceId, ingressPortNumber);\r
+            if (port == null) {\r
+                error("No such port %s on device %s", portURI, ingressDeviceId.uri());\r
+                return;\r
+            }\r
+        }\r
+\r
+        if (flowLiveType != null) {\r
+            flowLiveType = flowLiveType.toUpperCase();\r
+        }\r
+        if (instructionType != null) {\r
+            instructionType = instructionType.toUpperCase();\r
+        }\r
+\r
+        // convert String to FlowLiveType and check validity\r
+        TypedStoredFlowEntry.FlowLiveType inLiveType;\r
+        if (flowLiveType == null) {\r
+            inLiveType = null;\r
+        } else {\r
+            inLiveType = getFlowLiveType(flowLiveType);\r
+            if (inLiveType == null) {\r
+                error("Invalid flow live type [%s] error", flowLiveType);\r
+                return;\r
+            }\r
+        }\r
+        // convert String to InstructionType and check validity\r
+        Instruction.Type inInstructionType;\r
+        if (instructionType == null) {\r
+            inInstructionType = null;\r
+        } else {\r
+            inInstructionType = getInstructionType(instructionType);\r
+            if (inInstructionType == null) {\r
+                error("Invalid instruction type [%s] error", instructionType);\r
+                return;\r
+            }\r
+        }\r
+\r
+        if (showTopn != null) {\r
+            int topn = Integer.parseInt(showTopn);\r
+\r
+            if (topn <= 0) {\r
+                topn = 100; //default value\r
+            } else if (topn > 1000) {\r
+                topn = 1000; //max value\r
+            }\r
+\r
+            // print show topn head line with type\r
+            print("deviceId=%s, show TOPN=%s flows, live type=%s, instruction type=%s",\r
+                    deviceURI,\r
+                    Integer.toString(topn),\r
+                    flowLiveType == null ? "ALL" : flowLiveType,\r
+                    instructionType == null ? "ALL" : instructionType);\r
+            if (ingressPortNumber == null) {\r
+                Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap =\r
+                          flowStatsService.loadTopnByType(device, inLiveType, inInstructionType, topn);\r
+                // print all ports topn flows load for a given device\r
+                for (ConnectPoint cp : typedFlowLoadMap.keySet()) {\r
+                    printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));\r
+                }\r
+            } else {\r
+                List<TypedFlowEntryWithLoad> typedFlowLoad =\r
+                        flowStatsService.loadTopnByType(device, ingressPortNumber, inLiveType, inInstructionType, topn);\r
+                // print device/port topn flows load\r
+                ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);\r
+                printPortFlowsLoad(cp, typedFlowLoad);\r
+            }\r
+        } else if (showAll) { // is true?\r
+            // print show all head line with type\r
+            print("deviceId=%s, show ALL flows, live type=%s, instruction type=%s",\r
+                    deviceURI,\r
+                    flowLiveType == null ? "ALL" : flowLiveType,\r
+                    instructionType == null ? "ALL" : instructionType);\r
+            if (ingressPortNumber == null) {\r
+                Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap =\r
+                        flowStatsService.loadAllByType(device, inLiveType, inInstructionType);\r
+                // print all ports all flows load for a given device\r
+                for (ConnectPoint cp : typedFlowLoadMap.keySet()) {\r
+                    printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));\r
+                }\r
+            } else {\r
+                List<TypedFlowEntryWithLoad> typedFlowLoad =\r
+                        flowStatsService.loadAllByType(device, ingressPortNumber, inLiveType, inInstructionType);\r
+                // print device/port all flows load\r
+                ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);\r
+                printPortFlowsLoad(cp, typedFlowLoad);\r
+            }\r
+        } else { // if (showSummary == true) //always is true\r
+            // print show summary head line\r
+            print("deviceId=%s, show SUMMARY flows", deviceURI);\r
+            if (ingressPortNumber == null) {\r
+                Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryFlowLoadMap =\r
+                        flowStatsService.loadSummary(device);\r
+                // print all ports flow load summary for a given device\r
+                for (ConnectPoint cp : summaryFlowLoadMap.keySet()) {\r
+                    printPortSummaryLoad(cp, summaryFlowLoadMap.get(cp));\r
+                }\r
+            } else {\r
+                SummaryFlowEntryWithLoad summaryFlowLoad =\r
+                        flowStatsService.loadSummary(device, ingressPortNumber);\r
+                // print device/port flow load summary\r
+                ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);\r
+                printPortSummaryLoad(cp, summaryFlowLoad);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Extracts the port number portion of the ConnectPoint.\r
+     *\r
+     * @param deviceString string representing the device/port\r
+     * @return port number as a string, empty string if the port is not found\r
+     */\r
+    private String getPortNumber(String deviceString) {\r
+        if (deviceString == null) {\r
+            return "";\r
+        }\r
+\r
+        int slash = deviceString.indexOf('/');\r
+        if (slash <= 0) {\r
+            return ""; // return when no port number\r
+        }\r
+        return deviceString.substring(slash + 1, deviceString.length());\r
+    }\r
+\r
+    /**\r
+     * Extracts the device ID portion of the ConnectPoint.\r
+     *\r
+     * @param deviceString string representing the device/port\r
+     * @return device ID string\r
+     */\r
+    private String getDeviceId(String deviceString) {\r
+        if (deviceString == null) {\r
+            return "";\r
+        }\r
+\r
+        int slash = deviceString.indexOf('/');\r
+        if (slash <= 0) {\r
+            return deviceString; // return only included device ID\r
+        }\r
+        return deviceString.substring(0, slash);\r
+    }\r
+\r
+    /**\r
+     * converts string of flow live type to FloeLiveType enum.\r
+     *\r
+     * @param liveType string representing the flow live type\r
+     * @return TypedStoredFlowEntry.FlowLiveType\r
+     */\r
+    private TypedStoredFlowEntry.FlowLiveType getFlowLiveType(String liveType) {\r
+        String liveTypeUC = liveType.toUpperCase();\r
+\r
+        if (liveTypeUC.equals("IMMEDIATE")) {\r
+            return TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW;\r
+        } else if (liveTypeUC.equals("SHORT")) {\r
+            return TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW;\r
+        } else if (liveTypeUC.equals("MID")) {\r
+            return TypedStoredFlowEntry.FlowLiveType.MID_FLOW;\r
+        } else if (liveTypeUC.equals("LONG")) {\r
+            return TypedStoredFlowEntry.FlowLiveType.LONG_FLOW;\r
+        } else if (liveTypeUC.equals("UNKNOWN")) {\r
+            return TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW;\r
+        } else {\r
+            return null; // flow live type error\r
+        }\r
+    }\r
+\r
+    /**\r
+     * converts string of instruction type to Instruction type enum.\r
+     *\r
+     * @param instType string representing the instruction type\r
+     * @return Instruction.Type\r
+     */\r
+    private Instruction.Type getInstructionType(String instType) {\r
+        String instTypeUC = instType.toUpperCase();\r
+\r
+        if (instTypeUC.equals("DROP")) {\r
+            return Instruction.Type.DROP;\r
+        } else if (instTypeUC.equals("OUTPUT")) {\r
+            return Instruction.Type.OUTPUT;\r
+        } else if (instTypeUC.equals("GROUP")) {\r
+            return Instruction.Type.GROUP;\r
+        } else if (instTypeUC.equals("L0MODIFICATION")) {\r
+            return Instruction.Type.L0MODIFICATION;\r
+        } else if (instTypeUC.equals("L2MODIFICATION")) {\r
+            return Instruction.Type.L2MODIFICATION;\r
+        } else if (instTypeUC.equals("TABLE")) {\r
+            return Instruction.Type.TABLE;\r
+        } else if (instTypeUC.equals("L3MODIFICATION")) {\r
+            return Instruction.Type.L3MODIFICATION;\r
+        } else if (instTypeUC.equals("METADATA")) {\r
+            return Instruction.Type.METADATA;\r
+        } else {\r
+             return null; // instruction type error\r
+        }\r
+    }\r
+\r
+    private void printPortFlowsLoad(ConnectPoint cp, List<TypedFlowEntryWithLoad> typedFlowLoad) {\r
+       print("  deviceId/Port=%s/%s, %s flows", cp.elementId(), cp.port(), typedFlowLoad.size());\r
+        for (TypedFlowEntryWithLoad tfel: typedFlowLoad) {\r
+            TypedStoredFlowEntry tfe =  tfel.typedStoredFlowEntry();\r
+            print("    flowId=%s, state=%s, liveType=%s, life=%s -> %s",\r
+                    Long.toHexString(tfe.id().value()),\r
+                    tfe.state(),\r
+                    tfe.flowLiveType(),\r
+                    tfe.life(),\r
+                    tfel.load().isValid() ? tfel.load() : "Load{rate=0, NOT VALID}");\r
+        }\r
+    }\r
+\r
+    private void printPortSummaryLoad(ConnectPoint cp, SummaryFlowEntryWithLoad summaryFlowLoad) {\r
+        print("  deviceId/Port=%s/%s, Total=%s, Immediate=%s, Short=%s, Mid=%s, Long=%s, Unknown=%s",\r
+                cp.elementId(),\r
+                cp.port(),\r
+                summaryFlowLoad.totalLoad().isValid() ? summaryFlowLoad.totalLoad() : "Load{rate=0, NOT VALID}",\r
+                summaryFlowLoad.immediateLoad().isValid() ? summaryFlowLoad.immediateLoad() : "Load{rate=0, NOT VALID}",\r
+                summaryFlowLoad.shortLoad().isValid() ? summaryFlowLoad.shortLoad() : "Load{rate=0, NOT VALID}",\r
+                summaryFlowLoad.midLoad().isValid() ? summaryFlowLoad.midLoad() : "Load{rate=0, NOT VALID}",\r
+                summaryFlowLoad.longLoad().isValid() ? summaryFlowLoad.longLoad() : "Load{rate=0, NOT VALID}",\r
+                summaryFlowLoad.unknownLoad().isValid() ? summaryFlowLoad.unknownLoad() : "Load{rate=0, NOT VALID}");\r
+    }\r
+}\r
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java
new file mode 100644 (file)
index 0000000..eefb711
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cli.net;
+
+import com.google.common.collect.Sets;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceAdminService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.host.InterfaceIpAddress;
+
+import java.util.Set;
+
+/**
+ * Adds a new interface configuration.
+ */
+@Command(scope = "onos", name = "add-interface",
+        description = "Adds a new configured interface")
+public class InterfaceAddCommand extends AbstractShellCommand {
+
+    @Option(name = "-c", aliases = "--connectPoint",
+            description = "Device port that the interface is associated with",
+            required = true, multiValued = false)
+    private String connectPoint = null;
+
+    @Option(name = "-m", aliases = "--mac",
+            description = "MAC address of the interface",
+            required = true, multiValued = false)
+    private String mac = null;
+
+    @Option(name = "-i", aliases = "--ip",
+            description = "IP address configured on the interface\n" +
+            "(e.g. 10.0.1.1/24). Can be specified multiple times.",
+            required = false, multiValued = true)
+    private String[] ips = null;
+
+    @Option(name = "-v", aliases = "--vlan",
+            description = "VLAN configured on the interface",
+            required = false, multiValued = false)
+    private String vlan = null;
+
+    @Override
+    protected void execute() {
+        InterfaceAdminService interfaceService = get(InterfaceAdminService.class);
+
+        Set<InterfaceIpAddress> ipAddresses = Sets.newHashSet();
+        if (ips != null) {
+            for (String strIp : ips) {
+                ipAddresses.add(InterfaceIpAddress.valueOf(strIp));
+            }
+        }
+
+        VlanId vlanId = vlan == null ? VlanId.NONE : VlanId.vlanId(Short.parseShort(vlan));
+
+        Interface intf = new Interface(ConnectPoint.deviceConnectPoint(connectPoint),
+                ipAddresses, MacAddress.valueOf(mac), vlanId);
+
+        interfaceService.add(intf);
+    }
+
+}
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceRemoveCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceRemoveCommand.java
new file mode 100644 (file)
index 0000000..941a65d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.intf.InterfaceAdminService;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Removes an interface configuration.
+ */
+@Command(scope = "onos", name = "remove-interface",
+        description = "Removes a configured interface")
+public class InterfaceRemoveCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "connectPoint",
+            description = "Connect point of the interface",
+            required = true, multiValued = false)
+    private String connectPoint = null;
+
+    @Argument(index = 1, name = "vlan",
+            description = "Interface vlan",
+            required = true, multiValued = false)
+    private String vlan = null;
+
+    @Override
+    protected void execute() {
+        InterfaceAdminService interfaceService = get(InterfaceAdminService.class);
+
+        interfaceService.remove(ConnectPoint.deviceConnectPoint(connectPoint),
+                VlanId.vlanId(Short.parseShort(vlan)));
+    }
+
+}
index ff66b80..6b7d933 100644 (file)
@@ -17,7 +17,7 @@ package org.onosproject.cli.net;
 
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketProcessorEntry;
 import org.onosproject.net.packet.PacketService;
 
 import static org.onosproject.net.packet.PacketProcessor.ADVISOR_MAX;
@@ -30,7 +30,7 @@ import static org.onosproject.net.packet.PacketProcessor.DIRECTOR_MAX;
         description = "Lists packet processors")
 public class PacketProcessorsListCommand extends AbstractShellCommand {
 
-    private static final String FMT = "priority=%s, class=%s";
+    private static final String FMT = "priority=%s, class=%s, packets=%d, avgNanos=%d";
 
     @Override
     protected void execute() {
@@ -43,8 +43,10 @@ public class PacketProcessorsListCommand extends AbstractShellCommand {
         }
     }
 
-    private void print(int priority, PacketProcessor processor) {
-        print(FMT, priorityFormat(priority), processor.getClass().getName());
+    private void print(PacketProcessorEntry entry) {
+        print(FMT, priorityFormat(entry.priority()),
+              entry.processor().getClass().getName(),
+              entry.invocations(), entry.averageNanos());
     }
 
     private String priorityFormat(int priority) {
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java
new file mode 100644 (file)
index 0000000..e0cd72f
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cli.net;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.Comparators;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TableStatisticsEntry;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Lists port statistic of all ports in the system.
+ */
+@Command(scope = "onos", name = "tablestats",
+        description = "Lists statistics of all tables in the device")
+public class TableStatisticsCommand extends AbstractShellCommand {
+
+    @Option(name = "-t", aliases = "--table", description = "Show human readable table format for statistics",
+            required = false, multiValued = false)
+    private boolean table = false;
+
+    @Argument(index = 0, name = "uri", description = "Device ID",
+            required = false, multiValued = false)
+    String uri = null;
+
+    private static final String FORMAT =
+            "   table=%s, active=%s, lookedup=%s, matched=%s";
+
+    @Override
+    protected void execute() {
+        FlowRuleService flowService = get(FlowRuleService.class);
+        DeviceService deviceService = get(DeviceService.class);
+
+        SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats =
+                getSortedTableStats(deviceService, flowService);
+
+        if (outputJson()) {
+            print("%s", json(deviceTableStats.keySet(), deviceTableStats));
+        } else {
+            deviceTableStats.forEach((device, tableStats) -> printTableStats(device, tableStats));
+        }
+    }
+
+    /**
+     * Produces a JSON array of table statistics grouped by the each device.
+     *
+     * @param devices     collection of devices
+     * @param deviceTableStats collection of table statistics per each device
+     * @return JSON array
+     */
+    private JsonNode json(Iterable<Device> devices,
+                          Map<Device, List<TableStatisticsEntry>> deviceTableStats) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
+        for (Device device : devices) {
+            result.add(json(mapper, device, deviceTableStats.get(device)));
+        }
+        return result;
+    }
+
+    // Produces JSON object with the table statistics of the given device.
+    private ObjectNode json(ObjectMapper mapper,
+                            Device device, List<TableStatisticsEntry> tableStats) {
+        ObjectNode result = mapper.createObjectNode();
+        ArrayNode array = mapper.createArrayNode();
+
+        tableStats.forEach(tableStat -> array.add(jsonForEntity(tableStat, TableStatisticsEntry.class)));
+
+        result.put("device", device.id().toString())
+                .put("tableCount", tableStats.size())
+                .set("tables", array);
+        return result;
+    }
+
+    /**
+     * Prints flow table statistics.
+     *
+     * @param d     the device
+     * @param tableStats the set of flow table statistics for that device
+     */
+    protected void printTableStats(Device d,
+                                   List<TableStatisticsEntry> tableStats) {
+        boolean empty = tableStats == null || tableStats.isEmpty();
+        print("deviceId=%s, tableCount=%d", d.id(), empty ? 0 : tableStats.size());
+        if (!empty) {
+            for (TableStatisticsEntry t : tableStats) {
+                print(FORMAT, t.tableId(), t.activeFlowEntries(),
+                      t.packetsLookedup(), t.packetsMatched());
+            }
+        }
+    }
+
+    /**
+     * Returns the list of table statistics sorted using the device ID URIs and table IDs.
+     *
+     * @param deviceService device service
+     * @param flowService flow rule service
+     * @return sorted table statistics list
+     */
+    protected SortedMap<Device, List<TableStatisticsEntry>> getSortedTableStats(DeviceService deviceService,
+                                                          FlowRuleService flowService) {
+        SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats = new TreeMap<>(Comparators.ELEMENT_COMPARATOR);
+        List<TableStatisticsEntry> tableStatsList;
+        Iterable<Device> devices = uri == null ? deviceService.getDevices() :
+                Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri)));
+        for (Device d : devices) {
+            tableStatsList = newArrayList(flowService.getFlowTableStatistics(d.id()));
+            tableStatsList.sort((p1, p2) -> Integer.valueOf(p1.tableId()).compareTo(Integer.valueOf(p2.tableId())));
+            deviceTableStats.put(d, tableStatsList);
+        }
+        return deviceTableStats;
+    }
+
+}
index 459ffa9..28461e2 100644 (file)
                 <ref component-id="deviceIdCompleter"/>
             </completers>
         </command>
+        <command>
+            <action class="org.onosproject.cli.net.DeviceControllersCommand"/>
+            <completers>
+                <ref component-id="deviceIdCompleter"/>
+            </completers>
+        </command>
+        <command>
+            <action class="org.onosproject.cli.net.DeviceSetControllersCommand"/>
+            <completers>
+                <ref component-id="deviceIdCompleter"/>
+            </completers>
+        </command>
         <command>
             <action class="org.onosproject.cli.net.DeviceRemoveCommand"/>
             <completers>
                 <ref component-id="connectPointCompleter"/>
             </completers>
         </command>
+        <command>
+            <action class="org.onosproject.cli.net.GetFlowStatistics"/>
+            <completers>
+                <ref component-id="deviceIdCompleter"/>
+            </completers>
+        </command>
         <command>
             <action class="org.onosproject.cli.net.AddMultiPointToSinglePointIntentCommand"/>
             <completers>
         <command>
             <action class="org.onosproject.cli.net.InterfacesListCommand"/>
         </command>
-
+        <command>
+            <action class="org.onosproject.cli.net.InterfaceAddCommand"/>
+            <optional-completers>
+                <entry key="-c" value-ref="connectPointCompleter"/>
+                <entry key="--connectPoint" value-ref="connectPointCompleter"/>
+            </optional-completers>
+        </command>
+        <command>
+            <action class="org.onosproject.cli.net.InterfaceRemoveCommand"/>
+            <completers>
+                <ref component-id="connectPointCompleter"/>
+            </completers>
+        </command>
         <command>
             <action class="org.onosproject.cli.net.GroupsListCommand"/>
         </command>
             <action class="org.onosproject.cli.net.DevicePortStatsCommand"/>
         </command>
 
+        <command>
+            <action class="org.onosproject.cli.net.TableStatisticsCommand"/>
+        </command>
+
         <command>
             <action class="org.onosproject.cli.net.FlowsListCommand"/>
             <completers>
index a311002..408a893 100644 (file)
@@ -62,6 +62,16 @@ public interface ComponentConfigService {
      */
     void setProperty(String componentName, String name, String value);
 
+    /**
+     * Presets the value of the specified configuration property, regardless
+     * of the component's state.
+     *
+     * @param componentName component name
+     * @param name          property name
+     * @param value         new property value
+     */
+    void preSetProperty(String componentName, String name, String value);
+
     /**
      * Clears the value of the specified configuration property thus making
      * the property take on its default value.
@@ -72,3 +82,4 @@ public interface ComponentConfigService {
     void unsetProperty(String componentName, String name);
 
 }
+
index 4949bc4..8c5fb79 100644 (file)
@@ -25,8 +25,10 @@ package org.onosproject.net;
  */
 public final class AnnotationKeys {
 
+
     // Prohibit instantiation
-    private AnnotationKeys() {}
+    private AnnotationKeys() {
+    }
 
     /**
      * Annotation key for instance name.
@@ -124,13 +126,23 @@ public final class AnnotationKeys {
      */
     public static final String OWNER = "owner";
 
+    /**
+     * Annotation key for the channel id.
+     */
+    public static final String CHANNEL_ID = "channelId";
+
+    /**
+     * Annotation key for the management address.
+     */
+    public static final String MANAGEMENT_ADDRESS = "managementAddress";
+
     /**
      * Returns the value annotated object for the specified annotation key.
      * The annotated value is expected to be String that can be parsed as double.
      * If parsing fails, the returned value will be 1.0.
      *
      * @param annotated annotated object whose annotated value is obtained
-     * @param key key of annotation
+     * @param key       key of annotation
      * @return double value of annotated object for the specified key
      */
     public static double getAnnotatedValue(Annotated annotated, String key) {
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/DefaultDisjointPath.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/DefaultDisjointPath.java
new file mode 100644 (file)
index 0000000..4895964
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net;
+
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.List;
+import java.util.Objects;
+import static com.google.common.collect.ImmutableSet.of;
+
+/**
+ * Default implementation of a network disjoint path pair.
+ */
+public class DefaultDisjointPath extends DefaultPath implements DisjointPath {
+
+    private final DefaultPath path1;
+    private final DefaultPath path2;
+
+    boolean usingPath1 = true;
+
+    /**
+     * Creates a disjoint path pair from two default paths.
+     *
+     * @param providerId provider identity
+     * @param path1      primary path
+     * @param path2      backup path
+     */
+    public DefaultDisjointPath(ProviderId providerId, DefaultPath path1, DefaultPath path2) {
+        super(providerId, path1.links(), path1.cost() + path2.cost());
+        this.path1 = path1;
+        this.path2 = path2;
+    }
+
+    @Override
+    public List<Link> links() {
+        if (usingPath1) {
+            return path1.links();
+        } else {
+            return path2.links();
+        }
+    }
+
+    @Override
+    public double cost() {
+        if (usingPath1) {
+            return path1.cost();
+        }
+        return path2.cost();
+    }
+
+    @Override
+    public Path primary() {
+        return path1;
+    }
+
+    @Override
+    public Path backup() {
+        return path2;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(of(path1, path2), src(), dst());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultDisjointPath) {
+            final DefaultDisjointPath other = (DefaultDisjointPath) obj;
+            return Objects.equals(this.path1, other.path1) && Objects.equals(this.path2, other.path2);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean useBackup() {
+        if (path2 == null || path2.links() == null) {
+            return false;
+        }
+        usingPath1 = !usingPath1;
+        return true;
+    }
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/DisjointPath.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/DisjointPath.java
new file mode 100644 (file)
index 0000000..3d54cbf
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net;
+
+
+/**
+ * Representation of a contiguous directed path in a network. Path comprises
+ * of a sequence of links, where adjacent links must share the same device,
+ * meaning that destination of the source of one link must coincide with the
+ * destination of the previous link.
+ */
+public interface DisjointPath extends Path {
+
+    /**
+     * Uses backup path.
+     *
+     * @return boolean corresponding to whether request to use
+     *          backup was successful.
+     */
+    boolean useBackup();
+
+    /**
+     * Gets primary path.
+     *
+     * @return primary path
+     */
+    Path primary();
+
+    /**
+     * Gets secondary path.
+     *
+     * @return secondary path
+     */
+    Path backup();
+}
index bb8a788..cd7cb97 100644 (file)
  */
 package org.onosproject.net.behaviour;
 
+import org.onosproject.net.driver.HandlerBehaviour;
+
 import java.util.List;
 
 /**
  * Device behaviour to obtain and set controllers at the device.
  */
-public interface ControllerConfig {
+public interface ControllerConfig extends HandlerBehaviour {
 
     //TODO: add other controller parameters as needed.
 
     /**
      * Obtain the list of controller which are currently configured.
+     *
      * @return a list for controller descriptions
      */
     List<ControllerInfo> getControllers();
 
     /**
      * Set a list of controllers on a device.
+     *
      * @param controllers a list of controller descriptions
      */
     void setControllers(List<ControllerInfo> controllers);
index 9ff808a..ded3b3a 100644 (file)
  */
 package org.onosproject.net.behaviour;
 
+import com.google.common.base.Preconditions;
 import org.onlab.packet.IpAddress;
 
+import java.util.Objects;
+
 /**
  * Represents information for a device to connect to a controller.
  */
 public class ControllerInfo {
 
-    public final IpAddress ip;
-    public final int tcpPort;
+    private IpAddress ip = IpAddress.valueOf("0.0.0.0");
+    private int port = 6653;
+    private String type = "error";
 
     /**
      * Information for contacting the controller.
      *
-     * @param ip the ip address
-     * @param tcpPort the tcp port
+     * @param ip   the ip address
+     * @param port the tcp port
      */
-    public ControllerInfo(IpAddress ip, int tcpPort) {
+    public ControllerInfo(IpAddress ip, int port, String type) {
         this.ip = ip;
-        this.tcpPort = tcpPort;
+        this.port = port;
+        this.type = type;
+    }
+
+    /**
+     * Information for contacting the controller, if some information
+     * is not contained in the target string because it's optional
+     * it's leaved as in the field declaration (default values).
+     *
+     * @param target column returned from ovsdb query
+     */
+    public ControllerInfo(String target) {
+        String[] data = target.split(":");
+        this.type = data[0];
+        Preconditions.checkArgument(!data[0].contains("unix"),
+                                    "Unable to create controller info " +
+                                            "from {} because it's based " +
+                                            "on unix sockets", target);
+        if (data[0].startsWith("p")) {
+            if (data.length >= 2) {
+                this.port = Integer.parseInt(data[1]);
+            }
+            if (data.length == 3) {
+                this.ip = IpAddress.valueOf(data[2]);
+            }
+        } else {
+            this.ip = IpAddress.valueOf(data[1]);
+            if (data.length == 3) {
+                this.port = Integer.parseInt(data[2]);
+            }
+        }
+    }
+
+    /**
+     * Exposes the ip address of the controller.
+     *
+     * @return IpAddress ip address
+     */
+    public IpAddress ip() {
+        return ip;
+    }
+
+    /**
+     * Exposes the tcp port of the controller.
+     *
+     * @return int tcp port
+     */
+    public int port() {
+        return port;
+    }
+
+    /**
+     * Exposes the type of the controller connection.
+     *
+     * @return String type
+     */
+    public String type() {
+        return type;
+    }
+
+    public String target() {
+        if (type.startsWith("p")) {
+            return type + ":" + port + ":" + ip;
+        } else {
+            return type + ":" + ip + ":" + port;
+        }
+    }
+
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ip, port, type);
+    }
+
+    @Override
+    public boolean equals(Object toBeCompared) {
+        if (toBeCompared instanceof ControllerInfo) {
+            ControllerInfo controllerInfo = (ControllerInfo) toBeCompared;
+            if (controllerInfo.type().equals(this.type)
+                    && controllerInfo.ip().equals(this.ip())
+                    && controllerInfo.port() == this.port) {
+                return true;
+            }
+        }
+        return false;
     }
 }
index c1eed98..8eb69a4 100644 (file)
@@ -41,27 +41,27 @@ public interface NetworkConfigService
     /**
      * Returns the subject factory with the specified key.
      *
-     * @param subjectKey subject class key
+     * @param subjectClassKey subject class key
      * @return subject class
      */
-    SubjectFactory getSubjectFactory(String subjectKey);
+    SubjectFactory getSubjectFactory(String subjectClassKey);
 
     /**
      * Returns the subject factory for the specified class.
      *
      * @param subjectClass subject class
-     * @return subject class key
+     * @return subject class factory
      */
     SubjectFactory getSubjectFactory(Class subjectClass);
 
     /**
      * Returns the configuration class with the specified key.
      *
-     * @param subjectKey subject class key
-     * @param configKey  subject class name
+     * @param subjectClassKey subject class key
+     * @param configKey       subject class name
      * @return subject class
      */
-    Class<? extends Config> getConfigClass(String subjectKey, String configKey);
+    Class<? extends Config> getConfigClass(String subjectClassKey, String configKey);
 
     /**
      * Returns the set of subjects for which some configuration is available.
index cd2db34..f992d72 100644 (file)
@@ -28,7 +28,7 @@ import com.google.common.annotations.Beta;
 public abstract class SubjectFactory<S> {
 
     private final Class<S> subjectClass;
-    private final String subjectKey;
+    private final String subjectClassKey;
 
     /**
      * Creates a new configuration factory for the specified class of subjects
@@ -36,12 +36,12 @@ public abstract class SubjectFactory<S> {
      * subject and configuration class keys are used merely as keys for use in
      * composite JSON trees.
      *
-     * @param subjectClass subject class
-     * @param subjectKey   subject class key
+     * @param subjectClass    subject class
+     * @param subjectClassKey subject class key
      */
-    protected SubjectFactory(Class<S> subjectClass, String subjectKey) {
+    protected SubjectFactory(Class<S> subjectClass, String subjectClassKey) {
         this.subjectClass = subjectClass;
-        this.subjectKey = subjectKey;
+        this.subjectClassKey = subjectClassKey;
     }
 
     /**
@@ -60,8 +60,20 @@ public abstract class SubjectFactory<S> {
      *
      * @return configuration key
      */
-    public String subjectKey() {
-        return subjectKey;
+    public String subjectClassKey() {
+        return subjectClassKey;
+    }
+
+    /**
+     * Returns the unique key of the specified configuration subject.
+     * This is primarily aimed for use in composite JSON trees in external
+     * representations and has no bearing on the internal behaviours.
+     *
+     * @param subject specific subject
+     * @return subject key
+     */
+    public String subjectKey(S subject) {
+        return subject.toString();
     }
 
     /**
index fd8bfa3..afde9a9 100644 (file)
@@ -25,6 +25,7 @@ public class BasicDeviceConfig extends BasicElementConfig<DeviceId> {
 
     public static final String TYPE = "type";
     public static final String DRIVER = "driver";
+    public static final String MANAGEMENT_ADDRESS = "managementAddress";
 
     /**
      * Returns the device type.
@@ -64,6 +65,25 @@ public class BasicDeviceConfig extends BasicElementConfig<DeviceId> {
         return (BasicElementConfig) setOrClear(DRIVER, driverName);
     }
 
+    /**
+     * Returns the device management ip (ip:port).
+     *
+     * @return device management address (ip:port) or null if not set
+     */
+    public String managementAddress() {
+        return get(MANAGEMENT_ADDRESS, null);
+    }
+
+    /**
+     * Sets the driver name.
+     *
+     * @param managementAddress new device management address (ip:port); null to clear
+     * @return self
+     */
+    public BasicElementConfig managementAddress(String managementAddress) {
+        return (BasicElementConfig) setOrClear(MANAGEMENT_ADDRESS, managementAddress);
+    }
+
     // TODO: device port meta-data to be configured via BasicPortsConfig
     // TODO: device credentials/keys
 
index 884f2e2..311566b 100644 (file)
@@ -43,6 +43,10 @@ public final class SubjectFactories {
                 public ApplicationId createSubject(String key) {
                     return coreService.registerApplication(key);
                 }
+                @Override
+                public String subjectKey(ApplicationId subject) {
+                    return subject.name();
+                }
             };
 
     public static final SubjectFactory<DeviceId> DEVICE_SUBJECT_FACTORY =
@@ -59,6 +63,10 @@ public final class SubjectFactories {
                 public ConnectPoint createSubject(String key) {
                     return ConnectPoint.deviceConnectPoint(key);
                 }
+                @Override
+                public String subjectKey(ConnectPoint subject) {
+                    return key(subject);
+                }
             };
 
     public static final SubjectFactory<HostId> HOST_SUBJECT_FACTORY =
@@ -78,6 +86,10 @@ public final class SubjectFactories {
                     return LinkKey.linkKey(ConnectPoint.deviceConnectPoint(cps[0]),
                                            ConnectPoint.deviceConnectPoint(cps[1]));
                 }
+                @Override
+                public String subjectKey(LinkKey subject) {
+                    return key(subject.src()) + "-" + key(subject.dst());
+                }
             };
 
     /**
@@ -90,4 +102,8 @@ public final class SubjectFactories {
         coreService = service;
     }
 
+    private static String key(ConnectPoint subject) {
+        return subject.deviceId() + "/" + subject.port();
+    }
+
 }
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java
new file mode 100644 (file)
index 0000000..929b285
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flow;
+
+import org.onosproject.net.DeviceId;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of table statistics entry interface.
+ */
+public final class DefaultTableStatisticsEntry implements TableStatisticsEntry {
+
+    private final DeviceId deviceId;
+    private final int tableId;
+    private final long activeFlowEntries;
+    private final long packetsLookedupCount;
+    private final long packetsMatchedCount;
+
+    /**
+     * Default table statistics constructor.
+     *
+     * @param deviceId device identifier
+     * @param tableId table identifier
+     * @param activeFlowEntries number of active flow entries in the table
+     * @param packetsLookedupCount number of packets looked up in table
+     * @param packetsMatchedCount number of packets that hit table
+     */
+    public DefaultTableStatisticsEntry(DeviceId deviceId,
+                                  int  tableId,
+                                  long activeFlowEntries,
+                                  long packetsLookedupCount,
+                                  long packetsMatchedCount) {
+        this.deviceId = checkNotNull(deviceId);
+        this.tableId = tableId;
+        this.activeFlowEntries = activeFlowEntries;
+        this.packetsLookedupCount = packetsLookedupCount;
+        this.packetsMatchedCount = packetsMatchedCount;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("device: " + deviceId + ", ");
+
+        sb.append("tableId: " + this.tableId + ", ");
+        sb.append("activeEntries: " + this.activeFlowEntries + ", ");
+        sb.append("packetsLookedUp: " + this.packetsLookedupCount + ", ");
+        sb.append("packetsMatched: " + this.packetsMatchedCount);
+
+        return sb.toString();
+    }
+
+    @Override
+    public int tableId() {
+        return tableId;
+    }
+
+    @Override
+    public long activeFlowEntries() {
+        return activeFlowEntries;
+    }
+
+    @Override
+    public long packetsLookedup() {
+        return packetsLookedupCount;
+    }
+
+    @Override
+    public long packetsMatched() {
+        return packetsMatchedCount;
+    }
+
+    @Override
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+}
index f88c6bc..4416456 100644 (file)
@@ -23,22 +23,26 @@ import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.VlanId;
-import org.onosproject.net.IndexedLambda;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.criteria.Criterion;
 
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TreeSet;
 
 /**
  * Default traffic selector implementation.
  */
 public final class DefaultTrafficSelector implements TrafficSelector {
 
+    private static final Comparator<? super Criterion> TYPE_COMPARATOR =
+            (c1, c2) -> c1.type().compareTo(c2.type());
+
     private final Set<Criterion> criteria;
 
     private static final TrafficSelector EMPTY
@@ -50,7 +54,9 @@ public final class DefaultTrafficSelector implements TrafficSelector {
      * @param criteria criteria
      */
     private DefaultTrafficSelector(Set<Criterion> criteria) {
-        this.criteria = ImmutableSet.copyOf(criteria);
+        TreeSet<Criterion> elements = new TreeSet<>(TYPE_COMPARATOR);
+        elements.addAll(criteria);
+        this.criteria = ImmutableSet.copyOf(elements);
     }
 
     @Override
@@ -345,18 +351,6 @@ public final class DefaultTrafficSelector implements TrafficSelector {
             return add(Criteria.matchIPv6ExthdrFlags(exthdrFlags));
         }
 
-        @Deprecated
-        @Override
-        public Builder matchLambda(short lambda) {
-            return add(Criteria.matchLambda(new IndexedLambda(lambda)));
-        }
-
-        @Deprecated
-        @Override
-        public Builder matchOpticalSignalType(short signalType) {
-            return add(Criteria.matchOpticalSignalType(signalType));
-        }
-
         @Override
         public TrafficSelector build() {
             return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values()));
index 5d18a9a..a628725 100644 (file)
@@ -31,7 +31,6 @@ import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.meter.MeterId;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -51,7 +50,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
     private final boolean hasClear;
 
     private static final DefaultTrafficTreatment EMPTY
-            = new DefaultTrafficTreatment(Collections.emptyList());
+            = new DefaultTrafficTreatment(ImmutableList.of(Instructions.createNoAction()));
     private final Instructions.MeterInstruction meter;
 
     /**
@@ -212,8 +211,6 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
 
         List<Instruction> current = immediate;
 
-
-
         // Creates a new builder
         private Builder() {
         }
@@ -224,7 +221,10 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
             treatment.deferred().forEach(i -> add(i));
 
             immediate();
-            treatment.immediate().forEach(i -> add(i));
+            treatment.immediate().stream()
+                    // NOACTION will get re-added if there are no other actions
+                    .filter(i -> i.type() != Instruction.Type.NOACTION)
+                    .forEach(i -> add(i));
 
             clear = treatment.clearedDeferred();
         }
@@ -234,6 +234,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
 
             switch (instruction.type()) {
                 case DROP:
+                case NOACTION:
                 case OUTPUT:
                 case GROUP:
                 case L0MODIFICATION:
@@ -250,6 +251,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
                     break;
                 case METER:
                     meter = (Instructions.MeterInstruction) instruction;
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown instruction type: " +
                                                                instruction.type());
@@ -258,9 +260,23 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
             return this;
         }
 
+        /**
+         * Add a NOACTION when DROP instruction is explicitly specified.
+         *
+         * @return the traffic treatment builder
+         */
         @Override
         public Builder drop() {
-            return add(Instructions.createDrop());
+            return add(Instructions.createNoAction());
+        }
+
+        /**
+         * Add a NOACTION when no instruction is specified.
+         *
+         * @return the traffic treatment builder
+         */
+        private Builder noAction() {
+            return add(Instructions.createNoAction());
         }
 
         @Override
@@ -379,11 +395,6 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
             return add(Instructions.pushVlan());
         }
 
-        @Override
-        public Builder transition(FlowRule.Type type) {
-            return add(Instructions.transition(type.ordinal()));
-        }
-
         @Override
         public Builder transition(Integer tableId) {
             return add(Instructions.transition(tableId));
@@ -463,14 +474,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
 
         @Override
         public TrafficTreatment build() {
-            //Don't add DROP instruction by default when instruction
-            //set is empty. This will be handled in DefaultSingleTablePipeline
-            //driver.
-
-            //if (deferred.size() == 0 && immediate.size() == 0
-            //        && table == null && !clear) {
-            //    drop();
-            //}
+            if (deferred.size() == 0 && immediate.size() == 0
+                    && table == null && !clear) {
+                immediate();
+                noAction();
+            }
             return new DefaultTrafficTreatment(deferred, immediate, table, clear, meta, meter);
         }
 
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTypedFlowEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTypedFlowEntry.java
new file mode 100644 (file)
index 0000000..afceb14
--- /dev/null
@@ -0,0 +1,122 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.net.flow;\r
+\r
+import static com.google.common.base.MoreObjects.toStringHelper;\r
+\r
+/**\r
+ * Default flow entry class with FlowLiveType value, IMMEDIATE_FLOW, SHORT_FLOW, MID_FLOW, LONG_FLOW.\r
+ */\r
+public class DefaultTypedFlowEntry extends DefaultFlowEntry\r
+    implements TypedStoredFlowEntry {\r
+    private FlowLiveType liveType;\r
+\r
+    /**\r
+     * Creates a typed flow entry from flow rule and its statistics, with default flow live type(IMMEDIATE_FLOW).\r
+     *\r
+     * @param rule the flow rule\r
+     * @param state the flow state\r
+     * @param life the flow duration since creation\r
+     * @param packets the flow packets count\r
+     * @param bytes the flow bytes count\r
+     *\r
+     */\r
+    public DefaultTypedFlowEntry(FlowRule rule, FlowEntryState state,\r
+                            long life, long packets, long bytes) {\r
+        super(rule, state, life, packets, bytes);\r
+        this.liveType = FlowLiveType.IMMEDIATE_FLOW;\r
+    }\r
+\r
+    /**\r
+     * Creates a typed flow entry from flow rule,  with default flow live type(IMMEDIATE_FLOW).\r
+     *\r
+     * @param rule the flow rule\r
+     *\r
+     */\r
+    public DefaultTypedFlowEntry(FlowRule rule) {\r
+        super(rule);\r
+        this.liveType = FlowLiveType.IMMEDIATE_FLOW;\r
+    }\r
+\r
+    /**\r
+     * Creates a typed flow entry from flow entry,  with default flow live type(IMMEDIATE_FLOW).\r
+     *\r
+     * @param fe the flow entry\r
+     *\r
+     */\r
+    public DefaultTypedFlowEntry(FlowEntry fe) {\r
+        super(fe, fe.state(), fe.life(), fe.packets(), fe.bytes());\r
+        this.liveType = FlowLiveType.IMMEDIATE_FLOW;\r
+    }\r
+\r
+    /**\r
+     * Creates a typed flow entry from flow rule and flow live type.\r
+     *\r
+     * @param rule the flow rule\r
+     * @param liveType the flow live type\r
+     *\r
+     */\r
+    public DefaultTypedFlowEntry(FlowRule rule, FlowLiveType liveType) {\r
+        super(rule);\r
+        this.liveType = liveType;\r
+    }\r
+\r
+    /**\r
+     * Creates a typed flow entry from flow entry and flow live type.\r
+     *\r
+     * @param fe the flow rule\r
+     * @param liveType the flow live type\r
+     *\r
+     */\r
+    public DefaultTypedFlowEntry(FlowEntry fe,  FlowLiveType liveType) {\r
+        super(fe, fe.state(), fe.life(), fe.packets(), fe.bytes());\r
+        this.liveType = liveType;\r
+    }\r
+\r
+    /**\r
+     * Creates a typed flow entry from flow rule, error code and flow live type.\r
+     *\r
+     * @param rule the flow rule\r
+     * @param errType the flow error type\r
+     * @param errCode the flow error code\r
+     * @param liveType the flow live type\r
+     *\r
+     */\r
+    public DefaultTypedFlowEntry(FlowRule rule, int errType, int errCode, FlowLiveType liveType) {\r
+        super(rule, errType, errCode);\r
+        this.liveType = liveType;\r
+    }\r
+\r
+    @Override\r
+    public FlowLiveType flowLiveType() {\r
+        return this.liveType;\r
+    }\r
+\r
+    @Override\r
+    public void setFlowLiveType(FlowLiveType liveType) {\r
+        this.liveType = liveType;\r
+    }\r
+\r
+    @Override\r
+    public String toString() {\r
+        return toStringHelper(this)\r
+                .add("entry", super.toString())\r
+                .add("type", liveType)\r
+                .toString();\r
+    }\r
+}\r
+\r
index a487cbc..35d45fb 100644 (file)
@@ -28,43 +28,6 @@ public interface FlowRule {
     int MAX_TIMEOUT = 60;
     int MIN_PRIORITY = 0;
 
-    /**
-     * The FlowRule type is used to determine in which table the flow rule needs
-     * to be put for multi-table support switch. For single table switch,
-     * Default is used.
-     *
-     * @deprecated in Cardinal Release
-     */
-    @Deprecated
-    enum Type {
-        /*
-         * Default type - used in flow rule for single table switch NOTE: this
-         * setting should not be used as Table 0 in a multi-table pipeline
-         */
-        DEFAULT,
-        /* Used in flow entry for IP table */
-        IP,
-        /* Used in flow entry for MPLS table */
-        MPLS,
-        /* Used in flow entry for ACL table */
-        ACL,
-
-        /* VLAN-to-MPLS table */
-        VLAN_MPLS,
-
-        /* VLAN table */
-        VLAN,
-
-        /* Ethtype table */
-        ETHER,
-
-        /* Class of Service table */
-        COS,
-
-        /* Table 0 in a multi-table pipeline */
-        FIRST,
-    }
-
     /**
      * Returns the ID of this flow.
      *
index 8a36a92..aefa96b 100644 (file)
@@ -15,6 +15,8 @@
  */
 package org.onosproject.net.flow;
 
+import java.util.List;
+
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.provider.ProviderService;
 
@@ -40,6 +42,24 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide
      */
     void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
 
+    /**
+     * Pushes the collection of flow entries currently applied on the given
+     * device without flowMissing process.
+     *
+     * @param deviceId device identifier
+     * @param flowEntries collection of flow rules
+     */
+    void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries);
+
+    /**
+     * Pushes the collection of table statistics entries currently extracted
+     * from the given device.
+     *
+     * @param deviceId device identifier
+     * @param tableStatsEntries collection of flow table statistics entries
+     */
+    void pushTableStatistics(DeviceId deviceId, List<TableStatisticsEntry> tableStatsEntries);
+
     /**
      * Indicates to the core that the requested batch operation has
      * been completed.
@@ -48,5 +68,4 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide
      * @param operation the resulting outcome of the operation
      */
     void batchOperationCompleted(long batchId, CompletedBatchOperation operation);
-
 }
index d4f959c..ee8d5a9 100644 (file)
@@ -104,4 +104,11 @@ public interface FlowRuleService
      */
     void apply(FlowRuleOperations ops);
 
+    /**
+     * Returns the collection of flow table statistics of the specified device.
+     *
+     * @param deviceId device identifier
+     * @return collection of flow table statistics
+     */
+    Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId);
 }
index cece989..d81c73c 100644 (file)
@@ -15,6 +15,8 @@
  */
 package org.onosproject.net.flow;
 
+import java.util.List;
+
 import org.onosproject.net.DeviceId;
 import org.onosproject.store.Store;
 
@@ -93,4 +95,23 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe
      * @return flow_removed event, or null if nothing removed
      */
     FlowRuleEvent removeFlowRule(FlowEntry rule);
+
+    /**
+     * Updates the flow table statistics of the specified device using
+     * the given statistics.
+     *
+     * @param deviceId    device identifier
+     * @param tableStats   list of table statistics
+     * @return ready to send event describing what occurred;
+     */
+    FlowRuleEvent updateTableStatistics(DeviceId deviceId,
+                                        List<TableStatisticsEntry> tableStats);
+
+    /**
+     * Returns the flow table statistics associated with a device.
+     *
+     * @param deviceId the device ID
+     * @return the flow table statistics
+     */
+    Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId);
 }
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java
new file mode 100644 (file)
index 0000000..563f31c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flow;
+
+import org.onosproject.net.DeviceId;
+
+/**
+ * Interface for flow table statistics of a device.
+ */
+public interface TableStatisticsEntry {
+
+    /**
+     * Returns the device Id.
+     *
+     * @return device id
+     */
+    DeviceId  deviceId();
+
+    /**
+     * Returns the table number.
+     *
+     * @return table number
+     */
+    int  tableId();
+
+    /**
+     * Returns the number of active flow entries in this table.
+     *
+     * @return the number of active flow entries
+     */
+    long activeFlowEntries();
+
+    /**
+     * Returns the number of packets looked up in the table.
+     *
+     * @return the number of packets looked up in the table
+     */
+    long packetsLookedup();
+
+    /**
+     * Returns the number of packets that successfully matched in the table.
+     *
+     * @return the number of packets that successfully matched in the table
+     */
+    long packetsMatched();
+}
index 534f6b9..1286ffc 100644 (file)
@@ -385,30 +385,6 @@ public interface TrafficSelector {
          */
         Builder matchIPv6ExthdrFlags(short exthdrFlags);
 
-        /**
-         * Matches an optical signal ID or lambda.
-         *
-         * @param lambda lambda
-         * @return a selection builder
-         * @deprecated in Cardinal Release.
-         * Use {@link #add(Criterion)} with an instance created
-         * by {@link org.onosproject.net.flow.criteria.Criteria#matchLambda(org.onosproject.net.Lambda)}.
-         */
-        @Deprecated
-        Builder matchLambda(short lambda);
-
-        /**
-         * Matches an optical Signal Type.
-         *
-         * @param signalType signalType
-         * @return a selection builder
-         * @deprecated in Cardinal Release.
-         * Use {@link #add(Criterion)}} with an instance created
-         * by {@link org.onosproject.net.flow.criteria.Criteria#matchOchSignalType(org.onosproject.net.OchSignalType)}.
-         */
-        @Deprecated
-        Builder matchOpticalSignalType(short signalType);
-
         /**
          * Builds an immutable traffic selector.
          *
index 1ce669c..33753af 100644 (file)
@@ -268,16 +268,6 @@ public interface TrafficTreatment {
          */
         Builder meter(MeterId meterId);
 
-        /**
-         * Sets the next table type to transition to.
-         *
-         * @param type the table type
-         * @return a treatement builder
-         * @deprecated in Cardinal Release
-         */
-        @Deprecated
-        Builder transition(FlowRule.Type type);
-
         /**
          * Sets the next table id to transition to.
          *
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TypedStoredFlowEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TypedStoredFlowEntry.java
new file mode 100644 (file)
index 0000000..a93dc07
--- /dev/null
@@ -0,0 +1,65 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.net.flow;\r
+\r
+/**\r
+ * Represents a flow live type for a given flow entry.\r
+ */\r
+public interface TypedStoredFlowEntry extends StoredFlowEntry {\r
+    enum FlowLiveType {\r
+        /**\r
+         * Indicates that this rule has been submitted for addition immediately.\r
+         * Not necessarily collecting flow stats.\r
+         */\r
+        IMMEDIATE_FLOW,\r
+\r
+        /**\r
+         * Indicates that this rule has been submitted for a short time.\r
+         * Necessarily collecting flow stats every calAndPollInterval.\r
+         */\r
+        SHORT_FLOW,\r
+\r
+        /**\r
+         * Indicates that this rule has been submitted for a mid time.\r
+         * Necessarily collecting flow stats every midPollInterval.\r
+         */\r
+        MID_FLOW,\r
+\r
+        /**\r
+         * Indicates that this rule has been submitted for a long time.\r
+         * Necessarily collecting flow stats every longPollInterval.\r
+         */\r
+        LONG_FLOW,\r
+\r
+        /**\r
+         * Indicates that this rule has been submitted for UNKNOWN or ERROR.\r
+         * Not necessarily collecting flow stats.\r
+         */\r
+        UNKNOWN_FLOW\r
+    }\r
+\r
+    /**\r
+     * Gets the flow live type for this entry.\r
+     */\r
+    FlowLiveType flowLiveType();\r
+\r
+    /**\r
+     * Sets the new flow live type for this entry.\r
+     * @param liveType new flow live type.\r
+     */\r
+    void setFlowLiveType(FlowLiveType liveType);\r
+}\r
index 0252cfb..7e1d43a 100644 (file)
@@ -460,18 +460,6 @@ public final class Criteria {
         return new IPv6ExthdrFlagsCriterion(exthdrFlags);
     }
 
-    /**
-     * Creates a match on lambda field using the specified value.
-     *
-     * @param lambda lambda to match on (16 bits unsigned integer)
-     * @return match criterion
-     * @deprecated in Cardinal Release. Use {@link #matchLambda(Lambda)} instead.
-     */
-    @Deprecated
-    public static Criterion matchLambda(int lambda) {
-        return new LambdaCriterion(lambda, Type.OCH_SIGID);
-    }
-
     /**
      * Creates a match on lambda using the specified value.
      *
@@ -488,18 +476,6 @@ public final class Criteria {
         }
     }
 
-    /**
-     * Creates a match on optical signal type using the specified value.
-     *
-     * @param sigType optical signal type (8 bits unsigned integer)
-     * @return match criterion
-     * @deprecated in Cardinal Release
-     */
-    @Deprecated
-    public static Criterion matchOpticalSignalType(short sigType) {
-        return new OpticalSignalTypeCriterion(sigType, Type.OCH_SIGTYPE);
-    }
-
     /**
      * Create a match on OCh (Optical Channel) signal type.
      *
index 6f2cac6..d01ea29 100644 (file)
@@ -27,8 +27,17 @@ public interface Instruction {
         /**
          * Signifies that the traffic should be dropped.
          */
+        @Deprecated
         DROP,
 
+        /**
+         * Signifies that the traffic requires no action.
+         *
+         * In OF10, the behavior of NOACTION is DROP.
+         * In OF13, the behavior depends on current Action Set.
+         */
+        NOACTION,
+
         /**
          * Signifies that the traffic should be output to a port.
          */
index c5358a2..c9f1068 100644 (file)
@@ -68,10 +68,20 @@ public final class Instructions {
      *
      * @return drop instruction
      */
+    @Deprecated
     public static DropInstruction createDrop() {
         return new DropInstruction();
     }
 
+    /**
+     * Creates a no action instruction.
+     *
+     * @return no action instruction
+     */
+    public static NoActionInstruction createNoAction() {
+        return new NoActionInstruction();
+    }
+
     /**
      * Creates a group instruction.
      *
@@ -88,19 +98,6 @@ public final class Instructions {
         return new MeterInstruction(meterId);
     }
 
-    /**
-     * Creates a l0 modification.
-     *
-     * @param lambda the lambda to modify to
-     * @return a l0 modification
-     * @deprecated in Cardinal Release. Use {@link #modL0Lambda(Lambda)} instead.
-     */
-    @Deprecated
-    public static L0ModificationInstruction modL0Lambda(short lambda) {
-        checkNotNull(lambda, "L0 lambda cannot be null");
-        return new ModLambdaInstruction(L0SubType.LAMBDA, lambda);
-    }
-
     /**
      * Creates an L0 modification with the specified OCh signal.
      *
@@ -298,21 +295,6 @@ public final class Instructions {
                 EthType.EtherType.MPLS_UNICAST.ethType());
     }
 
-    /**
-     * Creates a pop MPLS header instruction with a particular ethertype.
-     *
-     * @param etherType Ethernet type to set
-     * @return a L2 modification.
-     * @deprecated in Cardinal Release
-     */
-    @Deprecated
-    public static Instruction popMpls(int etherType) {
-        checkNotNull(etherType, "Ethernet type cannot be null");
-        return new L2ModificationInstruction.PushHeaderInstructions(
-                L2ModificationInstruction.L2SubType.MPLS_POP, new EthType(etherType));
-    }
-
-
     /**
      * Creates a pop MPLS header instruction with a particular ethertype.
      *
@@ -478,6 +460,7 @@ public final class Instructions {
     /**
      *  Drop instruction.
      */
+    @Deprecated
     public static final class DropInstruction implements Instruction {
 
         private DropInstruction() {}
@@ -509,6 +492,40 @@ public final class Instructions {
         }
     }
 
+    /**
+     *  No Action instruction.
+     */
+    public static final class NoActionInstruction implements Instruction {
+
+        private NoActionInstruction() {}
+
+        @Override
+        public Type type() {
+            return Type.NOACTION;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(type().toString()).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type().ordinal());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof NoActionInstruction) {
+                return true;
+            }
+            return false;
+        }
+    }
+
     /**
      *  Output Instruction.
      */
index f7b7c49..068663b 100644 (file)
@@ -15,6 +15,7 @@
  */
 package org.onosproject.net.host;
 
+import org.onlab.packet.IpAddress;
 import org.onosproject.net.HostId;
 import org.onosproject.net.provider.ProviderService;
 
@@ -29,6 +30,7 @@ public interface HostProviderService extends ProviderService<HostProvider> {
      *
      * @param hostId          id of the host that been detected
      * @param hostDescription description of host and its location
+     * @deprecated in Drake release
      */
     @Deprecated
     default void hostDetected(HostId hostId, HostDescription hostDescription) {
@@ -52,4 +54,11 @@ public interface HostProviderService extends ProviderService<HostProvider> {
      */
     void hostVanished(HostId hostId);
 
+    /**
+     * Notifies the core when a host is no longer detected on a network.
+     *
+     * @param hostId id of the host that vanished
+     */
+    void removeIpFromHost(HostId hostId, IpAddress ipAddress);
+
 }
index 5894fe9..918ced4 100644 (file)
@@ -54,6 +54,15 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> {
      */
     HostEvent removeHost(HostId hostId);
 
+    /**
+     * Removes the specified ip from the host entry.
+     *
+     * @param hostId host identification
+     * @param ipAddress ipAddress to be removed
+     * @return remove event or null if host was not found
+     */
+    HostEvent removeIp(HostId hostId, IpAddress ipAddress);
+
     /**
      * Returns the number of hosts in the store.
      *
index 0646a00..2a2d7c7 100644 (file)
@@ -23,7 +23,6 @@ import org.onosproject.net.NetworkResource;
 import org.onosproject.net.flow.FlowRule;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -37,18 +36,6 @@ public class FlowRuleIntent extends Intent {
 
     private final Collection<FlowRule> flowRules;
 
-    /**
-     * Creates an flow rule intent with the specified flow rules to be set.
-     *
-     * @param appId     application id
-     * @param flowRules flow rules to be set.
-     * @deprecated in Cardinal Release
-     */
-    @Deprecated
-    public FlowRuleIntent(ApplicationId appId, List<FlowRule> flowRules) {
-        this(appId, null, flowRules, Collections.emptyList());
-    }
-
     /**
      * Creates a flow rule intent with the specified flow rules and resources.
      *
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastEvent.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastEvent.java
new file mode 100644 (file)
index 0000000..979194c
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.mcast;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.event.AbstractEvent;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.Optional;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * An entity representing a multicast event. Event either add or remove
+ * sinks or sources.
+ */
+@Beta
+public class McastEvent extends AbstractEvent<McastEvent.Type, McastRoute> {
+
+    private final Optional<ConnectPoint> sink;
+    private final Optional<ConnectPoint> source;
+
+    public enum Type {
+        /**
+         * A new mcast route has been added.
+         */
+        ROUTE_ADDED,
+
+        /**
+         * A mcast route has been removed.
+         */
+        ROUTE_REMOVED,
+
+        /**
+         * A source for a mcast route (ie. the subject) has been added.
+         */
+        SOURCE_ADDED,
+
+        /**
+         * A sink for a mcast route (ie. the subject) has been added.
+         */
+        SINK_ADDED,
+
+        /**
+         * A source for a mcast route (ie. the subject) has been removed.
+         */
+        SINK_REMOVED
+    }
+
+    private McastEvent(McastEvent.Type type, McastRoute subject) {
+        super(type, subject);
+        sink = Optional.empty();
+        source = Optional.empty();
+    }
+
+    private McastEvent(McastEvent.Type type, McastRoute subject, long time) {
+        super(type, subject, time);
+        sink = Optional.empty();
+        source = Optional.empty();
+    }
+
+    public McastEvent(McastEvent.Type type, McastRoute subject,
+                      ConnectPoint sink,
+                      ConnectPoint source) {
+        super(type, subject);
+        this.sink = Optional.ofNullable(sink);
+        this.source = Optional.ofNullable(source);
+    }
+
+    public McastEvent(McastEvent.Type type, McastRoute subject, long time,
+                       ConnectPoint sink,
+                       ConnectPoint source) {
+        super(type, subject, time);
+        this.sink = Optional.ofNullable(sink);
+        this.source = Optional.ofNullable(source);
+    }
+
+    /**
+     * The sink which has been removed or added. The field may not be set
+     * if the sink has not been detected yet or has been removed.
+     *
+     * @return an optional connect point
+     */
+    public Optional<ConnectPoint> sink() {
+        return sink;
+    }
+
+    /**
+     * The source which has been removed or added.
+
+     * @return an optional connect point
+     */
+    public Optional<ConnectPoint> source() {
+        return source;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("type", type())
+                .add("route", subject())
+                .add("source", source)
+                .add("sinks", sink).toString();
+    }
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastListener.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastListener.java
new file mode 100644 (file)
index 0000000..06449b9
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.mcast;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.event.EventListener;
+
+/**
+ * A listener interface for multicast events.
+ */
+@Beta
+public interface McastListener extends EventListener<McastEvent> {
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastRoute.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastRoute.java
new file mode 100644 (file)
index 0000000..ff1292b
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.mcast;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onlab.packet.IpPrefix;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An entity representing a multicast route consisting of a source
+ * and a multicast group address.
+ */
+@Beta
+public class McastRoute {
+
+    public enum Type {
+        /**
+         * Route originates from PIM.
+         */
+        PIM,
+
+        /**
+         * Route originates from IGMP.
+         */
+        IGMP,
+
+        /**
+         * Route originates from other config (ie. REST, CLI).
+         */
+        STATIC
+    }
+
+    private final IpPrefix source;
+    private final IpPrefix group;
+    private final Type type;
+
+    public McastRoute(IpPrefix source, IpPrefix group, Type type) {
+        checkNotNull(source, "Multicast route must have a source");
+        checkNotNull(group, "Multicast route must specify a group address");
+        checkNotNull(type, "Must indicate what type of route");
+        this.source = source;
+        this.group = group;
+        this.type = type;
+    }
+
+    /**
+     * Fetches the source address of this route.
+     *
+     * @return an ip address
+     */
+    public IpPrefix source() {
+        return source;
+    }
+
+    /**
+     * Fetches the group address of this route.
+     *
+     * @return an ip address
+     */
+    public IpPrefix group() {
+        return group;
+    }
+
+    /**
+     * Obtains how this route was created.
+     * @return a type of route
+
+     */
+    public Type type() {
+        return type;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("source", source)
+                .add("group", group)
+                .add("origin", type)
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        McastRoute that = (McastRoute) o;
+        return Objects.equal(source, that.source) &&
+                Objects.equal(group, that.group) &&
+                Objects.equal(type, that.type);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(source, group, type);
+    }
+
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/MulticastRouteService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/MulticastRouteService.java
new file mode 100644 (file)
index 0000000..56e87c5
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.mcast;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.List;
+
+/**
+ * A service interface for maintaining multicast information.
+ */
+@Beta
+public interface MulticastRouteService {
+
+    /**
+     * Adds a route to the information base.
+     *
+     * @param route a multicast route
+     */
+    void add(McastRoute route);
+
+    /**
+     * Removes a route from the information base.
+     *
+     * @param route a multicast route
+     */
+    void remove(McastRoute route);
+
+    /**
+     * Adds a source connection to the route from where the
+     * data stream is originating.
+     *
+     * @param route the multicast route
+     * @param connectPoint a source connect point
+     */
+    void addSource(McastRoute route, ConnectPoint connectPoint);
+
+    /**
+     * Adds a sink to the route to which a data stream should be
+     * sent to.
+     *
+     * @param route a multicast route
+     * @param connectPoint a sink connect point
+     */
+    void addSink(McastRoute route, ConnectPoint connectPoint);
+
+    /**
+     * Removes a sink from the route.
+     *
+     * @param route the multicast route
+     * @param connectPoint a sink connect point
+     */
+    void removeSink(McastRoute route, ConnectPoint connectPoint);
+
+    /**
+     * Find the data source association for this multicast route.
+     *
+     * @param route a multicast route
+     * @return a connect point
+     */
+    ConnectPoint fetchSource(McastRoute route);
+
+    /**
+     * Find the list of sinks for this route.
+     *
+     * @param route a multicast route
+     * @return a list of connect points
+     */
+    List<ConnectPoint> fetchSinks(McastRoute route);
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/package-info.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/package-info.java
new file mode 100644 (file)
index 0000000..e8dcc7b
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * External model entities of the multicast RIB.
+ */
+package org.onosproject.net.mcast;
\ No newline at end of file
index 94cada4..51394c3 100644 (file)
@@ -43,7 +43,8 @@ public final class DefaultMeterRequest implements MeterRequest {
 
     private DefaultMeterRequest(DeviceId deviceId, ApplicationId appId,
                                 Meter.Unit unit, boolean burst,
-                                Collection<Band> bands, MeterContext context, Type op) {
+                                Collection<Band> bands, MeterContext context,
+                                Type op) {
         this.deviceId = deviceId;
         this.appId = appId;
         this.unit = unit;
@@ -58,6 +59,7 @@ public final class DefaultMeterRequest implements MeterRequest {
         return deviceId;
     }
 
+
     @Override
     public ApplicationId appId() {
         return appId;
@@ -107,6 +109,7 @@ public final class DefaultMeterRequest implements MeterRequest {
         private Collection<Band> bands;
         private DeviceId deviceId;
         private MeterContext context;
+        private Optional<MeterId> desiredId = Optional.empty();
 
 
         @Override
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java
new file mode 100644 (file)
index 0000000..5bc01b0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.meter;
+
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A meter key represents a meter uniquely.
+ */
+public final class MeterKey {
+
+    private final DeviceId deviceId;
+    private final MeterId id;
+
+    private MeterKey(DeviceId deviceId, MeterId id) {
+        this.deviceId = deviceId;
+        this.id = id;
+    }
+
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    public MeterId meterId() {
+        return id;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MeterKey meterKey = (MeterKey) o;
+        return Objects.equal(deviceId, meterKey.deviceId) &&
+                Objects.equal(id, meterKey.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(deviceId, id);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("deviceId", deviceId)
+                .add("meterId", id).toString();
+    }
+
+    public static MeterKey key(DeviceId deviceId, MeterId id) {
+        return new MeterKey(deviceId, id);
+    }
+}
index bdc90eb..2e07cb6 100644 (file)
@@ -16,6 +16,7 @@
 package org.onosproject.net.meter;
 
 import org.onosproject.event.ListenerService;
+import org.onosproject.net.DeviceId;
 
 import java.util.Collection;
 
@@ -46,10 +47,11 @@ public interface MeterService
     /**
      * Fetch the meter by the meter id.
      *
+     * @param deviceId a device id
      * @param id a meter id
      * @return a meter
      */
-    Meter getMeter(MeterId id);
+    Meter getMeter(DeviceId deviceId, MeterId id);
 
     /**
      * Fetches all the meters.
index 5112a4a..f429e95 100644 (file)
@@ -57,12 +57,12 @@ public interface MeterStore extends Store<MeterEvent, MeterStoreDelegate> {
     void updateMeterState(Meter meter);
 
     /**
-     * Obtains a meter matching the given meter id.
+     * Obtains a meter matching the given meter key.
      *
-     * @param meterId a meter id
+     * @param key a meter key
      * @return a meter
      */
-    Meter getMeter(MeterId meterId);
+    Meter getMeter(MeterKey key);
 
     /**
      * Returns all meters stored in the store.
index 618042a..82d8474 100644 (file)
@@ -124,6 +124,15 @@ public interface ResourceService {
      */
     boolean release(ResourceConsumer consumer);
 
+    /**
+     * Returns resource allocation of the specified resource.
+     *
+     * @param resource resource to check the allocation
+     * @return allocation information enclosed by Optional.
+     * If the resource is not allocated, the return value is empty.
+     */
+    Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource);
+
     /**
      * Returns allocated resources being as children of the specified parent and being the specified resource type.
      *
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketProcessorEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketProcessorEntry.java
new file mode 100644 (file)
index 0000000..40386fb
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.packet;
+
+/**
+ * Packet processor entry tracking the processor, its priority and
+ * time consumption.
+ */
+public interface PacketProcessorEntry {
+
+    /**
+     * Returns the packet processor.
+     *
+     * @return packet processor
+     */
+    PacketProcessor processor();
+
+    /**
+     * Returns the packet processor priority.
+     *
+     * @return processor priority
+     */
+    int priority();
+
+    /**
+     * Returns the number of invocations.
+     *
+     * @return number of invocations
+     */
+    long invocations();
+
+    /**
+     * Returns the total time, in nanoseconds, spent processing packets.
+     *
+     * @return total time in nanos
+     */
+    long totalNanos();
+
+    /**
+     * Returns the average time, in nanoseconds, spent processing packets.
+     *
+     * @return average time in nanos
+     */
+    long averageNanos();
+}
index 98f4d8e..2e7a1b9 100644 (file)
@@ -20,7 +20,6 @@ import org.onosproject.core.ApplicationId;
 import org.onosproject.net.flow.TrafficSelector;
 
 import java.util.List;
-import java.util.Map;
 
 /**
  * Service for intercepting data plane packets and for emitting synthetic
@@ -52,13 +51,12 @@ public interface PacketService {
     void removeProcessor(PacketProcessor processor);
 
     /**
-     * Returns priority bindings of all registered packet processors.
+     * Returns priority bindings of all registered packet processor entries.
      *
-     * @return list of existing packet processors
+     * @return list of existing packet processor entries
      */
     @Beta
-    // TODO: Consider returning list of PacketProcessorEntry with processor, priority and stats
-    Map<Integer, PacketProcessor> getProcessors();
+    List<PacketProcessorEntry> getProcessors();
 
     /**
      * Requests that packets matching the given selector are punted from the
index 97f7cb5..d83fc9a 100644 (file)
@@ -37,17 +37,15 @@ public interface PacketStore extends Store<PacketEvent, PacketStoreDelegate> {
      * Requests intercept of packets that match the given selector.
      *
      * @param request a packet request
-     * @return true if the first time the given selector was requested
      */
-    boolean requestPackets(PacketRequest request);
+    void requestPackets(PacketRequest request);
 
     /**
      * Cancels intercept of packets that match the given selector.
      *
      * @param request a packet request
-     * @return true if there is no other application requesting the given selector
      */
-    boolean cancelPackets(PacketRequest request);
+    void cancelPackets(PacketRequest request);
 
     /**
      * Obtains all existing requests in the system.
index bf5c3cc..2e59b19 100644 (file)
@@ -21,4 +21,20 @@ import org.onosproject.store.StoreDelegate;
  * Packet store delegate abstraction.
  */
 public interface PacketStoreDelegate extends StoreDelegate<PacketEvent> {
+
+    /**
+     * Requests that packets matching to following request be collected
+     * from all switches.
+     *
+     * @param request packet request
+     */
+    void requestPackets(PacketRequest request);
+
+    /**
+     * Requests that packets matching to following request no longer be
+     * collected from any switches.
+     *
+     * @param request packet request
+     */
+    void cancelPackets(PacketRequest request);
 }
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/device/IntentSetMultimap.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/device/IntentSetMultimap.java
new file mode 100644 (file)
index 0000000..67c539d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.resource.device;
+
+import org.onosproject.net.intent.IntentId;
+
+import java.util.Set;
+
+public interface IntentSetMultimap {
+
+    /**
+     * Allocates the mapping between the given intents.
+     *
+     * @param keyIntentId key intent ID
+     * @param valIntentId value intent ID
+     * @return true if mapping was successful, false otherwise
+     */
+    boolean allocateMapping(IntentId keyIntentId, IntentId valIntentId);
+
+    /**
+     * Returns the set of intents mapped to a lower intent.
+     *
+     * @param intentId intent ID
+     * @return set of intent IDs
+     */
+    Set<IntentId> getMapping(IntentId intentId);
+
+    /**
+     * Releases the mapping of the given intent.
+     *
+     * @param intentId intent ID
+     */
+    void releaseMapping(IntentId intentId);
+}
index 91cc3d1..e07309c 100644 (file)
@@ -64,7 +64,7 @@ public class BandwidthResourceRequest implements ResourceRequest {
         if (obj == null || getClass() != obj.getClass()) {
             return false;
         }
-        final BandwidthResourceAllocation other = (BandwidthResourceAllocation) obj;
+        final BandwidthResourceRequest other = (BandwidthResourceRequest) obj;
         return Objects.equals(this.bandwidth, other.bandwidth());
     }
 
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticService.java
new file mode 100644 (file)
index 0000000..f59670b
--- /dev/null
@@ -0,0 +1,105 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.net.statistic;\r
+\r
+import org.onosproject.net.ConnectPoint;\r
+import org.onosproject.net.Device;\r
+import org.onosproject.net.PortNumber;\r
+import org.onosproject.net.flow.TypedStoredFlowEntry;\r
+import org.onosproject.net.flow.instructions.Instruction;\r
+\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+/**\r
+ * Service for obtaining individual flow statistic information about device and link in the system.\r
+ * Basic statistics are obtained from the StatisticService\r
+ */\r
+public interface FlowStatisticService {\r
+\r
+    /**\r
+     * Obtain the summary load list for the device with the given link.\r
+     *\r
+     * @param device the Device  to query.\r
+     * @return map of summary flow entry load\r
+     */\r
+    Map<ConnectPoint, SummaryFlowEntryWithLoad> loadSummary(Device device);\r
+\r
+    /**\r
+     * Obtain the summary load for the device with the given link or port.\r
+     *\r
+     * @param device the Device to query.\r
+     * @param pNumber the port number to query.\r
+     * @return summary flow entry load\r
+     */\r
+    SummaryFlowEntryWithLoad loadSummary(Device device, PortNumber pNumber);\r
+\r
+    /**\r
+     * Obtain the set of the flow type and load list for the device with the given link.\r
+     *\r
+     * @param device the Device  to query.\r
+     * @param liveType the FlowLiveType  to filter, null means no filtering .\r
+     * @param instType the InstructionType to filter, null means no filtering.\r
+     * @return map of flow entry load\r
+     */\r
+    Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadAllByType(Device device,\r
+                                                                  TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                                  Instruction.Type instType);\r
+\r
+    /**\r
+     * Obtain the flow type and load list for the device with the given link or port.\r
+     *\r
+     * @param device the Device to query.\r
+     * @param pNumber the port number of the Device to query\r
+     * @param liveType the FlowLiveType  to filter, null means no filtering .\r
+     * @param instType the InstructionType to filter, null means no filtering.\r
+     * @return list of flow entry load\r
+     */\r
+    List<TypedFlowEntryWithLoad> loadAllByType(Device device, PortNumber pNumber,\r
+                                               TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                               Instruction.Type instType);\r
+\r
+    /**\r
+     * Obtain the set of the flow type and load topn list for the device with the given link.\r
+     *\r
+     * @param device the Device  to query.\r
+     * @param liveType the FlowLiveType  to filter, null means no filtering .\r
+     * @param instType the InstructionType to filter, null means no filtering.\r
+     * @param topn the top number to filter, null means no filtering.\r
+     * @return map of flow entry load\r
+     */\r
+    Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadTopnByType(Device device,\r
+                                                                   TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                                   Instruction.Type instType,\r
+                                                                   int topn);\r
+\r
+    /**\r
+     * Obtain the flow type and load topn list for the device with the given link or port.\r
+     *\r
+     * @param device the Device  to query.\r
+     * @param pNumber the port number of the Device to query\r
+     * @param liveType the FlowLiveType  to filter, null means no filtering .\r
+     * @param instType the InstructionType to filter, null means no filtering.\r
+     * @return list of flow entry load\r
+     */\r
+    List<TypedFlowEntryWithLoad> loadTopnByType(Device device, PortNumber pNumber,\r
+                                                TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                Instruction.Type instType,\r
+                                                int topn);\r
+}\r
+\r
+\r
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticStore.java
new file mode 100644 (file)
index 0000000..3c2aa89
--- /dev/null
@@ -0,0 +1,65 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.net.statistic;\r
+\r
+import org.onosproject.net.ConnectPoint;\r
+import org.onosproject.net.flow.FlowEntry;\r
+import org.onosproject.net.flow.FlowRule;\r
+\r
+import java.util.Set;\r
+\r
+/**\r
+ * Flow Store to house the computed statistics.\r
+ */\r
+public interface FlowStatisticStore {\r
+    /**\r
+     * Remove entries associated with this rule.\r
+     *\r
+     * @param rule {@link org.onosproject.net.flow.FlowRule}\r
+     */\r
+    void removeFlowStatistic(FlowRule rule);\r
+\r
+    /**\r
+     * Adds a flow stats observation for a flow rule. The previous flow will be removed.\r
+     *\r
+     * @param rule a {@link org.onosproject.net.flow.FlowEntry}\r
+     */\r
+    void addFlowStatistic(FlowEntry rule);\r
+\r
+    /**\r
+     * Updates a stats observation for a flow rule. The old flow stats will be moved to previous stats.\r
+     *\r
+     * @param rule a {@link org.onosproject.net.flow.FlowEntry}\r
+     */\r
+    void updateFlowStatistic(FlowEntry rule);\r
+\r
+    /**\r
+     * Fetches the current observed flow stats values.\r
+     *\r
+     * @param connectPoint the port to fetch information for\r
+     * @return set of current flow rules\r
+     */\r
+    Set<FlowEntry> getCurrentFlowStatistic(ConnectPoint connectPoint);\r
+\r
+    /**\r
+     * Fetches the current observed flow stats values.\r
+     *\r
+     * @param connectPoint the port to fetch information for\r
+     * @return set of current values\r
+     */\r
+    Set<FlowEntry> getPreviousFlowStatistic(ConnectPoint connectPoint);\r
+}\r
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/SummaryFlowEntryWithLoad.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/SummaryFlowEntryWithLoad.java
new file mode 100644 (file)
index 0000000..60da636
--- /dev/null
@@ -0,0 +1,143 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.net.statistic;\r
+\r
+import org.onosproject.net.ConnectPoint;\r
+\r
+/**\r
+ * Summary Load classified by flow live type.\r
+ */\r
+public class SummaryFlowEntryWithLoad {\r
+    private ConnectPoint cp;\r
+    private Load totalLoad;\r
+    private Load immediateLoad;\r
+    private Load shortLoad;\r
+    private Load midLoad;\r
+    private Load longLoad;\r
+    private Load unknownLoad;\r
+\r
+    /**\r
+     * Creates a new summary flow entry having load for the given connect point and total load.\r
+     *\r
+     * @param cp        connect point\r
+     * @param totalLoad total load\r
+     */\r
+    public SummaryFlowEntryWithLoad(ConnectPoint cp, Load totalLoad) {\r
+        this.cp = cp;\r
+        this.totalLoad = totalLoad;\r
+        this.immediateLoad = new DefaultLoad();\r
+        this.shortLoad = new DefaultLoad();\r
+        this.midLoad = new DefaultLoad();\r
+        this.longLoad = new DefaultLoad();\r
+        this.unknownLoad = new DefaultLoad();\r
+    }\r
+\r
+    /**\r
+     * Creates a new summary flow entry having load for the given connect point\r
+     * and total, immediate, short, mid, and long load.\r
+     *\r
+     * @param cp        connect point\r
+     * @param totalLoad total load\r
+     * @param immediateLoad immediate load\r
+     * @param shortLoad short load\r
+     * @param midLoad mid load\r
+     * @param longLoad long load\r
+     */\r
+    public SummaryFlowEntryWithLoad(ConnectPoint cp,\r
+                                    Load totalLoad, Load immediateLoad, Load shortLoad, Load midLoad, Load longLoad) {\r
+        this.cp = cp;\r
+        this.totalLoad = totalLoad;\r
+        this.immediateLoad = immediateLoad;\r
+        this.shortLoad = shortLoad;\r
+        this.midLoad = midLoad;\r
+        this.longLoad = longLoad;\r
+        this.unknownLoad = new DefaultLoad();\r
+    }\r
+\r
+    /**\r
+     * Creates a new summary flow entry having load for the given connect point\r
+     * and total, immediate, short, mid, long, and unknown load.\r
+     *\r
+     * @param cp        connect point\r
+     * @param totalLoad total load\r
+     * @param immediateLoad immediate load\r
+     * @param shortLoad short load\r
+     * @param midLoad mid load\r
+     * @param longLoad long load\r
+     * @param unknownLoad long load\r
+     */\r
+    public SummaryFlowEntryWithLoad(ConnectPoint cp,\r
+                                    Load totalLoad, Load immediateLoad,\r
+                                    Load shortLoad, Load midLoad, Load longLoad, Load unknownLoad) {\r
+        this.cp = cp;\r
+        this.totalLoad = totalLoad;\r
+        this.immediateLoad = immediateLoad;\r
+        this.shortLoad = shortLoad;\r
+        this.midLoad = midLoad;\r
+        this.longLoad = longLoad;\r
+        this.unknownLoad = unknownLoad;\r
+    }\r
+\r
+    /**\r
+     * Returns connect point.\r
+     */\r
+    public ConnectPoint connectPoint() {\r
+        return cp;\r
+    }\r
+\r
+    /**\r
+     * Returns total load of connect point.\r
+     */\r
+    public Load totalLoad() {\r
+        return totalLoad;\r
+    }\r
+\r
+    /**\r
+     * Returns immediate load of connect point.\r
+     */\r
+    public Load immediateLoad() {\r
+        return immediateLoad;\r
+    }\r
+\r
+    /**\r
+     * Returns short load of connect point.\r
+     */\r
+    public Load shortLoad() {\r
+        return shortLoad;\r
+    }\r
+\r
+    /**\r
+     * Returns mid load of connect point.\r
+     */\r
+    public Load midLoad() {\r
+        return midLoad;\r
+    }\r
+\r
+    /**\r
+     * Returns long load of connect point.\r
+     */\r
+    public Load longLoad() {\r
+        return longLoad;\r
+    }\r
+\r
+    /**\r
+     * Returns unknown load of connect point.\r
+     */\r
+    public Load unknownLoad() {\r
+        return unknownLoad;\r
+    }\r
+}\r
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/TypedFlowEntryWithLoad.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/TypedFlowEntryWithLoad.java
new file mode 100644 (file)
index 0000000..3e2dbdf
--- /dev/null
@@ -0,0 +1,143 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.net.statistic;\r
+\r
+import org.onosproject.net.ConnectPoint;\r
+import org.onosproject.net.flow.FlowEntry;\r
+import org.onosproject.net.flow.TypedStoredFlowEntry;\r
+import org.onosproject.net.flow.DefaultTypedFlowEntry;\r
+\r
+import static com.google.common.base.Preconditions.checkNotNull;\r
+\r
+/**\r
+ * Load of flow entry of flow live type.\r
+ */\r
+public class TypedFlowEntryWithLoad {\r
+    private ConnectPoint cp;\r
+    private TypedStoredFlowEntry tfe;\r
+    private Load load;\r
+\r
+    //TODO: make this variables class, and share with NewAdaptivceFlowStatsCollector class\r
+    private static final int CAL_AND_POLL_INTERVAL = 5; // means SHORT_POLL_INTERVAL\r
+    private static final int MID_POLL_INTERVAL = 10;\r
+    private static final int LONG_POLL_INTERVAL = 15;\r
+\r
+\r
+    public TypedFlowEntryWithLoad(ConnectPoint cp, TypedStoredFlowEntry tfe, Load load) {\r
+        this.cp = cp;\r
+        this.tfe = tfe;\r
+        this.load = load;\r
+    }\r
+\r
+    public TypedFlowEntryWithLoad(ConnectPoint cp, TypedStoredFlowEntry tfe) {\r
+        this.cp = cp;\r
+        this.tfe = tfe;\r
+        this.load = new DefaultLoad(tfe.bytes(), 0, typedPollInterval(tfe));\r
+    }\r
+\r
+    public TypedFlowEntryWithLoad(ConnectPoint cp, FlowEntry fe) {\r
+        this.cp = cp;\r
+        this.tfe = newTypedStoredFlowEntry(fe);\r
+        this.load = new DefaultLoad(fe.bytes(), 0, typedPollInterval(this.tfe));\r
+    }\r
+\r
+    public ConnectPoint connectPoint() {\r
+        return cp;\r
+    }\r
+    public TypedStoredFlowEntry typedStoredFlowEntry() {\r
+        return tfe;\r
+    }\r
+    public Load load() {\r
+        return load;\r
+    }\r
+    public void setLoad(Load load) {\r
+        this.load = load;\r
+    }\r
+\r
+    /**\r
+     * Returns short polling interval.\r
+     */\r
+    public static int shortPollInterval() {\r
+        return CAL_AND_POLL_INTERVAL;\r
+    }\r
+\r
+    /**\r
+     * Returns mid polling interval.\r
+     */\r
+    public static int midPollInterval() {\r
+        return MID_POLL_INTERVAL;\r
+    }\r
+\r
+    /**\r
+     * Returns long polling interval.\r
+     */\r
+    public static int longPollInterval() {\r
+        return LONG_POLL_INTERVAL;\r
+    }\r
+\r
+    /**\r
+     * Returns average polling interval.\r
+     */\r
+    public static int avgPollInterval() {\r
+        return (CAL_AND_POLL_INTERVAL + MID_POLL_INTERVAL + LONG_POLL_INTERVAL) / 3;\r
+    }\r
+\r
+    /**\r
+     * Returns current typed flow entry's polling interval.\r
+     *\r
+     * @param tfe typed flow entry\r
+     */\r
+    public static long typedPollInterval(TypedStoredFlowEntry tfe) {\r
+        checkNotNull(tfe, "TypedStoredFlowEntry cannot be null");\r
+\r
+        switch (tfe.flowLiveType()) {\r
+            case LONG_FLOW:\r
+                return LONG_POLL_INTERVAL;\r
+            case MID_FLOW:\r
+                return MID_POLL_INTERVAL;\r
+            case SHORT_FLOW:\r
+            case IMMEDIATE_FLOW:\r
+            default:\r
+                return CAL_AND_POLL_INTERVAL;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Creates a new typed flow entry with the given flow entry fe.\r
+     *\r
+     * @param fe flow entry\r
+     */\r
+    public static TypedStoredFlowEntry newTypedStoredFlowEntry(FlowEntry fe) {\r
+        if (fe == null) {\r
+            return null;\r
+        }\r
+\r
+        long life = fe.life();\r
+\r
+        if (life >= LONG_POLL_INTERVAL) {\r
+            return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.LONG_FLOW);\r
+        } else if (life >= MID_POLL_INTERVAL) {\r
+            return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.MID_FLOW);\r
+        } else if (life >= CAL_AND_POLL_INTERVAL) {\r
+            return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW);\r
+        } else if (life >= 0) {\r
+            return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW);\r
+        } else { // life < 0\r
+            return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW);\r
+        }\r
+    }\r
+}\r
index f1e20da..965c05d 100644 (file)
@@ -45,23 +45,6 @@ public class DefaultGraphDescription extends AbstractDescription
 
     private final Map<DeviceId, TopologyVertex> vertexesById = Maps.newHashMap();
 
-    /**
-     * Creates a minimal topology graph description to allow core to construct
-     * and process the topology graph.
-     *
-     * @param nanos       time in nanos of when the topology description was created
-     * @param devices     collection of infrastructure devices
-     * @param links       collection of infrastructure links
-     * @param annotations optional key/value annotations map
-     * @deprecated in Cardinal Release
-     */
-    @Deprecated
-    public DefaultGraphDescription(long nanos, Iterable<Device> devices,
-                                   Iterable<Link> links,
-                                   SparseAnnotations... annotations) {
-        this(nanos, System.currentTimeMillis(), devices, links, annotations);
-    }
-
     /**
      * Creates a minimal topology graph description to allow core to construct
      * and process the topology graph.
index be8c7cf..0bd4d75 100644 (file)
  */
 package org.onosproject.net.topology;
 
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.ElementId;
+import org.onosproject.net.Link;
 import org.onosproject.net.Path;
 
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -41,11 +44,58 @@ public interface PathService {
      * edge-weight entity, between the specified source and destination
      * network elements.
      *
-     * @param src source element
-     * @param dst destination element
+     * @param src    source element
+     * @param dst    destination element
      * @param weight edge-weight entity
      * @return set of all shortest paths between the two element
      */
     Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight);
 
+    /**
+     * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
+     * between the specified source and destination devices.
+     *
+     * @param src source device
+     * @param dst destination device
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst);
+
+    /**
+     * Returns the set of all disjoint shortest path pairs, computed using the supplied
+     * edge-weight entity, between the specified source and destination devices.
+     *
+     * @param src    source device
+     * @param dst    destination device
+     * @param weight edge-weight entity
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
+                                       LinkWeight weight);
+
+    /**
+     * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
+     * between the specified source and destination devices.
+     *
+     * @param src         source device
+     * @param dst         destination device
+     * @param riskProfile map of edges to risk profiles
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
+                                       Map<Link, Object> riskProfile);
+
+    /**
+     * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
+     * between the specified source and destination devices.
+     *
+     * @param src         source device
+     * @param dst         destination device
+     * @param weight      edge-weight entity
+     * @param riskProfile map of edges to risk profiles
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
+                                       LinkWeight weight, Map<Link, Object> riskProfile);
+
 }
index 41eac2c..466e4f9 100644 (file)
@@ -18,16 +18,18 @@ package org.onosproject.net.topology;
 import org.onosproject.event.ListenerService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
 
+import java.util.Map;
 import java.util.Set;
 
 /**
  * Service for providing network topology information.
  */
 public interface TopologyService
-    extends ListenerService<TopologyEvent, TopologyListener> {
+        extends ListenerService<TopologyEvent, TopologyListener> {
 
     /**
      * Returns the current topology descriptor.
@@ -72,8 +74,8 @@ public interface TopologyService
     /**
      * Returns the set of devices that belong to the specified cluster.
      *
-     * @param topology  topology descriptor
-     * @param cluster topology cluster
+     * @param topology topology descriptor
+     * @param cluster  topology cluster
      * @return set of cluster devices
      */
     Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster);
@@ -81,8 +83,8 @@ public interface TopologyService
     /**
      * Returns the set of links that form the specified cluster.
      *
-     * @param topology  topology descriptor
-     * @param cluster topology cluster
+     * @param topology topology descriptor
+     * @param cluster  topology cluster
      * @return set of cluster links
      */
     Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster);
@@ -111,6 +113,57 @@ public interface TopologyService
     Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
                        LinkWeight weight);
 
+    /**
+     * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
+     * between the specified source and destination devices.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst);
+
+    /**
+     * Returns the set of all disjoint shortest path pairs, computed using the supplied
+     * edge-weight entity, between the specified source and destination devices.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @param weight   edge-weight entity
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                       LinkWeight weight);
+
+    /**
+     * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
+     * between the specified source and destination devices.
+     *
+     * @param topology    topology descriptor
+     * @param src         source device
+     * @param dst         destination device
+     * @param riskProfile map of edges to risk profiles
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                       Map<Link, Object> riskProfile);
+
+    /**
+     * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count,
+     * between the specified source and destination devices.
+     *
+     * @param topology    topology descriptor
+     * @param src         source device
+     * @param dst         destination device
+     * @param weight      edge-weight entity
+     * @param riskProfile map of edges to risk profiles
+     * @return set of all shortest paths between the two devices
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                       LinkWeight weight, Map<Link, Object> riskProfile);
+
     /**
      * Indicates whether the specified connection point is part of the network
      * infrastructure or part of network edge.
index 983e616..039a205 100644 (file)
@@ -20,11 +20,13 @@ import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.store.Store;
 
 import java.util.List;
 import java.util.Set;
+import java.util.Map;
 
 /**
  * Manages inventory of topology snapshots; not intended for direct use.
@@ -111,6 +113,59 @@ public interface TopologyStore extends Store<TopologyEvent, TopologyStoreDelegat
     Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
                        LinkWeight weight);
 
+    /**
+     * Computes and returns the set of disjoint shortest path pairs
+     * between src and dst.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @param weight   link weight function
+     * @return set of shortest paths
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                       LinkWeight weight);
+
+    /**
+     * Computes and returns the set of disjoint shortest path pairs
+     * between src and dst.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @return set of shortest paths
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst);
+
+    /**
+     * Computes and returns the set of SRLG disjoint shortest path pairs between source
+     * and dst, given a mapping of edges to SRLG risk groups.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @param weight   link weight function
+     * @param riskProfile   map of edges to objects. Edges that map to the same object will
+     * be treated as if they were in the same risk group.
+     * @return set of shortest paths
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                       LinkWeight weight, Map<Link, Object> riskProfile);
+
+    /**
+     * Returns the set of pre-computed SRLG shortest paths between src and dest.
+     *
+     * @param topology topology descriptor
+     * @param src      source device
+     * @param dst      destination device
+     * @param riskProfile   map of edges to objects. Edges that map to the same object will
+     * be treated as if they were in the same risk group.
+     * @return set of shortest paths
+     */
+    Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                       Map<Link, Object> riskProfile);
+
+
     /**
      * Indicates whether the given connect point is part of the network fabric.
      *
index a879cc5..c0df713 100644 (file)
@@ -58,4 +58,21 @@ public interface AsyncAtomicCounter {
      * @return current value
      */
     CompletableFuture<Long> get();
+
+
+    /**
+     * Atomically sets the given value to the current value.
+     *
+     * @return future void
+     */
+    CompletableFuture<Void> set(long value);
+
+    /**
+     * Atomically sets the given counter to the updated value if the current value is the expected value, otherwise
+     * no change occurs.
+     * @param expectedValue the expected current value of the counter
+     * @param updateValue the new value to be set
+     * @return true if the update occurred and the expected value was equal to the current value, false otherwise
+     */
+    CompletableFuture<Boolean> compareAndSet(long expectedValue, long updateValue);
 }
index f620e08..3c9e02c 100644 (file)
@@ -50,6 +50,22 @@ public interface AtomicCounter {
      */
     long addAndGet(long delta);
 
+    /**
+     * Atomically sets the given value to the current value.
+     *
+     * @param value the value to set
+     */
+    void set(long value);
+
+    /**
+     * Atomically sets the given counter to the updated value if the current value is the expected value, otherwise
+     * no change occurs.
+     * @param expectedValue the expected current value of the counter
+     * @param updateValue the new value to be set
+     * @return true if the update occurred and the expected value was equal to the current value, false otherwise
+     */
+    boolean compareAndSet(long expectedValue, long updateValue);
+
     /**
      * Returns the current value of the counter without modifying it.
      *
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexExecutionService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexExecutionService.java
new file mode 100644 (file)
index 0000000..d05f3b9
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.service;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+/**
+ * Service for mutually exclusive job execution.
+ */
+public interface MutexExecutionService {
+
+    /**
+     * Runs the specified task in a mutually exclusive fashion.
+     * @param task task to run
+     * @param exclusionPath path on which different instances synchronize
+     * @param executor executor to use for running the task
+     * @return future that is completed when the task execution completes.
+     */
+    CompletableFuture<Void> execute(MutexTask task, String exclusionPath, Executor executor);
+}
\ No newline at end of file
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexTask.java b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexTask.java
new file mode 100644 (file)
index 0000000..ba5ee47
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.service;
+
+/**
+ * The MutexTask interface should be implemented by any class whose
+ * instances distributed across controllers are intended to be executed
+ * in a mutually exclusive fashion.
+ */
+public interface MutexTask {
+
+    /**
+     * Begins the execution of a mutually exclusive task.
+     * The start method will be called once the "lock" is acquired.
+     * After the start method returns the lock is released and some other
+     * instance can take over execution.
+     */
+    void start();
+
+    /**
+     * This method will be called when exclusivity of task execution
+     * can no longer be guaranteed. The implementation should take necessary steps
+     * to halt task execution in order to ensure correctness.
+     */
+    void stop();
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java
new file mode 100644 (file)
index 0000000..76f4246
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ui.table.cell;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+
+/**
+ * Formats number using the specified format string".
+ */
+public final class NumberFormatter extends AbstractCellFormatter {
+
+    private final NumberFormat format;
+
+    /**
+     * Creates a formatter using a default decimal format.
+     */
+    public NumberFormatter() {
+        this(new DecimalFormat("#,##0.00000"));
+    }
+
+    /**
+     * Creates a formatter using the specified format.
+     *
+     * @param format number format
+     */
+    public NumberFormatter(NumberFormat format) {
+        this.format = format;
+    }
+
+    @Override
+    protected String nonNullFormat(Object value) {
+        return format.format(value);
+    }
+
+}
index a1abd18..0fccef8 100644 (file)
@@ -17,10 +17,13 @@ package org.onosproject.cfg;
 
 import java.util.Set;
 
+import com.google.common.collect.ImmutableSet;
+
 /**
  * Adapter for testing against component configuration service.
  */
 public class ComponentConfigAdapter implements ComponentConfigService {
+
     @Override
     public Set<String> getComponentNames() {
         return null;
@@ -38,7 +41,7 @@ public class ComponentConfigAdapter implements ComponentConfigService {
 
     @Override
     public Set<ConfigProperty> getProperties(String componentName) {
-        return null;
+        return ImmutableSet.of();
     }
 
     @Override
@@ -46,6 +49,10 @@ public class ComponentConfigAdapter implements ComponentConfigService {
 
     }
 
+    @Override
+    public void preSetProperty(String componentName, String name, String value) {
+    }
+
     @Override
     public void unsetProperty(String componentName, String name) {
 
diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/behaviour/ControllerInfoTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/behaviour/ControllerInfoTest.java
new file mode 100644 (file)
index 0000000..ece7f19
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.behaviour;
+
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.onlab.packet.IpAddress;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test for ControllerInfo class.
+ */
+public class ControllerInfoTest {
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void tcpSslFormat() {
+        String target = "tcp:192.168.1.1:6653";
+        ControllerInfo controllerInfo = new ControllerInfo(target);
+        assertEquals("wrong type", controllerInfo.type(), "tcp");
+        assertEquals("wrong ip", controllerInfo.ip(), IpAddress.valueOf("192.168.1.1"));
+        assertEquals("wrong port", controllerInfo.port(), 6653);
+
+    }
+
+    @Test
+    public void ptcpPsslFormat() {
+        String target = "ptcp:6653:192.168.1.1";
+        ControllerInfo controllerInfo = new ControllerInfo(target);
+        assertEquals("wrong type", controllerInfo.type(), "ptcp");
+        assertEquals("wrong ip", controllerInfo.ip(), IpAddress.valueOf("192.168.1.1"));
+        assertEquals("wrong port", controllerInfo.port(), 6653);
+
+    }
+
+    @Test
+    public void unixFormat() {
+        String target = "unix:file";
+        thrown.expect(IllegalArgumentException.class);
+        ControllerInfo controllerInfo = new ControllerInfo(target);
+        assertTrue("wrong type", controllerInfo.type().contains("unix"));
+        assertNull("wrong ip", controllerInfo.ip());
+        assertEquals("wrong port", controllerInfo.port(), -1);
+
+    }
+
+    @Test
+    public void defaultValues() {
+        String target = "tcp:192.168.1.1";
+        ControllerInfo controllerInfo = new ControllerInfo(target);
+        assertEquals("wrong type", controllerInfo.type(), "tcp");
+        assertEquals("wrong ip", controllerInfo.ip(), IpAddress.valueOf("192.168.1.1"));
+        assertEquals("wrong port", controllerInfo.port(), 6653);
+        String target1 = "ptcp:5000:";
+        ControllerInfo controllerInfo2 = new ControllerInfo(target1);
+        assertEquals("wrong type", controllerInfo2.type(), "ptcp");
+        assertEquals("wrong ip", controllerInfo2.ip(), IpAddress.valueOf("0.0.0.0"));
+        assertEquals("wrong port", controllerInfo2.port(), 5000);
+        String target2 = "ptcp:";
+        ControllerInfo controllerInfo3 = new ControllerInfo(target2);
+        assertEquals("wrong type", controllerInfo3.type(), "ptcp");
+        assertEquals("wrong ip", controllerInfo3.ip(), IpAddress.valueOf("0.0.0.0"));
+        assertEquals("wrong port", controllerInfo3.port(), 6653);
+    }
+
+
+    @Test
+    public void testEquals() {
+        String target1 = "ptcp:6653:192.168.1.1";
+        ControllerInfo controllerInfo1 = new ControllerInfo(target1);
+        String target2 = "ptcp:6653:192.168.1.1";
+        ControllerInfo controllerInfo2 = new ControllerInfo(target2);
+        assertTrue("wrong equals method", controllerInfo1.equals(controllerInfo2));
+    }
+
+    @Test
+    public void testListEquals() {
+        String target1 = "ptcp:6653:192.168.1.1";
+        ControllerInfo controllerInfo1 = new ControllerInfo(target1);
+        String target2 = "ptcp:6653:192.168.1.1";
+        ControllerInfo controllerInfo2 = new ControllerInfo(target2);
+        String target3 = "tcp:192.168.1.1:6653";
+        ControllerInfo controllerInfo3 = new ControllerInfo(target3);
+        String target4 = "tcp:192.168.1.1:6653";
+        ControllerInfo controllerInfo4 = new ControllerInfo(target4);
+        List<ControllerInfo> list1 = new ArrayList<>(Arrays.asList(controllerInfo1, controllerInfo3));
+        List<ControllerInfo> list2 = new ArrayList<>(Arrays.asList(controllerInfo2, controllerInfo4));
+        assertTrue("wrong equals list method", list1.equals(list2));
+    }
+}
index b70d14e..7307258 100644 (file)
@@ -29,7 +29,7 @@ public class NetworkConfigServiceAdapter implements NetworkConfigService {
     }
 
     @Override
-    public SubjectFactory getSubjectFactory(String subjectKey) {
+    public SubjectFactory getSubjectFactory(String subjectClassKey) {
         return null;
     }
 
@@ -39,7 +39,7 @@ public class NetworkConfigServiceAdapter implements NetworkConfigService {
     }
 
     @Override
-    public Class<? extends Config> getConfigClass(String subjectKey, String configKey) {
+    public Class<? extends Config> getConfigClass(String subjectClassKey, String configKey) {
         return null;
     }
 
index b871397..10c5a63 100644 (file)
@@ -267,29 +267,5 @@ public class DefaultTrafficSelectorTest {
         selector = DefaultTrafficSelector.builder()
                 .add(Criteria.matchLambda(new IndexedLambda(shortValue))).build();
         assertThat(selector, hasCriterionWithType(Type.OCH_SIGID));
-
-        selector = DefaultTrafficSelector.builder()
-                .add(Criteria.matchOpticalSignalType(shortValue)).build();
-        assertThat(selector, hasCriterionWithType(Type.OCH_SIGTYPE));
-    }
-
-    /**
-     * Tests the traffic selector builder.
-     */
-    @Test
-    public void testTrafficSelectorBuilder() {
-        TrafficSelector selector;
-        final short shortValue = 33;
-
-        final TrafficSelector baseSelector = DefaultTrafficSelector.builder()
-                .add(Criteria.matchLambda(new IndexedLambda(shortValue))).build();
-        selector = DefaultTrafficSelector.builder(baseSelector)
-                .build();
-        assertThat(selector, hasCriterionWithType(Type.OCH_SIGID));
-
-        final Criterion criterion = Criteria.matchLambda(shortValue);
-        selector = DefaultTrafficSelector.builder()
-                .add(criterion).build();
-        assertThat(selector, hasCriterionWithType(Type.OCH_SIGID));
     }
 }
index c7b7879..56e5911 100644 (file)
@@ -35,17 +35,14 @@ public class FlowRuleServiceAdapter implements FlowRuleService {
 
     @Override
     public void applyFlowRules(FlowRule... flowRules) {
-
     }
 
     @Override
     public void removeFlowRules(FlowRule... flowRules) {
-
     }
 
     @Override
     public void removeFlowRulesById(ApplicationId appId) {
-
     }
 
     @Override
@@ -60,16 +57,18 @@ public class FlowRuleServiceAdapter implements FlowRuleService {
 
     @Override
     public void apply(FlowRuleOperations ops) {
-
     }
 
     @Override
     public void addListener(FlowRuleListener listener) {
-
     }
 
     @Override
     public void removeListener(FlowRuleListener listener) {
+    }
 
+    @Override
+    public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) {
+        return null;
     }
 }
index ee294f6..95d605c 100644 (file)
@@ -225,12 +225,6 @@ public class CriteriaTest {
     Criterion matchIpv6ExthdrFlags2 =
         Criteria.matchIPv6ExthdrFlags(ipv6ExthdrFlags2);
 
-    int lambda1 = 1;
-    int lambda2 = 2;
-    Criterion matchLambda1 = Criteria.matchLambda(lambda1);
-    Criterion sameAsMatchLambda1 = Criteria.matchLambda(lambda1);
-    Criterion matchLambda2 = Criteria.matchLambda(lambda2);
-
     Criterion matchOchSignalType1 = Criteria.matchOchSignalType(OchSignalType.FIXED_GRID);
     Criterion sameAsMatchOchSignalType1 = Criteria.matchOchSignalType(OchSignalType.FIXED_GRID);
     Criterion matchOchSignalType2 = Criteria.matchOchSignalType(OchSignalType.FLEX_GRID);
@@ -239,12 +233,6 @@ public class CriteriaTest {
     Criterion sameAsMatchIndexedLambda1 = Criteria.matchLambda(Lambda.indexedLambda(1));
     Criterion matchIndexedLambda2 = Criteria.matchLambda(Lambda.indexedLambda(2));
 
-    short signalLambda1 = 1;
-    short signalLambda2 = 2;
-    Criterion matchSignalLambda1 = Criteria.matchOpticalSignalType(signalLambda1);
-    Criterion sameAsMatchSignalLambda1 = Criteria.matchOpticalSignalType(signalLambda1);
-    Criterion matchSignalLambda2 = Criteria.matchOpticalSignalType(signalLambda2);
-
     Criterion matchOchSignal1 =
             Criteria.matchLambda(Lambda.ochSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, 4, 8));
     Criterion sameAsMatchOchSignal1 =
@@ -306,7 +294,6 @@ public class CriteriaTest {
         assertThatClassIsImmutable(MplsCriterion.class);
         assertThatClassIsImmutable(IPv6ExthdrFlagsCriterion.class);
         assertThatClassIsImmutable(LambdaCriterion.class);
-        assertThatClassIsImmutable(OpticalSignalTypeCriterion.class);
     }
 
     // PortCriterion class
@@ -1057,32 +1044,6 @@ public class CriteriaTest {
                 .testEquals();
     }
 
-    // LambdaCriterion class
-
-    /**
-     * Test the matchLambda method.
-     */
-    @Test
-    public void testMatchLambdaMethod() {
-        Criterion matchLambda = Criteria.matchLambda(lambda1);
-        LambdaCriterion lambdaCriterion =
-                checkAndConvert(matchLambda,
-                        Criterion.Type.OCH_SIGID,
-                        LambdaCriterion.class);
-        assertThat(lambdaCriterion.lambda(), is(equalTo(lambda1)));
-    }
-
-    /**
-     * Test the equals() method of the LambdaCriterion class.
-     */
-    @Test
-    public void testLambdaCriterionEquals() {
-        new EqualsTester()
-                .addEqualityGroup(matchLambda1, sameAsMatchLambda1)
-                .addEqualityGroup(matchLambda2)
-                .testEquals();
-    }
-
     @Test
     public void testIndexedLambdaCriterionEquals() {
         new EqualsTester()
@@ -1109,30 +1070,4 @@ public class CriteriaTest {
                 .addEqualityGroup(matchOchSignalType2)
                 .testEquals();
     }
-
-    // OpticalSignalTypeCriterion class
-
-    /**
-     * Test the matchOpticalSignalType method.
-     */
-    @Test
-    public void testMatchOpticalSignalTypeMethod() {
-        Criterion matchLambda = Criteria.matchOpticalSignalType(signalLambda1);
-        OpticalSignalTypeCriterion opticalSignalTypeCriterion =
-                checkAndConvert(matchLambda,
-                        Criterion.Type.OCH_SIGTYPE,
-                        OpticalSignalTypeCriterion.class);
-        assertThat(opticalSignalTypeCriterion.signalType(), is(equalTo(signalLambda1)));
-    }
-
-    /**
-     * Test the equals() method of the OpticalSignalTypeCriterion class.
-     */
-    @Test
-    public void testOpticalSignalTypeCriterionEquals() {
-        new EqualsTester()
-                .addEqualityGroup(matchSignalLambda1, sameAsMatchSignalLambda1)
-                .addEqualityGroup(matchSignalLambda2)
-                .testEquals();
-    }
 }
index ac4ecff..d42e22f 100644 (file)
@@ -17,7 +17,6 @@ package org.onosproject.net.intent;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableSet;
-
 import org.onlab.util.Bandwidth;
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
@@ -37,6 +36,9 @@ import org.onosproject.net.flow.criteria.Criterion.Type;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.flow.instructions.Instructions.MetadataInstruction;
+import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceRequest;
+import org.onosproject.net.resource.ResourceType;
 import org.onosproject.net.resource.link.BandwidthResource;
 import org.onosproject.net.resource.link.BandwidthResourceRequest;
 import org.onosproject.net.resource.link.LambdaResource;
@@ -48,13 +50,10 @@ import org.onosproject.net.resource.link.LinkResourceRequest;
 import org.onosproject.net.resource.link.LinkResourceService;
 import org.onosproject.net.resource.link.MplsLabel;
 import org.onosproject.net.resource.link.MplsLabelResourceAllocation;
-import org.onosproject.net.resource.ResourceAllocation;
-import org.onosproject.net.resource.ResourceRequest;
-import org.onosproject.net.resource.ResourceType;
 import org.onosproject.net.topology.DefaultTopologyEdge;
 import org.onosproject.net.topology.DefaultTopologyVertex;
 import org.onosproject.net.topology.LinkWeight;
-import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.PathServiceAdapter;
 import org.onosproject.net.topology.TopologyVertex;
 import org.onosproject.store.Timestamp;
 
@@ -68,9 +67,7 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
-import static org.onosproject.net.NetTestTools.createPath;
-import static org.onosproject.net.NetTestTools.did;
-import static org.onosproject.net.NetTestTools.link;
+import static org.onosproject.net.NetTestTools.*;
 
 /**
  * Common mocks used by the intent framework tests.
@@ -134,7 +131,7 @@ public class IntentTestsMocks {
     /**
      * Mock path service for creating paths within the test.
      */
-    public static class MockPathService implements PathService {
+    public static class MockPathService extends PathServiceAdapter {
 
         final String[] pathHops;
         final String[] reversePathHops;
@@ -424,7 +421,7 @@ public class IntentTestsMocks {
             }
             final MockFlowRule other = (MockFlowRule) obj;
             return Objects.equals(this.timestamp, other.timestamp) &&
-                   this.id == other.id;
+                    this.id == other.id;
         }
 
         @Override
@@ -450,7 +447,7 @@ public class IntentTestsMocks {
 
         public MockIntent(Long number) {
             super(NetTestTools.APP_ID, null, Collections.emptyList(),
-                    Intent.DEFAULT_INTENT_PRIORITY);
+                  Intent.DEFAULT_INTENT_PRIORITY);
             this.number = number;
         }
 
index c438659..2993ce6 100644 (file)
@@ -19,7 +19,6 @@ import org.onosproject.core.ApplicationId;
 import org.onosproject.net.flow.TrafficSelector;
 
 import java.util.List;
-import java.util.Map;
 
 /**
  * Test adapter for packet service.
@@ -34,7 +33,7 @@ public class PacketServiceAdapter implements PacketService {
     }
 
     @Override
-    public Map<Integer, PacketProcessor> getProcessors() {
+    public List<PacketProcessorEntry> getProcessors() {
         return null;
     }
 
index 8b0f8f0..f395849 100644 (file)
@@ -41,7 +41,7 @@ public class DefaultGraphDescriptionTest {
     @Test
     public void basics() {
         DefaultGraphDescription desc =
-                new DefaultGraphDescription(4321L, ImmutableSet.of(DEV1, DEV2, DEV3),
+                new DefaultGraphDescription(4321L, System.currentTimeMillis(), ImmutableSet.of(DEV1, DEV2, DEV3),
                                             ImmutableSet.of(L1, L2));
         assertEquals("incorrect time", 4321L, desc.timestamp());
         assertEquals("incorrect vertex count", 3, desc.vertexes().size());
@@ -50,7 +50,7 @@ public class DefaultGraphDescriptionTest {
 
     @Test
     public void missingVertex() {
-        GraphDescription desc = new DefaultGraphDescription(4321L,
+        GraphDescription desc = new DefaultGraphDescription(4321L, System.currentTimeMillis(),
                                                             ImmutableSet.of(DEV1, DEV3),
                                                             ImmutableSet.of(L1, L2));
         assertEquals("incorrect time", 4321L, desc.timestamp());
diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/PathServiceAdapter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/PathServiceAdapter.java
new file mode 100644 (file)
index 0000000..6a8e586
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.topology;
+
+import org.onosproject.net.DisjointPath;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test adapter for path service.
+ */
+public class PathServiceAdapter implements PathService {
+    @Override
+    public Set<Path> getPaths(ElementId src, ElementId dst) {
+        return null;
+    }
+
+    @Override
+    public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
+        return null;
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst) {
+        return null;
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, LinkWeight weight) {
+        return null;
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
+                                              Map<Link, Object> riskProfile) {
+        return null;
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
+                                              LinkWeight weight,
+                                              Map<Link, Object> riskProfile) {
+        return null;
+    }
+}
index 07e6784..72cc67d 100644 (file)
@@ -17,9 +17,11 @@ package org.onosproject.net.topology;
 
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
 
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -89,4 +91,28 @@ public class TopologyServiceAdapter implements TopologyService {
     public void removeListener(TopologyListener listener) {
     }
 
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
+        return null;
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+                                              DeviceId dst, LinkWeight weight) {
+        return null;
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                              Map<Link, Object> riskProfile) {
+        return null;
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+                                              DeviceId dst, LinkWeight weight,
+                                              Map<Link, Object> riskProfile) {
+        return null;
+    }
+
 }
index 01209be..8c577df 100644 (file)
@@ -47,6 +47,16 @@ public final class TestAtomicCounter implements AtomicCounter {
         return value.addAndGet(delta);
     }
 
+    @Override
+    public void set(long value) {
+        this.value.set(value);
+    }
+
+    @Override
+    public boolean compareAndSet(long expectedValue, long updateValue) {
+        return value.compareAndSet(expectedValue, updateValue);
+    }
+
     @Override
     public long get() {
         return value.get();
index eb53152..3433b3b 100644 (file)
@@ -16,6 +16,7 @@
 package org.onosproject.codec.impl;
 
 import com.google.common.collect.ImmutableSet;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -36,6 +37,7 @@ import org.onosproject.net.Port;
 import org.onosproject.net.driver.Driver;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -99,6 +101,7 @@ public class CodecManager implements CodecService {
         registerCodec(Driver.class, new DriverCodec());
         registerCodec(GroupBucket.class, new GroupBucketCodec());
         registerCodec(Load.class, new LoadCodec());
+        registerCodec(TableStatisticsEntry.class, new TableStatisticsEntryCodec());
         log.info("Started");
     }
 
index d61cf38..d12e4ad 100644 (file)
@@ -215,6 +215,7 @@ public final class EncodeInstructionCodecHelper {
                 break;
 
             case DROP:
+            case NOACTION:
                 break;
 
             case L0MODIFICATION:
diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java
new file mode 100644 (file)
index 0000000..7834ceb
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.codec.impl;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.flow.TableStatisticsEntry;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Table statistics entry JSON codec.
+ */
+public final class TableStatisticsEntryCodec extends JsonCodec<TableStatisticsEntry> {
+
+    @Override
+    public ObjectNode encode(TableStatisticsEntry entry, CodecContext context) {
+        checkNotNull(entry, "Table Statistics entry cannot be null");
+
+        final ObjectNode result = context.mapper().createObjectNode()
+                .put("tableId", entry.tableId())
+                .put("deviceId", entry.deviceId().toString())
+                .put("activeEntries", entry.activeFlowEntries())
+                .put("packetsLookedUp", entry.packetsLookedup())
+                .put("packetsMatched", entry.packetsMatched());
+
+        return result;
+    }
+
+}
+
index bdf7d73..3c5c540 100644 (file)
@@ -23,14 +23,19 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.ImmutableSetMultimap.Builder;
 import org.onlab.graph.DijkstraGraphSearch;
+import org.onlab.graph.DisjointPathPair;
 import org.onlab.graph.GraphPathSearch;
 import org.onlab.graph.GraphPathSearch.Result;
+import org.onlab.graph.SRLGGraphSearch;
+import org.onlab.graph.SuurballeGraphSearch;
 import org.onlab.graph.TarjanGraphSearch;
 import org.onlab.graph.TarjanGraphSearch.SCCResult;
 import org.onosproject.net.AbstractModel;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultDisjointPath;
 import org.onosproject.net.DefaultPath;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
 import org.onosproject.net.provider.ProviderId;
@@ -45,10 +50,11 @@ import org.onosproject.net.topology.TopologyEdge;
 import org.onosproject.net.topology.TopologyGraph;
 import org.onosproject.net.topology.TopologyVertex;
 
-import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkArgument;
@@ -67,6 +73,9 @@ public class DefaultTopology extends AbstractModel implements Topology {
 
     private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA = new DijkstraGraphSearch<>();
     private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN = new TarjanGraphSearch<>();
+    private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE =
+            new SuurballeGraphSearch<>();
+
 
     private final long time;
     private final long creationTime;
@@ -315,15 +324,135 @@ public class DefaultTopology extends AbstractModel implements Topology {
         return builder.build();
     }
 
+    /**
+     * /**
+     * Returns the set of pre-computed shortest disjoint path pairs between source and
+     * destination devices.
+     *
+     * @param src source device
+     * @param dst destination device
+     * @return set of shortest disjoint path pairs
+     */
+    public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst) {
+        return getDisjointPaths(src, dst, (LinkWeight) null);
+    }
+
+    /**
+     * Computes on-demand the set of shortest disjoint path pairs between source and
+     * destination devices.
+     *
+     * @param src    source device
+     * @param dst    destination device
+     * @param weight link weight function
+     * @return set of disjoint shortest path pairs
+     */
+    public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
+        final DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
+        final DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
+        Set<TopologyVertex> vertices = graph.getVertexes();
+        if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
+            // src or dst not part of the current graph
+            return ImmutableSet.of();
+        }
+
+        GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
+                SUURBALLE.search(graph, srcV, dstV, weight, ALL_PATHS);
+        ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
+        for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
+            builder.add(networkDisjointPath((org.onlab.graph.DisjointPathPair<TopologyVertex, TopologyEdge>) path));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Computes on-demand the set of shortest disjoint risk groups path pairs between source and
+     * destination devices.
+     *
+     * @param src         source device
+     * @param dst         destination device
+     * @param weight      edge weight object
+     * @param riskProfile map representing risk groups for each edge
+     * @return set of shortest disjoint paths
+     */
+    private Set<DisjointPath> disjointPaths(DeviceId src, DeviceId dst, LinkWeight weight,
+                                                Map<TopologyEdge, Object> riskProfile) {
+        DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
+        DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
+
+        Set<TopologyVertex> vertices = graph.getVertexes();
+        if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
+            // src or dst not part of the current graph
+            return ImmutableSet.of();
+        }
+
+        SRLGGraphSearch<TopologyVertex, TopologyEdge> srlg = new SRLGGraphSearch<>(riskProfile);
+        GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
+                srlg.search(graph, srcV, dstV, weight, ALL_PATHS);
+        ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
+        for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
+            builder.add(networkDisjointPath((org.onlab.graph.DisjointPathPair<TopologyVertex, TopologyEdge>) path));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Computes on-demand the set of shortest disjoint risk groups path pairs between source and
+     * destination devices.
+     *
+     * @param src         source device
+     * @param dst         destination device
+     * @param weight      edge weight object
+     * @param riskProfile map representing risk groups for each link
+     * @return set of shortest disjoint paths
+     */
+    public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, LinkWeight weight,
+                                              Map<Link, Object> riskProfile) {
+        Map<TopologyEdge, Object> riskProfile2 = new HashMap<>();
+        for (Link l : riskProfile.keySet()) {
+            riskProfile2.put(new TopologyEdge() {
+                Link cur = l;
+
+                public Link link() {
+                    return cur;
+                }
+
+                public TopologyVertex src() {
+                    return () -> src;
+                }
+
+                public TopologyVertex dst() {
+                    return () -> dst;
+                }
+            }, riskProfile.get(l));
+        }
+        return disjointPaths(src, dst, weight, riskProfile2);
+    }
+
+    /**
+     * Computes on-demand the set of shortest disjoint risk groups path pairs between source and
+     * destination devices.
+     *
+     * @param src         source device
+     * @param dst         destination device
+     * @param riskProfile map representing risk groups for each link
+     * @return set of shortest disjoint paths
+     */
+    public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, Map<Link, Object> riskProfile) {
+        return getDisjointPaths(src, dst, null, riskProfile);
+    }
+
     // Converts graph path to a network path with the same cost.
     private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
-        List<Link> links = new ArrayList<>();
-        for (TopologyEdge edge : path.edges()) {
-            links.add(edge.link());
-        }
+        List<Link> links = path.edges().stream().map(TopologyEdge::link).collect(Collectors.toList());
         return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
     }
 
+    private DisjointPath networkDisjointPath(DisjointPathPair<TopologyVertex, TopologyEdge> path) {
+        return new DefaultDisjointPath(CORE_PROVIDER_ID,
+                                       (DefaultPath) networkPath(path.primary()),
+                                       (DefaultPath) networkPath(path.secondary()));
+    }
+
     // Searches for SCC clusters in the network topology graph using Tarjan
     // algorithm.
     private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
@@ -334,6 +463,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
     private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
         ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
         SCCResult<TopologyVertex, TopologyEdge> results = clusterResults.get();
+
         // Extract both vertexes and edges from the results; the lists form
         // pairs along the same index.
         List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes();
index 6bf4680..54e1146 100644 (file)
@@ -38,10 +38,8 @@ import org.onosproject.net.flow.criteria.Criterion;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.onlab.junit.TestUtils.getField;
-import static org.onlab.junit.TestUtils.setField;
 import static org.onosproject.codec.impl.CriterionJsonMatcher.matchesCriterion;
 
 /**
@@ -429,17 +427,4 @@ public class CriterionCodecTest {
         ObjectNode result = criterionCodec.encode(criterion, context);
         assertThat(result, matchesCriterion(criterion));
     }
-
-    /**
-     * Tests that an unimplemented criterion type only returns the type and
-     * no other data.
-     */
-    @Test
-    public void matchUnknownTypeTest() throws Exception {
-        Criterion criterion = Criteria.matchOpticalSignalType((byte) 250);
-        setField(criterion, "type", Criterion.Type.UNASSIGNED_40);
-        ObjectNode result = criterionCodec.encode(criterion, context);
-        assertThat(result.get("type").textValue(), is(criterion.type().toString()));
-        assertThat(result.size(), is(1));
-    }
 }
index 72081e6..c3cdca0 100644 (file)
@@ -416,6 +416,8 @@ public final class InstructionJsonMatcher extends TypeSafeDiagnosingMatcher<Json
                                                     description);
         } else if (instruction instanceof ModMplsLabelInstruction) {
             return matchModMplsLabelInstruction(jsonInstruction, description);
+        } else if (instruction instanceof NoActionInstruction) {
+            return true;
         }
 
         return false;
index 4d435cf..ef0f332 100644 (file)
@@ -73,7 +73,7 @@ public class DefaultTopologyTest {
                              link("1", 3, "4", 3), link("4", 3, "1", 3),
                              link("3", 4, "4", 4), link("4", 4, "3", 4));
         GraphDescription graphDescription =
-                new DefaultGraphDescription(now, devices, links);
+                new DefaultGraphDescription(now, System.currentTimeMillis(), devices, links);
 
         dt = new DefaultTopology(PID, graphDescription);
         assertEquals("incorrect supplier", PID, dt.providerId());
index c8c92aa..bed32a2 100644 (file)
@@ -20,8 +20,10 @@ import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.RemovalListener;
 import com.google.common.cache.RemovalNotification;
 import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.SettableFuture;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -44,6 +46,7 @@ import org.onosproject.net.flow.FlowRuleEvent.Type;
 import org.onosproject.net.flow.FlowRuleStore;
 import org.onosproject.net.flow.FlowRuleStoreDelegate;
 import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.store.AbstractStore;
 import org.slf4j.Logger;
 
@@ -79,6 +82,9 @@ public class SimpleFlowRuleStore
     private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, List<StoredFlowEntry>>>
             flowEntries = new ConcurrentHashMap<>();
 
+    private final ConcurrentMap<DeviceId, List<TableStatisticsEntry>>
+            deviceTableStats = new ConcurrentHashMap<>();
+
     private final AtomicInteger localBatchIdGen = new AtomicInteger();
 
     // TODO: make this configurable
@@ -97,6 +103,7 @@ public class SimpleFlowRuleStore
 
     @Deactivate
     public void deactivate() {
+        deviceTableStats.clear();
         flowEntries.clear();
         log.info("Stopped");
     }
@@ -315,4 +322,20 @@ public class SimpleFlowRuleStore
             }
         }
     }
+
+    @Override
+    public FlowRuleEvent updateTableStatistics(DeviceId deviceId,
+                                               List<TableStatisticsEntry> tableStats) {
+        deviceTableStats.put(deviceId, tableStats);
+        return null;
+    }
+
+    @Override
+    public Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId) {
+        List<TableStatisticsEntry> tableStats = deviceTableStats.get(deviceId);
+        if (tableStats == null) {
+            return Collections.emptyList();
+        }
+        return ImmutableList.copyOf(tableStats);
+    }
 }
index 264d049..72ec98c 100644 (file)
@@ -159,6 +159,11 @@ public class SimpleHostStore
         }
     }
 
+    @Override
+    public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
+        return null;
+    }
+
     @Override
     public int getHostCount() {
         return hosts.size();
index f835926..7dda12c 100644 (file)
  */
 package org.onosproject.store.trivial;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketEvent;
 import org.onosproject.net.packet.PacketEvent.Type;
@@ -27,7 +30,9 @@ import org.onosproject.net.packet.PacketStore;
 import org.onosproject.net.packet.PacketStoreDelegate;
 import org.onosproject.store.AbstractStore;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -39,7 +44,7 @@ public class SimplePacketStore
         extends AbstractStore<PacketEvent, PacketStoreDelegate>
         implements PacketStore {
 
-    private Set<PacketRequest> requests = Sets.newConcurrentHashSet();
+    private Map<TrafficSelector, Set<PacketRequest>> requests = Maps.newConcurrentMap();
 
     @Override
     public void emit(OutboundPacket packet) {
@@ -47,18 +52,50 @@ public class SimplePacketStore
     }
 
     @Override
-    public boolean requestPackets(PacketRequest request) {
-        return requests.add(request);
+    public void requestPackets(PacketRequest request) {
+        requests.compute(request.selector(), (s, existingRequests) -> {
+            if (existingRequests == null) {
+                return ImmutableSet.of(request);
+            } else if (!existingRequests.contains(request)) {
+                if (delegate != null) {
+                    delegate.requestPackets(request);
+                }
+                return ImmutableSet.<PacketRequest>builder()
+                        .addAll(existingRequests)
+                        .add(request)
+                        .build();
+            } else {
+                return existingRequests;
+            }
+        });
     }
 
     @Override
-    public boolean cancelPackets(PacketRequest request) {
-        return requests.remove(request);
+    public void cancelPackets(PacketRequest request) {
+        requests.computeIfPresent(request.selector(), (s, existingRequests) -> {
+            if (existingRequests.contains(request)) {
+                HashSet<PacketRequest> newRequests = Sets.newHashSet(existingRequests);
+                newRequests.remove(request);
+                if (newRequests.size() > 0) {
+                    return ImmutableSet.copyOf(newRequests);
+                } else {
+                    if (delegate != null) {
+                        delegate.cancelPackets(request);
+                    }
+                    return null;
+                }
+            } else {
+                return existingRequests;
+            }
+        });
     }
 
     @Override
     public List<PacketRequest> existingRequests() {
-        return ImmutableList.copyOf(requests);
+        List<PacketRequest> list = Lists.newArrayList();
+        requests.values().forEach(list::addAll);
+        list.sort((o1, o2) -> o1.priority().priorityValue() - o2.priority().priorityValue());
+        return list;
     }
 
 }
index 6a89c01..29c5d84 100644 (file)
@@ -23,6 +23,7 @@ import org.onosproject.common.DefaultTopology;
 import org.onosproject.event.Event;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
 import org.onosproject.net.provider.ProviderId;
@@ -39,6 +40,7 @@ import org.onosproject.store.AbstractStore;
 import org.slf4j.Logger;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import static org.slf4j.LoggerFactory.getLogger;
@@ -113,6 +115,29 @@ public class SimpleTopologyStore
         return defaultTopology(topology).getPaths(src, dst, weight);
     }
 
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
+        return defaultTopology(topology).getDisjointPaths(src, dst);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                              LinkWeight weight) {
+        return defaultTopology(topology).getDisjointPaths(src, dst, weight);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                                  Map<Link, Object> riskProfile) {
+        return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                                  LinkWeight weight, Map<Link, Object> riskProfile) {
+        return defaultTopology(topology).getDisjointPaths(src, dst, weight, riskProfile);
+    }
+
     @Override
     public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
         return defaultTopology(topology).isInfrastructure(connectPoint);
index 9ea0007..c5d3126 100644 (file)
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <version>${project.version}</version>
+            <artifactId>onos-cli</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-core-common</artifactId>
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigLoader.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigLoader.java
new file mode 100644 (file)
index 0000000..6678db2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cfg.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.cfg.ComponentConfigService;
+import org.slf4j.Logger;
+
+import java.io.File;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Component responsible for automatically loading configuration file from
+ * configuration directory.
+ */
+@Component(immediate = true)
+public class ComponentConfigLoader {
+
+    private static final String CFG_JSON = "../config/component-cfg.json";
+    static File cfgFile = new File(CFG_JSON);
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService configService;
+
+    private ObjectNode root;
+
+    @Activate
+    protected void activate() {
+        this.loadConfigs();
+        log.info("Started");
+    }
+
+    // Loads the configurations for each component from the file in
+    // ../config/component-cfg.json, using the preSetProperty method.
+    private void loadConfigs() {
+        try {
+            if (cfgFile.exists()) {
+                root = (ObjectNode) new ObjectMapper().readTree(cfgFile);
+                root.fieldNames().
+                        forEachRemaining(component -> root.path(component).fieldNames()
+                                .forEachRemaining(k -> configService
+                                        .preSetProperty(component, k,
+                                                        root.path(component).path(k)
+                                                                .asText())));
+                log.info("Loaded initial component configuration from {}", cfgFile);
+            }
+        } catch (Exception e) {
+            log.warn("Unable to load initial component configuration from {}",
+                     cfgFile, e);
+        }
+    }
+}
index 1933ee5..b3b22c7 100644 (file)
@@ -61,6 +61,9 @@ public class ComponentConfigManager implements ComponentConfigService {
 
     private static final String COMPONENT_NULL = "Component name cannot be null";
     private static final String PROPERTY_NULL = "Property name cannot be null";
+    private static final String COMPONENT_MISSING = "Component %s is not registered";
+    private static final String PROPERTY_MISSING = "Property %s does not exist for %s";
+
 
     //Symbolic constants for use with the accumulator
     private static final int MAX_ITEMS = 100;
@@ -160,6 +163,22 @@ public class ComponentConfigManager implements ComponentConfigService {
 
         checkNotNull(componentName, COMPONENT_NULL);
         checkNotNull(name, PROPERTY_NULL);
+
+        checkArgument(properties.containsKey(componentName),
+                      COMPONENT_MISSING, componentName);
+        checkArgument(properties.get(componentName).containsKey(name),
+                      PROPERTY_MISSING, name, componentName);
+        store.setProperty(componentName, name, value);
+    }
+
+    @Override
+    public void preSetProperty(String componentName, String name, String value) {
+
+        checkPermission(CONFIG_WRITE);
+
+        checkNotNull(componentName, COMPONENT_NULL);
+        checkNotNull(name, PROPERTY_NULL);
+
         store.setProperty(componentName, name, value);
     }
 
@@ -169,6 +188,11 @@ public class ComponentConfigManager implements ComponentConfigService {
 
         checkNotNull(componentName, COMPONENT_NULL);
         checkNotNull(name, PROPERTY_NULL);
+
+        checkArgument(properties.containsKey(componentName),
+                      COMPONENT_MISSING, componentName);
+        checkArgument(properties.get(componentName).containsKey(name),
+                      PROPERTY_MISSING, name, componentName);
         store.unsetProperty(componentName, name);
     }
 
index 8a441f6..f4d560a 100644 (file)
@@ -24,7 +24,6 @@ import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.SharedExecutors;
-import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.ApplicationIdStore;
@@ -38,6 +37,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Set;
@@ -87,9 +90,15 @@ public class CoreManager implements CoreService {
     public void activate() {
         registerApplication(CORE_APP_NAME);
         cfgService.registerProperties(getClass());
-        List<String> versionLines = Tools.slurp(VERSION_FILE);
-        if (versionLines != null && !versionLines.isEmpty()) {
-            version = Version.version(versionLines.get(0));
+        try {
+            Path path = Paths.get(VERSION_FILE.getPath());
+            List<String> versionLines = Files.readAllLines(path);
+            if (versionLines != null && !versionLines.isEmpty()) {
+                version = Version.version(versionLines.get(0));
+            }
+        } catch (IOException e) {
+            // version file not found, using default
+            log.trace("Version file not found", e);
         }
     }
 
index 5cd96ca..db484ee 100644 (file)
@@ -96,7 +96,7 @@ public class NetworkConfigManager
         configClasses.put(identifier(configFactory), configFactory.configClass());
 
         SubjectFactory subjectFactory = configFactory.subjectFactory();
-        subjectClasses.putIfAbsent(subjectFactory.subjectKey(), subjectFactory);
+        subjectClasses.putIfAbsent(subjectFactory.subjectClassKey(), subjectFactory);
         subjectClassKeys.putIfAbsent(subjectFactory.subjectClass(), subjectFactory);
 
         store.addConfigFactory(configFactory);
@@ -145,8 +145,8 @@ public class NetworkConfigManager
     }
 
     @Override
-    public SubjectFactory getSubjectFactory(String subjectKey) {
-        return subjectClasses.get(subjectKey);
+    public SubjectFactory getSubjectFactory(String subjectClassKey) {
+        return subjectClasses.get(subjectClassKey);
     }
 
     @Override
@@ -155,8 +155,8 @@ public class NetworkConfigManager
     }
 
     @Override
-    public Class<? extends Config> getConfigClass(String subjectKey, String configKey) {
-        return configClasses.get(new ConfigIdentifier(subjectKey, configKey));
+    public Class<? extends Config> getConfigClass(String subjectClassKey, String configKey) {
+        return configClasses.get(new ConfigIdentifier(subjectClassKey, configKey));
     }
 
     @Override
@@ -255,7 +255,7 @@ public class NetworkConfigManager
     }
 
     private static ConfigIdentifier identifier(ConfigFactory factory) {
-        return new ConfigIdentifier(factory.subjectFactory().subjectKey(), factory.configKey());
+        return new ConfigIdentifier(factory.subjectFactory().subjectClassKey(), factory.configKey());
     }
 
     static final class ConfigIdentifier {
index 7900d18..fa90eb6 100644 (file)
  */
 package org.onosproject.net.device.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import org.onosproject.net.config.ConfigOperator;
-import org.onosproject.net.config.basics.BasicDeviceConfig;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.Device;
 import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.config.ConfigOperator;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
 import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.device.DeviceDescription;
 import org.slf4j.Logger;
 
 import java.util.Objects;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Implementations of merge policies for various sources of device configuration
  * information. This includes applications, provides, and network configurations.
@@ -46,7 +46,7 @@ public final class BasicDeviceOperator implements ConfigOperator {
      * Generates a DeviceDescription containing fields from a DeviceDescription and
      * a DeviceConfig.
      *
-     * @param bdc the device config entity from network config
+     * @param bdc   the device config entity from network config
      * @param descr a DeviceDescription
      * @return DeviceDescription based on both sources
      */
@@ -70,7 +70,7 @@ public final class BasicDeviceOperator implements ConfigOperator {
      * Generates an annotation from an existing annotation and DeviceConfig.
      *
      * @param bdc the device config entity from network config
-     * @param an the annotation
+     * @param an  the annotation
      * @return annotation combining both sources
      */
     public static SparseAnnotations combine(BasicDeviceConfig bdc, SparseAnnotations an) {
@@ -93,6 +93,9 @@ public final class BasicDeviceOperator implements ConfigOperator {
         if (bdc.owner() != null) {
             newBuilder.set(AnnotationKeys.OWNER, bdc.owner());
         }
+        if (bdc.managementAddress() != null) {
+            newBuilder.set(AnnotationKeys.MANAGEMENT_ADDRESS, bdc.managementAddress());
+        }
         DefaultAnnotations newAnnotations = newBuilder.build();
         return DefaultAnnotations.union(an, newAnnotations);
     }
index b0b3abe..e35dc0c 100644 (file)
  */
 package org.onosproject.net.device.impl;
 
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.Futures;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.net.MastershipRole.NONE;
+import static org.onosproject.net.MastershipRole.STANDBY;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.DEVICE_READ;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -26,12 +44,6 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.NodeId;
-import org.onosproject.net.provider.AbstractListenerProviderRegistry;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.config.basics.BasicDeviceConfig;
-import org.onosproject.net.config.basics.OpticalPortConfig;
 import org.onosproject.mastership.MastershipEvent;
 import org.onosproject.mastership.MastershipListener;
 import org.onosproject.mastership.MastershipService;
@@ -44,6 +56,11 @@ import org.onosproject.net.DeviceId;
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
+import org.onosproject.net.config.basics.OpticalPortConfig;
 import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.device.DefaultPortDescription;
 import org.onosproject.net.device.DeviceAdminService;
@@ -58,27 +75,11 @@ import org.onosproject.net.device.DeviceStore;
 import org.onosproject.net.device.DeviceStoreDelegate;
 import org.onosproject.net.device.PortDescription;
 import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
 import org.onosproject.net.provider.AbstractProviderService;
 import org.slf4j.Logger;
 
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.net.MastershipRole.*;
-import static org.onosproject.security.AppGuard.checkPermission;
-import static org.slf4j.LoggerFactory.getLogger;
-import static org.onosproject.security.AppPermission.Type.*;
+import com.google.common.util.concurrent.Futures;
 
 /**
  * Provides implementation of the device SB &amp; NB APIs.
@@ -347,11 +348,15 @@ public class DeviceManager
             log.info("Device {} disconnected from this node", deviceId);
 
             List<Port> ports = store.getPorts(deviceId);
-            List<PortDescription> descs = Lists.newArrayList();
-            ports.forEach(port ->
-                                  descs.add(new DefaultPortDescription(port.number(),
-                                                                       false, port.type(),
-                                                                       port.portSpeed())));
+            final Device device = getDevice(deviceId);
+
+            List<PortDescription> descs = ports.stream().map(
+              port -> (!(Device.Type.ROADM.equals(device.type()))) ?
+                  new DefaultPortDescription(port.number(), false,
+                          port.type(), port.portSpeed()) :
+                      OpticalPortOperator.descriptionOf(port, false)
+                 ).collect(Collectors.toList());
+
             store.updatePorts(this.provider().id(), deviceId, descs);
             try {
                 if (mastershipService.isLocalMaster(deviceId)) {
@@ -430,6 +435,12 @@ public class DeviceManager
                           portDescription);
                 return;
             }
+            final Device device = getDevice(deviceId);
+            if ((Device.Type.ROADM.equals(device.type()))) {
+                Port port = getPort(deviceId, portDescription.portNumber());
+                portDescription = OpticalPortOperator.descriptionOf(port, portDescription.isEnabled());
+            }
+
             portDescription = consolidate(deviceId, portDescription);
             final DeviceEvent event = store.updatePortStatus(this.provider().id(),
                                                              deviceId, portDescription);
index b2fd02c..8f2bda0 100644 (file)
@@ -151,8 +151,25 @@ public final class OpticalPortOperator implements ConfigOperator {
      */
     public static PortDescription descriptionOf(Port port) {
         checkNotNull(port, "Must supply non-null Port");
+        final boolean isUp = port.isEnabled();
+        return descriptionOfPort(port, isUp);
+    }
+
+    /**
+     * Returns a description built from an existing port and reported status.
+     *
+     * @param port
+     * @param isEnabled
+     * @return a PortDescription based on the port
+     */
+    static PortDescription descriptionOf(Port port, boolean isEnabled) {
+        checkNotNull(port, "Must supply non-null Port");
+        final boolean isup = isEnabled;
+        return descriptionOfPort(port, isup);
+    }
+
+    private static PortDescription descriptionOfPort(Port port, final boolean isup) {
         final PortNumber ptn = port.number();
-        final boolean isup = port.isEnabled();
         final SparseAnnotations an = (SparseAnnotations) port.annotations();
         switch (port.type()) {
             case OMS:
index a1d046c..5958d1f 100644 (file)
@@ -22,6 +22,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -58,6 +59,7 @@ import org.onosproject.net.flow.FlowRuleProviderService;
 import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.flow.FlowRuleStore;
 import org.onosproject.net.flow.FlowRuleStoreDelegate;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.provider.AbstractProviderService;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
@@ -388,6 +390,16 @@ public class FlowRuleManager
 
         @Override
         public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+            pushFlowMetricsInternal(deviceId, flowEntries, true);
+        }
+
+        @Override
+        public void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+            pushFlowMetricsInternal(deviceId, flowEntries, false);
+        }
+
+        private void pushFlowMetricsInternal(DeviceId deviceId, Iterable<FlowEntry> flowEntries,
+                                             boolean useMissingFlow) {
             Map<FlowEntry, FlowEntry> storedRules = Maps.newHashMap();
             store.getFlowEntries(deviceId).forEach(f -> storedRules.put(f, f));
 
@@ -415,17 +427,20 @@ public class FlowRuleManager
                     continue;
                 }
             }
-            for (FlowEntry rule : storedRules.keySet()) {
-                try {
-                    // there are rules in the store that aren't on the switch
-                    log.debug("Adding rule in store, but not on switch {}", rule);
-                    flowMissing(rule);
-                } catch (Exception e) {
-                    log.debug("Can't add missing flow rule {}", e.getMessage());
-                    continue;
+
+            // DO NOT reinstall
+            if (useMissingFlow) {
+                for (FlowEntry rule : storedRules.keySet()) {
+                    try {
+                        // there are rules in the store that aren't on the switch
+                        log.debug("Adding rule in store, but not on switch {}", rule);
+                        flowMissing(rule);
+                    } catch (Exception e) {
+                        log.debug("Can't add missing flow rule {}", e.getMessage());
+                        continue;
+                    }
                 }
             }
-
         }
 
         @Override
@@ -435,6 +450,12 @@ public class FlowRuleManager
                     operation
             ));
         }
+
+        @Override
+        public void pushTableStatistics(DeviceId deviceId,
+                                          List<TableStatisticsEntry> tableStats) {
+            store.updateTableStatistics(deviceId, tableStats);
+        }
     }
 
     // Store delegate to re-post events emitted from the store.
@@ -590,4 +611,10 @@ public class FlowRuleManager
         }
 
     }
+
+    @Override
+    public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) {
+        checkPermission(FLOWRULE_READ);
+        return store.getTableStatistics(deviceId);
+    }
 }
index 43f346b..1473f33 100644 (file)
@@ -236,6 +236,16 @@ public class HostManager
                 post(event);
             }
         }
+
+        @Override
+        public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
+            checkNotNull(hostId, HOST_ID_NULL);
+            checkValidity();
+            HostEvent event = store.removeIp(hostId, ipAddress);
+            if (event != null) {
+                post(event);
+            }
+        }
     }
 
     // Store delegate to re-post events emitted from the store.
index 44f8cbf..4dc93a5 100644 (file)
@@ -84,6 +84,7 @@ public class HostMonitor implements TimerTask {
      * @param hostManager host manager used to look up host information and
      * probe existing hosts
      * @param interfaceService interface service for interface information
+     * @param edgePortService  edge port service
      */
     public HostMonitor(PacketService packetService, HostManager hostManager,
                        InterfaceService interfaceService,
index d7fa322..417627a 100644 (file)
@@ -196,7 +196,7 @@ public class IntentCleanup implements Runnable, IntentListener {
                 service.withdraw(intentData.intent());
                 break;
             default:
-                log.warn("Trying to resubmit pending intent {} in state {} with request {}",
+                log.warn("Failed to resubmit pending intent {} in state {} with request {}",
                          intentData.key(), intentData.state(), intentData.request());
                 break;
         }
index 4c828e7..baa3bf4 100644 (file)
@@ -256,15 +256,16 @@ public class IntentManager
             submit(intent);
         }
 
-        // If required, compile all currently failed intents.
-        for (Intent intent : getIntents()) {
-            IntentState state = getIntentState(intent.key());
-            if ((compileAllFailed && RECOMPILE.contains(state))
-                    || intentAllowsPartialFailure(intent)) {
-                if (WITHDRAW.contains(state)) {
-                    withdraw(intent);
-                } else {
-                    submit(intent);
+        if (compileAllFailed) {
+            // If required, compile all currently failed intents.
+            for (Intent intent : getIntents()) {
+                IntentState state = getIntentState(intent.key());
+                if (RECOMPILE.contains(state) || intentAllowsPartialFailure(intent)) {
+                    if (WITHDRAW.contains(state)) {
+                        withdraw(intent);
+                    } else {
+                        submit(intent);
+                    }
                 }
             }
         }
index 5710ace..5ebc812 100644 (file)
@@ -16,8 +16,8 @@
 package org.onosproject.net.intent.impl;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -61,7 +61,6 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -91,8 +90,6 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
 
     private final Logger log = getLogger(getClass());
 
-    private final ConcurrentMap<Key, Intent> intents = Maps.newConcurrentMap();
-
     private final SetMultimap<LinkKey, Key> intentsByLink =
             //TODO this could be slow as a point of synchronization
             synchronizedSetMultimap(HashMultimap.<LinkKey, Key>create());
@@ -378,7 +375,12 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
             }
 
             // TODO should we recompile on available==true?
-            delegate.triggerCompile(intentsByDevice.get(id), available);
+
+            final ImmutableSet<Key> snapshot;
+            synchronized (intentsByDevice) {
+                snapshot = ImmutableSet.copyOf(intentsByDevice.get(id));
+            }
+            delegate.triggerCompile(snapshot, available);
         }
     }
 
@@ -415,9 +417,17 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
         @Override
         public void event(HostEvent event) {
             HostId id = event.subject().id();
-            HostEvent.Type type = event.type();
-            boolean available = (type == HostEvent.Type.HOST_ADDED);
-            executorService.execute(new DeviceAvailabilityHandler(id, available));
+            switch (event.type()) {
+                case HOST_ADDED:
+                case HOST_MOVED:
+                case HOST_REMOVED:
+                    executorService.execute(new DeviceAvailabilityHandler(id, false));
+                    break;
+                case HOST_UPDATED:
+                default:
+                    // DO NOTHING
+                    break;
+            }
         }
     }
 
index 99f58df..c6eb7c5 100644 (file)
  */
 package org.onosproject.net.intent.impl.compiler;
 
-import com.google.common.collect.Sets;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Property;
@@ -50,7 +48,10 @@ import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.OpticalCircuitIntent;
 import org.onosproject.net.intent.OpticalConnectivityIntent;
 import org.onosproject.net.intent.impl.IntentCompilationException;
-import org.onosproject.net.resource.device.DeviceResourceService;
+import org.onosproject.net.newresource.ResourceAllocation;
+import org.onosproject.net.newresource.ResourcePath;
+import org.onosproject.net.newresource.ResourceService;
+import org.onosproject.net.resource.device.IntentSetMultimap;
 import org.onosproject.net.resource.link.LinkResourceAllocations;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
@@ -60,6 +61,7 @@ import java.util.Collections;
 import java.util.Dictionary;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -67,7 +69,8 @@ import static com.google.common.base.Preconditions.checkArgument;
 /**
  * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}.
  */
-@Component(immediate = true)
+// For now, remove component designation until dependency on the new resource manager is available.
+// @Component(immediate = true)
 public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> {
 
     private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class);
@@ -92,7 +95,10 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceResourceService deviceResourceService;
+    protected ResourceService resourceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentSetMultimap intentSetMultimap;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentService intentService;
@@ -153,7 +159,10 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
         log.debug("Compiling optical circuit intent between {} and {}", src, dst);
 
         // Reserve OduClt ports
-        if (!deviceResourceService.requestPorts(Sets.newHashSet(srcPort, dstPort), intent)) {
+        ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port());
+        ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port());
+        List<ResourceAllocation> allocation = resourceService.allocate(intent.id(), srcPortPath, dstPortPath);
+        if (allocation.isEmpty()) {
             throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
         }
 
@@ -199,7 +208,7 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
         circuitIntent = new FlowRuleIntent(appId, rules, intent.resources());
 
         // Save circuit to connectivity intent mapping
-        deviceResourceService.requestMapping(connIntent.id(), intent.id());
+        intentSetMultimap.allocateMapping(connIntent.id(), intent.id());
         intents.add(circuitIntent);
 
         return intents;
@@ -209,16 +218,15 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
      * Checks if current allocations on given resource can satisfy request.
      * If the resource is null, return true.
      *
-     * @param request the intent making the request
      * @param resource the resource on which to map the intent
      * @return true if the resource can accept the request, false otherwise
      */
-    private boolean isAvailable(Intent request, IntentId resource) {
+    private boolean isAvailable(IntentId resource) {
         if (resource == null) {
             return true;
         }
 
-        Set<IntentId> mapping = deviceResourceService.getMapping(resource);
+        Set<IntentId> mapping = intentSetMultimap.getMapping(resource);
 
         if (mapping == null) {
             return true;
@@ -271,7 +279,7 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
                 continue;
             }
 
-            if (isAvailable(circuitIntent, connIntent.id())) {
+            if (isAvailable(connIntent.id())) {
                 return connIntent;
             }
         }
@@ -296,14 +304,19 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
         return null;
     }
 
-    private OchPort findAvailableOchPort(ConnectPoint oduPort, OpticalCircuitIntent circuitIntent) {
+    private OchPort findAvailableOchPort(ConnectPoint oduPort) {
         // First see if the port mappings are constrained
         ConnectPoint ochCP = staticPort(oduPort);
 
         if (ochCP != null) {
             OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
-            IntentId intentId = deviceResourceService.getAllocations(ochPort);
-            if (isAvailable(circuitIntent, intentId)) {
+            Optional<IntentId> intentId =
+                    resourceService.getResourceAllocation(new ResourcePath(ochCP.deviceId(), ochCP.port()))
+                            .map(ResourceAllocation::consumer)
+                            .filter(x -> x instanceof IntentId)
+                            .map(x -> (IntentId) x);
+
+            if (isAvailable(intentId.orElse(null))) {
                 return ochPort;
             }
         }
@@ -316,8 +329,12 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
                 continue;
             }
 
-            IntentId intentId = deviceResourceService.getAllocations(port);
-            if (isAvailable(circuitIntent, intentId)) {
+            Optional<IntentId> intentId =
+                    resourceService.getResourceAllocation(new ResourcePath(oduPort.deviceId(), port.number()))
+                            .map(ResourceAllocation::consumer)
+                            .filter(x -> x instanceof IntentId)
+                            .map(x -> (IntentId) x);
+            if (isAvailable(intentId.orElse(null))) {
                 return (OchPort) port;
             }
         }
@@ -327,12 +344,12 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
 
     private Pair<OchPort, OchPort> findPorts(OpticalCircuitIntent intent) {
 
-        OchPort srcPort = findAvailableOchPort(intent.getSrc(), intent);
+        OchPort srcPort = findAvailableOchPort(intent.getSrc());
         if (srcPort == null) {
             return null;
         }
 
-        OchPort dstPort = findAvailableOchPort(intent.getDst(), intent);
+        OchPort dstPort = findAvailableOchPort(intent.getDst());
         if (dstPort == null) {
             return null;
         }
index 05a20f9..eb5b4af 100644 (file)
@@ -16,9 +16,7 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -40,9 +38,9 @@ import org.onosproject.net.intent.IntentExtensionService;
 import org.onosproject.net.intent.OpticalConnectivityIntent;
 import org.onosproject.net.intent.OpticalPathIntent;
 import org.onosproject.net.intent.impl.IntentCompilationException;
-import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.newresource.ResourcePath;
+import org.onosproject.net.newresource.ResourceService;
 import org.onosproject.net.resource.ResourceType;
-import org.onosproject.net.resource.device.DeviceResourceService;
 import org.onosproject.net.resource.link.DefaultLinkResourceRequest;
 import org.onosproject.net.resource.link.LambdaResource;
 import org.onosproject.net.resource.link.LambdaResourceAllocation;
@@ -57,13 +55,15 @@ import org.slf4j.LoggerFactory;
 
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
 /**
  * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}.
  */
-@Component(immediate = true)
+// For now, remove component designation until dependency on the new resource manager is available.
+// @Component(immediate = true)
 public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> {
 
     protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class);
@@ -78,10 +78,10 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected LinkResourceService linkResourceService;
+    protected ResourceService resourceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceResourceService deviceResourceService;
+    protected LinkResourceService linkResourceService;
 
     @Activate
     public void activate() {
@@ -108,7 +108,11 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
         log.debug("Compiling optical connectivity intent between {} and {}", src, dst);
 
         // Reserve OCh ports
-        if (!deviceResourceService.requestPorts(ImmutableSet.of(srcPort, dstPort), intent)) {
+        ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port());
+        ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port());
+        List<org.onosproject.net.newresource.ResourceAllocation> allocation =
+                resourceService.allocate(intent.id(), srcPortPath, dstPortPath);
+        if (allocation.isEmpty()) {
             throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
         }
 
@@ -161,7 +165,7 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
         }
 
         // Release port allocations if unsuccessful
-        deviceResourceService.releasePorts(intent.id());
+        resourceService.release(intent.id());
 
         throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
     }
@@ -174,15 +178,12 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
      * @return lambda allocated to the given path
      */
     private LambdaResourceAllocation getWavelength(Path path, LinkResourceAllocations linkAllocs) {
-        for (Link link : path.links()) {
-            for (ResourceAllocation alloc : linkAllocs.getResourceAllocation(link)) {
-                if (alloc.type() == ResourceType.LAMBDA) {
-                    return (LambdaResourceAllocation) alloc;
-                }
-            }
-        }
-
-        return null;
+        return path.links().stream()
+                .flatMap(x -> linkAllocs.getResourceAllocation(x).stream())
+                .filter(x -> x.type() == ResourceType.LAMBDA)
+                .findFirst()
+                .map(x -> (LambdaResourceAllocation) x)
+                .orElse(null);
     }
 
     /**
@@ -215,23 +216,23 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
             return false;
         }
 
-        LambdaResource lambda = null;
+        List<LambdaResource> lambdas = path.links().stream()
+                .flatMap(x -> allocations.getResourceAllocation(x).stream())
+                .filter(x -> x.type() == ResourceType.LAMBDA)
+                .map(x -> ((LambdaResourceAllocation) x).lambda())
+                .collect(Collectors.toList());
 
-        for (Link link : path.links()) {
-            for (ResourceAllocation alloc : allocations.getResourceAllocation(link)) {
-                if (alloc.type() == ResourceType.LAMBDA) {
-                    LambdaResource nextLambda = ((LambdaResourceAllocation) alloc).lambda();
-                    if (nextLambda == null) {
-                        return false;
-                    }
-                    if (lambda == null) {
-                        lambda = nextLambda;
-                        continue;
-                    }
-                    if (!lambda.equals(nextLambda)) {
-                        return false;
-                    }
-                }
+        LambdaResource lambda = null;
+        for (LambdaResource nextLambda: lambdas) {
+            if (nextLambda == null) {
+                return false;
+            }
+            if (lambda == null) {
+                lambda = nextLambda;
+                continue;
+            }
+            if (!lambda.equals(nextLambda)) {
+                return false;
             }
         }
 
index ca9ae5c..2cc45e7 100644 (file)
@@ -39,7 +39,6 @@ import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.IntentExtensionService;
 import org.onosproject.net.intent.OpticalPathIntent;
 import org.onosproject.net.resource.link.LinkResourceAllocations;
-import org.onosproject.net.resource.link.LinkResourceService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,9 +58,6 @@ public class OpticalPathIntentCompiler implements IntentCompiler<OpticalPathInte
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected LinkResourceService resourceService;
-
     private ApplicationId appId;
 
     @Activate
index 2cd1a2e..5226967 100644 (file)
@@ -90,6 +90,14 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ
         return release(ImmutableList.copyOf(allocations));
     }
 
+    @Override
+    public Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource) {
+        checkNotNull(resource);
+
+        Optional<ResourceConsumer> consumer = store.getConsumer(resource);
+        return consumer.map(x -> new ResourceAllocation(resource, x));
+    }
+
     @Override
     public <T> Collection<ResourceAllocation> getResourceAllocations(ResourcePath parent, Class<T> cls) {
         checkNotNull(parent);
index a0bc693..8e87a07 100644 (file)
@@ -15,7 +15,8 @@
  */
 package org.onosproject.net.packet.impl;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -43,6 +44,7 @@ import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketEvent;
 import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketProcessorEntry;
 import org.onosproject.net.packet.PacketProvider;
 import org.onosproject.net.packet.PacketProviderRegistry;
 import org.onosproject.net.packet.PacketProviderService;
@@ -55,8 +57,6 @@ import org.onosproject.net.provider.AbstractProviderService;
 import org.slf4j.Logger;
 
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -102,18 +102,18 @@ public class PacketManager
 
     private final DeviceListener deviceListener = new InternalDeviceListener();
 
-    private final Map<Integer, PacketProcessor> processors = new ConcurrentHashMap<>();
+    private final List<ProcessorEntry> processors = Lists.newCopyOnWriteArrayList();
 
     private ApplicationId appId;
 
     @Activate
     public void activate() {
         eventHandlingExecutor = Executors.newSingleThreadExecutor(
-                        groupedThreads("onos/net/packet", "event-handler"));
+                groupedThreads("onos/net/packet", "event-handler"));
         appId = coreService.getAppId(CoreService.CORE_APP_NAME);
         store.setDelegate(delegate);
         deviceService.addListener(deviceListener);
-        // TODO: Should we request packets for all existing devices? I believe we should.
+        store.existingRequests().forEach(this::pushToAllDevices);
         log.info("Started");
     }
 
@@ -129,19 +129,35 @@ public class PacketManager
     public void addProcessor(PacketProcessor processor, int priority) {
         checkPermission(PACKET_EVENT);
         checkNotNull(processor, "Processor cannot be null");
-        processors.put(priority, processor);
+        ProcessorEntry entry = new ProcessorEntry(processor, priority);
+
+        // Insert the new processor according to its priority.
+        int i = 0;
+        for (; i < processors.size(); i++) {
+            if (priority < processors.get(i).priority()) {
+                break;
+            }
+        }
+        processors.add(i, entry);
     }
 
     @Override
     public void removeProcessor(PacketProcessor processor) {
         checkPermission(PACKET_EVENT);
         checkNotNull(processor, "Processor cannot be null");
-        processors.values().remove(processor);
+
+        // Remove the processor entry.
+        for (int i = 0; i < processors.size(); i++) {
+            if (processors.get(i).processor() == processor) {
+                processors.remove(i);
+                break;
+            }
+        }
     }
 
     @Override
-    public Map<Integer, PacketProcessor> getProcessors() {
-        return ImmutableMap.copyOf(processors);
+    public List<PacketProcessorEntry> getProcessors() {
+        return ImmutableList.copyOf(processors);
     }
 
     @Override
@@ -152,9 +168,7 @@ public class PacketManager
         checkNotNull(appId, "Application ID cannot be null");
 
         PacketRequest request = new DefaultPacketRequest(selector, priority, appId);
-        if (store.requestPackets(request)) {
-            pushToAllDevices(request);
-        }
+        store.requestPackets(request);
     }
 
     @Override
@@ -165,9 +179,7 @@ public class PacketManager
         checkNotNull(appId, "Application ID cannot be null");
 
         PacketRequest request = new DefaultPacketRequest(selector, priority, appId);
-        if (store.cancelPackets(request)) {
-            removeFromAllDevices(request);
-        }
+        store.cancelPackets(request);
     }
 
     @Override
@@ -175,6 +187,18 @@ public class PacketManager
         return store.existingRequests();
     }
 
+    /**
+     * Pushes all rules to the specified device.
+     *
+     * @param device device on which to install packet request flows
+     */
+    private void pushRulesToDevice(Device device) {
+        log.debug("Pushing packet requests to device {}", device.id());
+        for (PacketRequest request : store.existingRequests()) {
+            pushRule(device, request);
+        }
+    }
+
     /**
      * Pushes a packet request flow rule to all devices.
      *
@@ -187,16 +211,13 @@ public class PacketManager
         }
     }
 
-
     /**
      * Removes packet request flow rule from all devices.
      *
      * @param request the packet request
      */
     private void removeFromAllDevices(PacketRequest request) {
-        for (Device device : deviceService.getDevices()) {
-            removeRule(device, request);
-        }
+        deviceService.getAvailableDevices().forEach(d -> removeRule(d, request));
     }
 
     /**
@@ -232,7 +253,6 @@ public class PacketManager
         if (!device.type().equals(Device.Type.SWITCH)) {
             return;
         }
-
         ForwardingObjective forwarding = createBuilder(request)
                 .remove(new ObjectiveContext() {
                     @Override
@@ -241,7 +261,6 @@ public class PacketManager
                                  request, device.id(), error);
                     }
                 });
-
         objectiveService.forward(device.id(), forwarding);
     }
 
@@ -263,12 +282,10 @@ public class PacketManager
     }
 
     private void localEmit(OutboundPacket packet) {
-        final Device device = deviceService.getDevice(packet.sendThrough());
-
+        Device device = deviceService.getDevice(packet.sendThrough());
         if (device == null) {
             return;
         }
-
         PacketProvider packetProvider = getProvider(device.providerId());
         if (packetProvider != null) {
             packetProvider.emit(packet);
@@ -280,7 +297,9 @@ public class PacketManager
         return new InternalPacketProviderService(provider);
     }
 
-    // Personalized packet provider service issued to the supplied provider.
+    /**
+     * Personalized packet provider service issued to the supplied provider.
+     */
     private class InternalPacketProviderService
             extends AbstractProviderService<PacketProvider>
             implements PacketProviderService {
@@ -292,8 +311,10 @@ public class PacketManager
         @Override
         public void processPacket(PacketContext context) {
             // TODO filter packets sent to processors based on registrations
-            for (PacketProcessor processor : processors.values()) {
-                processor.process(context);
+            for (ProcessorEntry entry : processors) {
+                long start = System.nanoTime();
+                entry.processor().process(context);
+                entry.addNanos(System.nanoTime() - start);
             }
         }
 
@@ -307,6 +328,16 @@ public class PacketManager
         public void notify(PacketEvent event) {
             localEmit(event.subject());
         }
+
+        @Override
+        public void requestPackets(PacketRequest request) {
+            pushToAllDevices(request);
+        }
+
+        @Override
+        public void cancelPackets(PacketRequest request) {
+            removeFromAllDevices(request);
+        }
     }
 
     /**
@@ -319,17 +350,14 @@ public class PacketManager
                 try {
                     Device device = event.subject();
                     switch (event.type()) {
-                    case DEVICE_ADDED:
-                    case DEVICE_AVAILABILITY_CHANGED:
-                        if (deviceService.isAvailable(event.subject().id())) {
-                            log.debug("Pushing packet requests to device {}", event.subject().id());
-                            for (PacketRequest request : store.existingRequests()) {
-                                pushRule(device, request);
+                        case DEVICE_ADDED:
+                        case DEVICE_AVAILABILITY_CHANGED:
+                            if (deviceService.isAvailable(event.subject().id())) {
+                                pushRulesToDevice(device);
                             }
-                        }
-                        break;
-                    default:
-                        break;
+                            break;
+                        default:
+                            break;
                     }
                 } catch (Exception e) {
                     log.warn("Failed to process {}", event, e);
@@ -338,4 +366,48 @@ public class PacketManager
         }
     }
 
+    /**
+     * Entity for tracking stats for a packet processor.
+     */
+    private class ProcessorEntry implements PacketProcessorEntry {
+        private final PacketProcessor processor;
+        private final int priority;
+        private long invocations = 0;
+        private long nanos = 0;
+
+        public ProcessorEntry(PacketProcessor processor, int priority) {
+            this.processor = processor;
+            this.priority = priority;
+        }
+
+        @Override
+        public PacketProcessor processor() {
+            return processor;
+        }
+
+        @Override
+        public int priority() {
+            return priority;
+        }
+
+        @Override
+        public long invocations() {
+            return invocations;
+        }
+
+        @Override
+        public long totalNanos() {
+            return nanos;
+        }
+
+        @Override
+        public long averageNanos() {
+            return invocations > 0 ? nanos / invocations : 0;
+        }
+
+        void addNanos(long nanos) {
+            this.nanos += nanos;
+            this.invocations++;
+        }
+    }
 }
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/FlowStatisticManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/FlowStatisticManager.java
new file mode 100644 (file)
index 0000000..6515ef3
--- /dev/null
@@ -0,0 +1,634 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.net.statistic.impl;\r
+\r
+import com.google.common.base.MoreObjects;\r
+import com.google.common.base.Predicate;\r
+import com.google.common.collect.ImmutableSet;\r
+import org.apache.felix.scr.annotations.Activate;\r
+import org.apache.felix.scr.annotations.Component;\r
+import org.apache.felix.scr.annotations.Deactivate;\r
+import org.apache.felix.scr.annotations.Reference;\r
+import org.apache.felix.scr.annotations.ReferenceCardinality;\r
+import org.apache.felix.scr.annotations.Service;\r
+import org.onosproject.cli.Comparators;\r
+import org.onosproject.net.ConnectPoint;\r
+import org.onosproject.net.Device;\r
+import org.onosproject.net.Port;\r
+import org.onosproject.net.PortNumber;\r
+import org.onosproject.net.device.DeviceService;\r
+import org.onosproject.net.flow.DefaultTypedFlowEntry;\r
+import org.onosproject.net.flow.FlowEntry;\r
+import org.onosproject.net.flow.FlowRule;\r
+import org.onosproject.net.flow.FlowRuleEvent;\r
+import org.onosproject.net.flow.FlowRuleListener;\r
+import org.onosproject.net.flow.FlowRuleService;\r
+import org.onosproject.net.flow.TypedStoredFlowEntry;\r
+import org.onosproject.net.flow.instructions.Instruction;\r
+import org.onosproject.net.statistic.DefaultLoad;\r
+import org.onosproject.net.statistic.FlowStatisticService;\r
+import org.onosproject.net.statistic.Load;\r
+import org.onosproject.net.statistic.FlowStatisticStore;\r
+import org.onosproject.net.statistic.SummaryFlowEntryWithLoad;\r
+import org.onosproject.net.statistic.TypedFlowEntryWithLoad;\r
+\r
+import org.slf4j.Logger;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Objects;\r
+import java.util.Set;\r
+import java.util.TreeMap;\r
+import java.util.stream.Collectors;\r
+\r
+import static com.google.common.base.Preconditions.checkNotNull;\r
+import static org.onosproject.security.AppGuard.checkPermission;\r
+import static org.slf4j.LoggerFactory.getLogger;\r
+import static org.onosproject.security.AppPermission.Type.*;\r
+\r
+/**\r
+ * Provides an implementation of the Flow Statistic Service.\r
+ */\r
+@Component(immediate = true, enabled = true)\r
+@Service\r
+public class FlowStatisticManager implements FlowStatisticService {\r
+    private final Logger log = getLogger(getClass());\r
+\r
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)\r
+    protected FlowRuleService flowRuleService;\r
+\r
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)\r
+    protected FlowStatisticStore flowStatisticStore;\r
+\r
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)\r
+    protected DeviceService deviceService;\r
+\r
+    private final InternalFlowRuleStatsListener frListener = new InternalFlowRuleStatsListener();\r
+\r
+    @Activate\r
+    public void activate() {\r
+        flowRuleService.addListener(frListener);\r
+        log.info("Started");\r
+    }\r
+\r
+    @Deactivate\r
+    public void deactivate() {\r
+        flowRuleService.removeListener(frListener);\r
+        log.info("Stopped");\r
+    }\r
+\r
+    @Override\r
+    public Map<ConnectPoint, SummaryFlowEntryWithLoad> loadSummary(Device device) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR);\r
+\r
+        if (device == null) {\r
+            return summaryLoad;\r
+        }\r
+\r
+        List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id()));\r
+\r
+        for (Port port : ports) {\r
+            ConnectPoint cp = new ConnectPoint(device.id(), port.number());\r
+            SummaryFlowEntryWithLoad sfe = loadSummaryPortInternal(cp);\r
+            summaryLoad.put(cp, sfe);\r
+        }\r
+\r
+        return summaryLoad;\r
+    }\r
+\r
+    @Override\r
+    public SummaryFlowEntryWithLoad loadSummary(Device device, PortNumber pNumber) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        ConnectPoint cp = new ConnectPoint(device.id(), pNumber);\r
+        return loadSummaryPortInternal(cp);\r
+    }\r
+\r
+    @Override\r
+    public Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadAllByType(Device device,\r
+                                                                  TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                                  Instruction.Type instType) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        Map<ConnectPoint, List<TypedFlowEntryWithLoad>> allLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR);\r
+\r
+        if (device == null) {\r
+            return allLoad;\r
+        }\r
+\r
+        List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id()));\r
+\r
+        for (Port port : ports) {\r
+            ConnectPoint cp = new ConnectPoint(device.id(), port.number());\r
+            List<TypedFlowEntryWithLoad> tfel = loadAllPortInternal(cp, liveType, instType);\r
+            allLoad.put(cp, tfel);\r
+        }\r
+\r
+        return allLoad;\r
+    }\r
+\r
+    @Override\r
+    public List<TypedFlowEntryWithLoad> loadAllByType(Device device, PortNumber pNumber,\r
+                                               TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                               Instruction.Type instType) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        ConnectPoint cp = new ConnectPoint(device.id(), pNumber);\r
+        return loadAllPortInternal(cp, liveType, instType);\r
+    }\r
+\r
+    @Override\r
+    public Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadTopnByType(Device device,\r
+                                                                   TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                                   Instruction.Type instType,\r
+                                                                   int topn) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        Map<ConnectPoint, List<TypedFlowEntryWithLoad>> allLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR);\r
+\r
+        if (device == null) {\r
+            return allLoad;\r
+        }\r
+\r
+        List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id()));\r
+\r
+        for (Port port : ports) {\r
+            ConnectPoint cp = new ConnectPoint(device.id(), port.number());\r
+            List<TypedFlowEntryWithLoad> tfel = loadTopnPortInternal(cp, liveType, instType, topn);\r
+            allLoad.put(cp, tfel);\r
+        }\r
+\r
+        return allLoad;\r
+    }\r
+\r
+    @Override\r
+    public List<TypedFlowEntryWithLoad> loadTopnByType(Device device, PortNumber pNumber,\r
+                                                TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                Instruction.Type instType,\r
+                                                int topn) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        ConnectPoint cp = new ConnectPoint(device.id(), pNumber);\r
+        return loadTopnPortInternal(cp, liveType, instType, topn);\r
+    }\r
+\r
+    private SummaryFlowEntryWithLoad loadSummaryPortInternal(ConnectPoint cp) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        Set<FlowEntry> currentStats;\r
+        Set<FlowEntry> previousStats;\r
+\r
+        TypedStatistics typedStatistics;\r
+        synchronized (flowStatisticStore) {\r
+             currentStats = flowStatisticStore.getCurrentFlowStatistic(cp);\r
+            if (currentStats == null) {\r
+                return new SummaryFlowEntryWithLoad(cp, new DefaultLoad());\r
+            }\r
+            previousStats = flowStatisticStore.getPreviousFlowStatistic(cp);\r
+            if (previousStats == null) {\r
+                return new SummaryFlowEntryWithLoad(cp, new DefaultLoad());\r
+            }\r
+            // copy to local flow entry\r
+            typedStatistics = new TypedStatistics(currentStats, previousStats);\r
+\r
+            // Check for validity of this stats data\r
+            checkLoadValidity(currentStats, previousStats);\r
+        }\r
+\r
+        // current and previous set is not empty!\r
+        Set<FlowEntry> currentSet = typedStatistics.current();\r
+        Set<FlowEntry> previousSet = typedStatistics.previous();\r
+        Load totalLoad = new DefaultLoad(aggregateBytesSet(currentSet), aggregateBytesSet(previousSet),\r
+                TypedFlowEntryWithLoad.avgPollInterval());\r
+\r
+        Map<FlowRule, TypedStoredFlowEntry> currentMap;\r
+        Map<FlowRule, TypedStoredFlowEntry> previousMap;\r
+\r
+        currentMap = typedStatistics.currentImmediate();\r
+        previousMap = typedStatistics.previousImmediate();\r
+        Load immediateLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),\r
+                TypedFlowEntryWithLoad.shortPollInterval());\r
+\r
+        currentMap = typedStatistics.currentShort();\r
+        previousMap = typedStatistics.previousShort();\r
+        Load shortLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),\r
+                TypedFlowEntryWithLoad.shortPollInterval());\r
+\r
+        currentMap = typedStatistics.currentMid();\r
+        previousMap = typedStatistics.previousMid();\r
+        Load midLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),\r
+                TypedFlowEntryWithLoad.midPollInterval());\r
+\r
+        currentMap = typedStatistics.currentLong();\r
+        previousMap = typedStatistics.previousLong();\r
+        Load longLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),\r
+                TypedFlowEntryWithLoad.longPollInterval());\r
+\r
+        currentMap = typedStatistics.currentUnknown();\r
+        previousMap = typedStatistics.previousUnknown();\r
+        Load unknownLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),\r
+                TypedFlowEntryWithLoad.avgPollInterval());\r
+\r
+        return new SummaryFlowEntryWithLoad(cp, totalLoad, immediateLoad, shortLoad, midLoad, longLoad, unknownLoad);\r
+    }\r
+\r
+    private List<TypedFlowEntryWithLoad> loadAllPortInternal(ConnectPoint cp,\r
+                                                             TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                             Instruction.Type instType) {\r
+        checkPermission(STATISTIC_READ);\r
+\r
+        List<TypedFlowEntryWithLoad> retTFEL = new ArrayList<>();\r
+\r
+        Set<FlowEntry> currentStats;\r
+        Set<FlowEntry> previousStats;\r
+\r
+        TypedStatistics typedStatistics;\r
+        synchronized (flowStatisticStore) {\r
+            currentStats = flowStatisticStore.getCurrentFlowStatistic(cp);\r
+            if (currentStats == null) {\r
+                return retTFEL;\r
+            }\r
+            previousStats = flowStatisticStore.getPreviousFlowStatistic(cp);\r
+            if (previousStats == null) {\r
+                return retTFEL;\r
+            }\r
+            // copy to local flow entry set\r
+            typedStatistics = new TypedStatistics(currentStats, previousStats);\r
+\r
+            // Check for validity of this stats data\r
+            checkLoadValidity(currentStats, previousStats);\r
+        }\r
+\r
+        // current and previous set is not empty!\r
+        boolean isAllLiveType = (liveType == null ? true : false); // null is all live type\r
+        boolean isAllInstType = (instType == null ? true : false); // null is all inst type\r
+\r
+        Map<FlowRule, TypedStoredFlowEntry> currentMap;\r
+        Map<FlowRule, TypedStoredFlowEntry> previousMap;\r
+\r
+        if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW) {\r
+            currentMap = typedStatistics.currentImmediate();\r
+            previousMap = typedStatistics.previousImmediate();\r
+\r
+            List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,\r
+                    isAllInstType, instType, TypedFlowEntryWithLoad.shortPollInterval());\r
+            if (fel.size() > 0) {\r
+                retTFEL.addAll(fel);\r
+            }\r
+        }\r
+\r
+        if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW) {\r
+            currentMap = typedStatistics.currentShort();\r
+            previousMap = typedStatistics.previousShort();\r
+\r
+            List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,\r
+                    isAllInstType, instType, TypedFlowEntryWithLoad.shortPollInterval());\r
+            if (fel.size() > 0) {\r
+                retTFEL.addAll(fel);\r
+            }\r
+        }\r
+\r
+        if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.MID_FLOW) {\r
+            currentMap = typedStatistics.currentMid();\r
+            previousMap = typedStatistics.previousMid();\r
+\r
+            List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,\r
+                    isAllInstType, instType, TypedFlowEntryWithLoad.midPollInterval());\r
+            if (fel.size() > 0) {\r
+                retTFEL.addAll(fel);\r
+            }\r
+        }\r
+\r
+        if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.LONG_FLOW) {\r
+            currentMap = typedStatistics.currentLong();\r
+            previousMap = typedStatistics.previousLong();\r
+\r
+            List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,\r
+                    isAllInstType, instType, TypedFlowEntryWithLoad.longPollInterval());\r
+            if (fel.size() > 0) {\r
+                retTFEL.addAll(fel);\r
+            }\r
+        }\r
+\r
+        if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW) {\r
+            currentMap = typedStatistics.currentUnknown();\r
+            previousMap = typedStatistics.previousUnknown();\r
+\r
+            List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,\r
+                    isAllInstType, instType, TypedFlowEntryWithLoad.avgPollInterval());\r
+            if (fel.size() > 0) {\r
+                retTFEL.addAll(fel);\r
+            }\r
+        }\r
+\r
+        return retTFEL;\r
+    }\r
+\r
+    private List<TypedFlowEntryWithLoad> typedFlowEntryLoadByInstInternal(ConnectPoint cp,\r
+                                                                      Map<FlowRule, TypedStoredFlowEntry> currentMap,\r
+                                                                      Map<FlowRule, TypedStoredFlowEntry> previousMap,\r
+                                                                      boolean isAllInstType,\r
+                                                                      Instruction.Type instType,\r
+                                                                      int liveTypePollInterval) {\r
+        List<TypedFlowEntryWithLoad> fel = new ArrayList<>();\r
+\r
+        for (TypedStoredFlowEntry tfe : currentMap.values()) {\r
+            if (isAllInstType ||\r
+                    tfe.treatment().allInstructions().stream().\r
+                            filter(i -> i.type() == instType).\r
+                            findAny().isPresent()) {\r
+                long currentBytes = tfe.bytes();\r
+                long previousBytes = previousMap.getOrDefault(tfe, new DefaultTypedFlowEntry((FlowRule) tfe)).bytes();\r
+                Load fLoad = new DefaultLoad(currentBytes, previousBytes, liveTypePollInterval);\r
+                fel.add(new TypedFlowEntryWithLoad(cp, tfe, fLoad));\r
+            }\r
+        }\r
+\r
+        return fel;\r
+    }\r
+\r
+    private List<TypedFlowEntryWithLoad> loadTopnPortInternal(ConnectPoint cp,\r
+                                                             TypedStoredFlowEntry.FlowLiveType liveType,\r
+                                                             Instruction.Type instType,\r
+                                                             int topn) {\r
+        List<TypedFlowEntryWithLoad> fel = loadAllPortInternal(cp, liveType, instType);\r
+\r
+        // Sort with descending order of load\r
+        List<TypedFlowEntryWithLoad> tfel =\r
+                fel.stream().sorted(Comparators.TYPEFLOWENTRY_WITHLOAD_COMPARATOR).\r
+                        limit(topn).collect(Collectors.toList());\r
+\r
+        return tfel;\r
+    }\r
+\r
+    private long aggregateBytesSet(Set<FlowEntry> setFE) {\r
+        return setFE.stream().mapToLong(FlowEntry::bytes).sum();\r
+    }\r
+\r
+    private long aggregateBytesMap(Map<FlowRule, TypedStoredFlowEntry> mapFE) {\r
+        return mapFE.values().stream().mapToLong(FlowEntry::bytes).sum();\r
+    }\r
+\r
+    /**\r
+     * Internal data class holding two set of typed flow entries.\r
+     */\r
+    private static class TypedStatistics {\r
+        private final ImmutableSet<FlowEntry> currentAll;\r
+        private final ImmutableSet<FlowEntry> previousAll;\r
+\r
+        private final Map<FlowRule, TypedStoredFlowEntry> currentImmediate = new HashMap<>();\r
+        private final Map<FlowRule, TypedStoredFlowEntry> previousImmediate = new HashMap<>();\r
+\r
+        private final Map<FlowRule, TypedStoredFlowEntry> currentShort = new HashMap<>();\r
+        private final Map<FlowRule, TypedStoredFlowEntry> previousShort = new HashMap<>();\r
+\r
+        private final Map<FlowRule, TypedStoredFlowEntry> currentMid = new HashMap<>();\r
+        private final Map<FlowRule, TypedStoredFlowEntry> previousMid = new HashMap<>();\r
+\r
+        private final Map<FlowRule, TypedStoredFlowEntry> currentLong = new HashMap<>();\r
+        private final Map<FlowRule, TypedStoredFlowEntry> previousLong = new HashMap<>();\r
+\r
+        private final Map<FlowRule, TypedStoredFlowEntry> currentUnknown = new HashMap<>();\r
+        private final Map<FlowRule, TypedStoredFlowEntry> previousUnknown = new HashMap<>();\r
+\r
+        public TypedStatistics(Set<FlowEntry> current, Set<FlowEntry> previous) {\r
+            this.currentAll = ImmutableSet.copyOf(checkNotNull(current));\r
+            this.previousAll = ImmutableSet.copyOf(checkNotNull(previous));\r
+\r
+            currentAll.forEach(fe -> {\r
+                TypedStoredFlowEntry tfe = TypedFlowEntryWithLoad.newTypedStoredFlowEntry(fe);\r
+\r
+                switch (tfe.flowLiveType()) {\r
+                    case IMMEDIATE_FLOW:\r
+                        currentImmediate.put(fe, tfe);\r
+                        break;\r
+                    case SHORT_FLOW:\r
+                        currentShort.put(fe, tfe);\r
+                        break;\r
+                    case MID_FLOW:\r
+                        currentMid.put(fe, tfe);\r
+                        break;\r
+                    case LONG_FLOW:\r
+                        currentLong.put(fe, tfe);\r
+                        break;\r
+                    default:\r
+                        currentUnknown.put(fe, tfe);\r
+                        break;\r
+                }\r
+            });\r
+\r
+            previousAll.forEach(fe -> {\r
+                TypedStoredFlowEntry tfe = TypedFlowEntryWithLoad.newTypedStoredFlowEntry(fe);\r
+\r
+                switch (tfe.flowLiveType()) {\r
+                    case IMMEDIATE_FLOW:\r
+                        if (currentImmediate.containsKey(fe)) {\r
+                            previousImmediate.put(fe, tfe);\r
+                        } else if (currentShort.containsKey(fe)) {\r
+                            previousShort.put(fe, tfe);\r
+                        } else if (currentMid.containsKey(fe)) {\r
+                            previousMid.put(fe, tfe);\r
+                        } else if (currentLong.containsKey(fe)) {\r
+                            previousLong.put(fe, tfe);\r
+                        } else {\r
+                            previousUnknown.put(fe, tfe);\r
+                        }\r
+                        break;\r
+                    case SHORT_FLOW:\r
+                        if (currentShort.containsKey(fe)) {\r
+                            previousShort.put(fe, tfe);\r
+                        } else if (currentMid.containsKey(fe)) {\r
+                            previousMid.put(fe, tfe);\r
+                        } else if (currentLong.containsKey(fe)) {\r
+                            previousLong.put(fe, tfe);\r
+                        } else {\r
+                            previousUnknown.put(fe, tfe);\r
+                        }\r
+                        break;\r
+                    case MID_FLOW:\r
+                        if (currentMid.containsKey(fe)) {\r
+                            previousMid.put(fe, tfe);\r
+                        } else if (currentLong.containsKey(fe)) {\r
+                            previousLong.put(fe, tfe);\r
+                        } else {\r
+                            previousUnknown.put(fe, tfe);\r
+                        }\r
+                        break;\r
+                    case LONG_FLOW:\r
+                        if (currentLong.containsKey(fe)) {\r
+                            previousLong.put(fe, tfe);\r
+                        } else {\r
+                            previousUnknown.put(fe, tfe);\r
+                        }\r
+                        break;\r
+                    default:\r
+                        previousUnknown.put(fe, tfe);\r
+                        break;\r
+                }\r
+            });\r
+        }\r
+\r
+        /**\r
+         * Returns flow entries as the current value.\r
+         *\r
+         * @return flow entries as the current value\r
+         */\r
+        public ImmutableSet<FlowEntry> current() {\r
+            return currentAll;\r
+        }\r
+\r
+        /**\r
+         * Returns flow entries as the previous value.\r
+         *\r
+         * @return flow entries as the previous value\r
+         */\r
+        public ImmutableSet<FlowEntry> previous() {\r
+            return previousAll;\r
+        }\r
+\r
+        public Map<FlowRule, TypedStoredFlowEntry> currentImmediate() {\r
+            return currentImmediate;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> previousImmediate() {\r
+            return previousImmediate;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> currentShort() {\r
+            return currentShort;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> previousShort() {\r
+            return previousShort;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> currentMid() {\r
+            return currentMid;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> previousMid() {\r
+            return previousMid;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> currentLong() {\r
+            return currentLong;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> previousLong() {\r
+            return previousLong;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> currentUnknown() {\r
+            return currentUnknown;\r
+        }\r
+        public Map<FlowRule, TypedStoredFlowEntry> previousUnknown() {\r
+            return previousUnknown;\r
+        }\r
+\r
+        /**\r
+         * Validates values are not empty.\r
+         *\r
+         * @return false if either of the sets is empty. Otherwise, true.\r
+         */\r
+        public boolean isValid() {\r
+            return !(currentAll.isEmpty() || previousAll.isEmpty());\r
+        }\r
+\r
+        @Override\r
+        public int hashCode() {\r
+            return Objects.hash(currentAll, previousAll);\r
+        }\r
+\r
+        @Override\r
+        public boolean equals(Object obj) {\r
+            if (this == obj) {\r
+                return true;\r
+            }\r
+            if (!(obj instanceof TypedStatistics)) {\r
+                return false;\r
+            }\r
+            final TypedStatistics other = (TypedStatistics) obj;\r
+            return Objects.equals(this.currentAll, other.currentAll) &&\r
+                    Objects.equals(this.previousAll, other.previousAll);\r
+        }\r
+\r
+        @Override\r
+        public String toString() {\r
+            return MoreObjects.toStringHelper(this)\r
+                    .add("current", currentAll)\r
+                    .add("previous", previousAll)\r
+                    .toString();\r
+        }\r
+    }\r
+\r
+    private void checkLoadValidity(Set<FlowEntry> current, Set<FlowEntry> previous) {\r
+        current.stream().forEach(c -> {\r
+            FlowEntry f = previous.stream().filter(p -> c.equals(p)).\r
+                    findAny().orElse(null);\r
+            if (f != null && c.bytes() < f.bytes()) {\r
+                log.debug("FlowStatisticManager:checkLoadValidity():" +\r
+                        "Error: " + c + " :Previous bytes=" + f.bytes() +\r
+                        " is larger than current bytes=" + c.bytes() + " !!!");\r
+            }\r
+        });\r
+\r
+    }\r
+\r
+    /**\r
+     * Creates a predicate that checks the instruction type of a flow entry is the same as\r
+     * the specified instruction type.\r
+     *\r
+     * @param instType instruction type to be checked\r
+     * @return predicate\r
+     */\r
+    private static Predicate<FlowEntry> hasInstructionType(Instruction.Type instType) {\r
+        return new Predicate<FlowEntry>() {\r
+            @Override\r
+            public boolean apply(FlowEntry flowEntry) {\r
+                List<Instruction> allInstructions = flowEntry.treatment().allInstructions();\r
+\r
+                return allInstructions.stream().filter(i -> i.type() == instType).findAny().isPresent();\r
+            }\r
+        };\r
+    }\r
+\r
+    /**\r
+     * Internal flow rule event listener for FlowStatisticManager.\r
+     */\r
+    private class InternalFlowRuleStatsListener implements FlowRuleListener {\r
+\r
+        @Override\r
+        public void event(FlowRuleEvent event) {\r
+            FlowRule rule = event.subject();\r
+            switch (event.type()) {\r
+                case RULE_ADDED:\r
+                    if (rule instanceof FlowEntry) {\r
+                        flowStatisticStore.addFlowStatistic((FlowEntry) rule);\r
+                    }\r
+                    break;\r
+                case RULE_UPDATED:\r
+                    flowStatisticStore.updateFlowStatistic((FlowEntry) rule);\r
+                    break;\r
+                case RULE_ADD_REQUESTED:\r
+                    break;\r
+                case RULE_REMOVE_REQUESTED:\r
+                    break;\r
+                case RULE_REMOVED:\r
+                    flowStatisticStore.removeFlowStatistic(rule);\r
+                    break;\r
+                default:\r
+                    log.warn("Unknown flow rule event {}", event);\r
+            }\r
+        }\r
+    }\r
+}\r
index a238c7f..8347ee3 100644 (file)
@@ -27,6 +27,8 @@ import org.apache.felix.scr.annotations.Service;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultEdgeLink;
 import org.onosproject.net.DefaultPath;
+import org.onosproject.net.DisjointPath;
+import org.onosproject.net.DefaultDisjointPath;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.EdgeLink;
 import org.onosproject.net.ElementId;
@@ -46,6 +48,8 @@ import org.slf4j.Logger;
 
 import java.util.List;
 import java.util.Set;
+import java.util.Map;
+
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -128,6 +132,84 @@ public class PathManager implements PathService {
         return edgeToEdgePaths(srcEdge, dstEdge, paths);
     }
 
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst) {
+        return getDisjointPaths(src, dst, (LinkWeight) null);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, LinkWeight weight) {
+        checkNotNull(src, ELEMENT_ID_NULL);
+        checkNotNull(dst, ELEMENT_ID_NULL);
+
+        // Get the source and destination edge locations
+        EdgeLink srcEdge = getEdgeLink(src, true);
+        EdgeLink dstEdge = getEdgeLink(dst, false);
+
+        // If either edge is null, bail with no paths.
+        if (srcEdge == null || dstEdge == null) {
+            return ImmutableSet.of();
+        }
+
+        DeviceId srcDevice = srcEdge != NOT_HOST ? srcEdge.dst().deviceId() : (DeviceId) src;
+        DeviceId dstDevice = dstEdge != NOT_HOST ? dstEdge.src().deviceId() : (DeviceId) dst;
+
+        // If the source and destination are on the same edge device, there
+        // is just one path, so build it and return it.
+        if (srcDevice.equals(dstDevice)) {
+            return edgeToEdgePathsDisjoint(srcEdge, dstEdge);
+        }
+
+        // Otherwise get all paths between the source and destination edge
+        // devices.
+        Topology topology = topologyService.currentTopology();
+        Set<DisjointPath> paths = weight == null ?
+                topologyService.getDisjointPaths(topology, srcDevice, dstDevice) :
+                topologyService.getDisjointPaths(topology, srcDevice, dstDevice, weight);
+
+        return edgeToEdgePathsDisjoint(srcEdge, dstEdge, paths);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst,
+                                              Map<Link, Object> riskProfile) {
+        return getDisjointPaths(src, dst, null, riskProfile);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, LinkWeight weight,
+                                              Map<Link, Object> riskProfile) {
+        checkNotNull(src, ELEMENT_ID_NULL);
+        checkNotNull(dst, ELEMENT_ID_NULL);
+
+        // Get the source and destination edge locations
+        EdgeLink srcEdge = getEdgeLink(src, true);
+        EdgeLink dstEdge = getEdgeLink(dst, false);
+
+        // If either edge is null, bail with no paths.
+        if (srcEdge == null || dstEdge == null) {
+            return ImmutableSet.of();
+        }
+
+        DeviceId srcDevice = srcEdge != NOT_HOST ? srcEdge.dst().deviceId() : (DeviceId) src;
+        DeviceId dstDevice = dstEdge != NOT_HOST ? dstEdge.src().deviceId() : (DeviceId) dst;
+
+        // If the source and destination are on the same edge device, there
+        // is just one path, so build it and return it.
+        if (srcDevice.equals(dstDevice)) {
+            return edgeToEdgePathsDisjoint(srcEdge, dstEdge);
+        }
+
+        // Otherwise get all paths between the source and destination edge
+        // devices.
+        Topology topology = topologyService.currentTopology();
+        Set<DisjointPath> paths = weight == null ?
+                topologyService.getDisjointPaths(topology, srcDevice, dstDevice, riskProfile) :
+                topologyService.getDisjointPaths(topology, srcDevice, dstDevice, weight, riskProfile);
+
+        return edgeToEdgePathsDisjoint(srcEdge, dstEdge, paths);
+    }
+
     // Finds the host edge link if the element ID is a host id of an existing
     // host. Otherwise, if the host does not exist, it returns null and if
     // the element ID is not a host ID, returns NOT_HOST edge link.
@@ -162,6 +244,20 @@ public class PathManager implements PathService {
         return endToEndPaths;
     }
 
+    private Set<DisjointPath> edgeToEdgePathsDisjoint(EdgeLink srcLink, EdgeLink dstLink) {
+        Set<DisjointPath> endToEndPaths = Sets.newHashSetWithExpectedSize(1);
+        endToEndPaths.add(edgeToEdgePathD(srcLink, dstLink, null));
+        return endToEndPaths;
+    }
+
+    private Set<DisjointPath> edgeToEdgePathsDisjoint(EdgeLink srcLink, EdgeLink dstLink, Set<DisjointPath> paths) {
+        Set<DisjointPath> endToEndPaths = Sets.newHashSetWithExpectedSize(paths.size());
+        for (DisjointPath path : paths) {
+            endToEndPaths.add(edgeToEdgePathD(srcLink, dstLink, path));
+        }
+        return endToEndPaths;
+    }
+
     // Produces a direct edge-to-edge path.
     private Path edgeToEdgePath(EdgeLink srcLink, EdgeLink dstLink, Path path) {
         List<Link> links = Lists.newArrayListWithCapacity(2);
@@ -179,6 +275,13 @@ public class PathManager implements PathService {
         return new DefaultPath(PID, links, 2);
     }
 
+    // Produces a direct edge-to-edge path.
+    private DisjointPath edgeToEdgePathD(EdgeLink srcLink, EdgeLink dstLink, DisjointPath path) {
+        return new DefaultDisjointPath(PID, (DefaultPath) edgeToEdgePath(srcLink, dstLink, path.primary()),
+                                       (DefaultPath) edgeToEdgePath(srcLink, dstLink, path.backup()));
+    }
+
+
     // Special value for edge link to represent that this is really not an
     // edge link since the src or dst are really an infrastructure device.
     private static class NotHost extends DefaultEdgeLink implements EdgeLink {
index 04c4f1c..4425e1c 100644 (file)
@@ -21,6 +21,7 @@ import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.provider.AbstractListenerProviderRegistry;
 import org.onosproject.event.Event;
 import org.onosproject.net.ConnectPoint;
@@ -46,6 +47,7 @@ import org.slf4j.Logger;
 
 import java.util.List;
 import java.util.Set;
+import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onosproject.security.AppGuard.checkPermission;
@@ -60,7 +62,7 @@ import static org.onosproject.security.AppPermission.Type.*;
 @Service
 public class TopologyManager
         extends AbstractListenerProviderRegistry<TopologyEvent, TopologyListener,
-                                                 TopologyProvider, TopologyProviderService>
+        TopologyProvider, TopologyProviderService>
         implements TopologyService, TopologyProviderRegistry {
 
     public static final String TOPOLOGY_NULL = "Topology cannot be null";
@@ -68,6 +70,7 @@ public class TopologyManager
     private static final String CLUSTER_ID_NULL = "Cluster ID cannot be null";
     private static final String CLUSTER_NULL = "Topology cluster cannot be null";
     public static final String CONNECTION_POINT_NULL = "Connection point cannot be null";
+    public static final String LINK_WEIGHT_NULL = "Link weight cannot be null";
 
     private final Logger log = getLogger(getClass());
 
@@ -161,6 +164,44 @@ public class TopologyManager
         return store.getPaths(topology, src, dst, weight);
     }
 
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(src, DEVICE_ID_NULL);
+        checkNotNull(dst, DEVICE_ID_NULL);
+        return store.getDisjointPaths(topology, src, dst);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+                                              DeviceId dst, LinkWeight weight) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(src, DEVICE_ID_NULL);
+        checkNotNull(dst, DEVICE_ID_NULL);
+        checkNotNull(weight, LINK_WEIGHT_NULL);
+        return store.getDisjointPaths(topology, src, dst, weight);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                              Map<Link, Object> riskProfile) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(src, DEVICE_ID_NULL);
+        checkNotNull(dst, DEVICE_ID_NULL);
+        return store.getDisjointPaths(topology, src, dst, riskProfile);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src,
+                                              DeviceId dst, LinkWeight weight,
+                                              Map<Link, Object> riskProfile) {
+        checkNotNull(topology, TOPOLOGY_NULL);
+        checkNotNull(src, DEVICE_ID_NULL);
+        checkNotNull(dst, DEVICE_ID_NULL);
+        checkNotNull(weight, LINK_WEIGHT_NULL);
+        return store.getDisjointPaths(topology, src, dst, weight, riskProfile);
+    }
+
     @Override
     public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
         checkPermission(TOPOLOGY_READ);
diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ComponentConfigLoaderTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ComponentConfigLoaderTest.java
new file mode 100644 (file)
index 0000000..0320cf7
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cfg.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.Files;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.slf4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+import static com.google.common.io.ByteStreams.toByteArray;
+import static com.google.common.io.Files.write;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * UnitTest for ComponentLoader.
+ */
+public class ComponentConfigLoaderTest {
+
+    static final File TEST_DIR = Files.createTempDir();
+
+    private static final String FOO_COMPONENT = "fooComponent";
+
+    private ComponentConfigLoader loader;
+
+    private TestConfigService service;
+
+    private final Logger log = getLogger(getClass());
+
+    /*
+     * Method to SetUp the test environment with test file, a config loader a service,
+     * and assign it to the loader.configService for the test.
+     */
+    @Before
+    public void setUp() {
+        ComponentConfigLoader.cfgFile = new File(TEST_DIR, "test.json");
+        loader = new ComponentConfigLoader();
+        service = new TestConfigService();
+        loader.configService = service;
+    }
+
+    /*
+     * Tests that the component in the json receives the correct configuration.
+     */
+    @Test
+    public void basics() throws IOException {
+        stageTestResource("basic.json");
+        loader.activate();
+        assertEquals("incorrect component", FOO_COMPONENT, service.component);
+    }
+
+    /*
+     * Tests that the component is null if the file has a bad configuration format
+     * for which it yielded an exception. Can't test the exception because it happens
+     * in a different thread.
+     */
+    @Test
+    public void badConfig() throws IOException {
+        stageTestResource("badConfig.json");
+        loader.activate();
+        assertNull("incorrect configuration", service.component);
+
+    }
+
+    /*
+     * Writes the necessary file for the tests in the temporary directory
+     */
+    static void stageTestResource(String name) throws IOException {
+        byte[] bytes = toByteArray(ComponentConfigLoaderTest.class.getResourceAsStream(name));
+        write(bytes, ComponentConfigLoader.cfgFile);
+    }
+
+    /*
+     * Mockup class for the config service.
+     */
+    private class TestConfigService extends ComponentConfigAdapter {
+
+        protected String component;
+        protected String name;
+        protected String value;
+
+        @Override
+        public Set<String> getComponentNames() {
+            return ImmutableSet.of(FOO_COMPONENT);
+        }
+
+        @Override
+        public void preSetProperty(String componentName, String name, String value) {
+            log.info("preSet");
+            this.component = componentName;
+            this.name = name;
+            this.value = value;
+
+        }
+
+        @Override
+        public void setProperty(String componentName, String name, String value) {
+            log.info("Set");
+            this.component = componentName;
+            this.name = name;
+            this.value = value;
+
+        }
+    }
+}
\ No newline at end of file
index d167197..3cd2ca2 100644 (file)
@@ -23,10 +23,12 @@ import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.ARP;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv6;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborSolicitation;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
@@ -64,14 +66,22 @@ import static org.junit.Assert.assertTrue;
 
 public class HostMonitorTest {
 
-    private static final IpAddress TARGET_IP_ADDR =
+    private static final IpAddress TARGET_IPV4_ADDR =
             IpAddress.valueOf("10.0.0.1");
-    private static final IpAddress SOURCE_ADDR =
+    private static final IpAddress SOURCE_IPV4_ADDR =
             IpAddress.valueOf("10.0.0.99");
     private static final InterfaceIpAddress IA1 =
-            new InterfaceIpAddress(SOURCE_ADDR, IpPrefix.valueOf("10.0.0.0/24"));
+            new InterfaceIpAddress(SOURCE_IPV4_ADDR, IpPrefix.valueOf("10.0.0.0/24"));
     private MacAddress sourceMac = MacAddress.valueOf(1L);
 
+    private static final IpAddress TARGET_IPV6_ADDR =
+            IpAddress.valueOf("1000::1");
+    private static final IpAddress SOURCE_IPV6_ADDR =
+            IpAddress.valueOf("1000::f");
+    private static final InterfaceIpAddress IA2 =
+            new InterfaceIpAddress(SOURCE_IPV6_ADDR, IpPrefix.valueOf("1000::/64"));
+    private MacAddress sourceMac2 = MacAddress.valueOf(2L);
+
     private EdgePortService edgePortService;
 
     private HostMonitor hostMonitor;
@@ -90,7 +100,36 @@ public class HostMonitorTest {
     }
 
     @Test
-    public void testMonitorHostExists() throws Exception {
+    public void testMonitorIpv4HostExists() throws Exception {
+        ProviderId id = new ProviderId("fake://", "id");
+
+        Host host = createMock(Host.class);
+        expect(host.providerId()).andReturn(id);
+        replay(host);
+
+        HostManager hostManager = createMock(HostManager.class);
+        expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR))
+                .andReturn(Collections.singleton(host));
+        replay(hostManager);
+
+        HostProvider hostProvider = createMock(HostProvider.class);
+        expect(hostProvider.id()).andReturn(id).anyTimes();
+        hostProvider.triggerProbe(host);
+        expectLastCall().once();
+        replay(hostProvider);
+
+        hostMonitor = new HostMonitor(null, hostManager, null, edgePortService);
+
+        hostMonitor.registerHostProvider(hostProvider);
+        hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR);
+
+        hostMonitor.run(null);
+
+        verify(hostProvider);
+    }
+
+    @Test
+    public void testMonitorIpv6HostExists() throws Exception {
         ProviderId id = new ProviderId("fake://", "id");
 
         Host host = createMock(Host.class);
@@ -98,7 +137,7 @@ public class HostMonitorTest {
         replay(host);
 
         HostManager hostManager = createMock(HostManager.class);
-        expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
+        expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR))
                 .andReturn(Collections.singleton(host));
         replay(hostManager);
 
@@ -111,7 +150,7 @@ public class HostMonitorTest {
         hostMonitor = new HostMonitor(null, hostManager, null, edgePortService);
 
         hostMonitor.registerHostProvider(hostProvider);
-        hostMonitor.addMonitoringFor(TARGET_IP_ADDR);
+        hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR);
 
         hostMonitor.run(null);
 
@@ -119,7 +158,7 @@ public class HostMonitorTest {
     }
 
     @Test
-    public void testMonitorHostDoesNotExist() throws Exception {
+    public void testMonitorIpv4HostDoesNotExist() throws Exception {
 
         HostManager hostManager = createMock(HostManager.class);
 
@@ -140,12 +179,12 @@ public class HostMonitorTest {
 
         ConnectPoint cp = new ConnectPoint(devId, portNum);
 
-        expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
+        expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR))
                 .andReturn(Collections.emptySet()).anyTimes();
         replay(hostManager);
 
         InterfaceService interfaceService = createMock(InterfaceService.class);
-        expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR))
+        expect(interfaceService.getMatchingInterface(TARGET_IPV4_ADDR))
                 .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.NONE))
                 .anyTimes();
         replay(interfaceService);
@@ -156,7 +195,7 @@ public class HostMonitorTest {
         // Run the test
         hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
 
-        hostMonitor.addMonitoringFor(TARGET_IP_ADDR);
+        hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR);
         hostMonitor.run(null);
 
 
@@ -178,16 +217,85 @@ public class HostMonitorTest {
         Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
         assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID());
         ARP arp = (ARP) eth.getPayload();
-        assertArrayEquals(SOURCE_ADDR.toOctets(),
+        assertArrayEquals(SOURCE_IPV4_ADDR.toOctets(),
                           arp.getSenderProtocolAddress());
         assertArrayEquals(sourceMac.toBytes(),
                           arp.getSenderHardwareAddress());
-        assertArrayEquals(TARGET_IP_ADDR.toOctets(),
+        assertArrayEquals(TARGET_IPV4_ADDR.toOctets(),
                           arp.getTargetProtocolAddress());
     }
 
     @Test
-    public void testMonitorHostDoesNotExistWithVlan() throws Exception {
+    public void testMonitorIpv6HostDoesNotExist() throws Exception {
+
+        HostManager hostManager = createMock(HostManager.class);
+
+        DeviceId devId = DeviceId.deviceId("fake");
+
+        Device device = createMock(Device.class);
+        expect(device.id()).andReturn(devId).anyTimes();
+        replay(device);
+
+        PortNumber portNum = PortNumber.portNumber(2L);
+
+        Port port = createMock(Port.class);
+        expect(port.number()).andReturn(portNum).anyTimes();
+        replay(port);
+
+        TestDeviceService deviceService = new TestDeviceService();
+        deviceService.addDevice(device, Collections.singleton(port));
+
+        ConnectPoint cp = new ConnectPoint(devId, portNum);
+
+        expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR))
+                .andReturn(Collections.emptySet()).anyTimes();
+        replay(hostManager);
+
+        InterfaceService interfaceService = createMock(InterfaceService.class);
+        expect(interfaceService.getMatchingInterface(TARGET_IPV6_ADDR))
+                .andReturn(new Interface(cp, Collections.singleton(IA2), sourceMac2, VlanId.NONE))
+                .anyTimes();
+        replay(interfaceService);
+
+        TestPacketService packetService = new TestPacketService();
+
+
+        // Run the test
+        hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
+
+        hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR);
+        hostMonitor.run(null);
+
+
+        // Check that a packet was sent to our PacketService and that it has
+        // the properties we expect
+        assertEquals(1, packetService.packets.size());
+        OutboundPacket packet = packetService.packets.get(0);
+
+        // Check the output port is correct
+        assertEquals(1, packet.treatment().immediate().size());
+        Instruction instruction = packet.treatment().immediate().get(0);
+        assertTrue(instruction instanceof OutputInstruction);
+        OutputInstruction oi = (OutputInstruction) instruction;
+        assertEquals(portNum, oi.port());
+
+        // Check the output packet is correct (well the important bits anyway)
+        final byte[] pktData = new byte[packet.data().remaining()];
+        packet.data().get(pktData);
+        Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
+        assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID());
+        IPv6 ipv6 = (IPv6) eth.getPayload();
+        assertArrayEquals(SOURCE_IPV6_ADDR.toOctets(), ipv6.getSourceAddress());
+
+        NeighborSolicitation ns =
+                (NeighborSolicitation) ipv6.getPayload().getPayload();
+        assertArrayEquals(sourceMac2.toBytes(), ns.getOptions().get(0).data());
+
+        assertArrayEquals(TARGET_IPV6_ADDR.toOctets(), ns.getTargetAddress());
+    }
+
+    @Test
+    public void testMonitorIpv4HostDoesNotExistWithVlan() throws Exception {
 
         HostManager hostManager = createMock(HostManager.class);
 
@@ -209,12 +317,12 @@ public class HostMonitorTest {
 
         ConnectPoint cp = new ConnectPoint(devId, portNum);
 
-        expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
+        expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR))
                 .andReturn(Collections.emptySet()).anyTimes();
         replay(hostManager);
 
         InterfaceService interfaceService = createMock(InterfaceService.class);
-        expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR))
+        expect(interfaceService.getMatchingInterface(TARGET_IPV4_ADDR))
                 .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.vlanId(vlan)))
                 .anyTimes();
         replay(interfaceService);
@@ -225,7 +333,7 @@ public class HostMonitorTest {
         // Run the test
         hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
 
-        hostMonitor.addMonitoringFor(TARGET_IP_ADDR);
+        hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR);
         hostMonitor.run(null);
 
 
@@ -247,14 +355,84 @@ public class HostMonitorTest {
         Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
         assertEquals(vlan, eth.getVlanID());
         ARP arp = (ARP) eth.getPayload();
-        assertArrayEquals(SOURCE_ADDR.toOctets(),
+        assertArrayEquals(SOURCE_IPV4_ADDR.toOctets(),
                           arp.getSenderProtocolAddress());
         assertArrayEquals(sourceMac.toBytes(),
                           arp.getSenderHardwareAddress());
-        assertArrayEquals(TARGET_IP_ADDR.toOctets(),
+        assertArrayEquals(TARGET_IPV4_ADDR.toOctets(),
                           arp.getTargetProtocolAddress());
     }
 
+    @Test
+    public void testMonitorIpv6HostDoesNotExistWithVlan() throws Exception {
+
+        HostManager hostManager = createMock(HostManager.class);
+
+        DeviceId devId = DeviceId.deviceId("fake");
+        short vlan = 5;
+
+        Device device = createMock(Device.class);
+        expect(device.id()).andReturn(devId).anyTimes();
+        replay(device);
+
+        PortNumber portNum = PortNumber.portNumber(1L);
+
+        Port port = createMock(Port.class);
+        expect(port.number()).andReturn(portNum).anyTimes();
+        replay(port);
+
+        TestDeviceService deviceService = new TestDeviceService();
+        deviceService.addDevice(device, Collections.singleton(port));
+
+        ConnectPoint cp = new ConnectPoint(devId, portNum);
+
+        expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR))
+                .andReturn(Collections.emptySet()).anyTimes();
+        replay(hostManager);
+
+        InterfaceService interfaceService = createMock(InterfaceService.class);
+        expect(interfaceService.getMatchingInterface(TARGET_IPV6_ADDR))
+                .andReturn(new Interface(cp, Collections.singleton(IA2), sourceMac2, VlanId.vlanId(vlan)))
+                .anyTimes();
+        replay(interfaceService);
+
+        TestPacketService packetService = new TestPacketService();
+
+
+        // Run the test
+        hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService);
+
+        hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR);
+        hostMonitor.run(null);
+
+
+        // Check that a packet was sent to our PacketService and that it has
+        // the properties we expect
+        assertEquals(1, packetService.packets.size());
+        OutboundPacket packet = packetService.packets.get(0);
+
+        // Check the output port is correct
+        assertEquals(1, packet.treatment().immediate().size());
+        Instruction instruction = packet.treatment().immediate().get(0);
+        assertTrue(instruction instanceof OutputInstruction);
+        OutputInstruction oi = (OutputInstruction) instruction;
+        assertEquals(portNum, oi.port());
+
+        // Check the output packet is correct (well the important bits anyway)
+        final byte[] pktData = new byte[packet.data().remaining()];
+        packet.data().get(pktData);
+        Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length);
+        assertEquals(vlan, eth.getVlanID());
+        IPv6 ipv6 = (IPv6) eth.getPayload();
+        assertArrayEquals(SOURCE_IPV6_ADDR.toOctets(), ipv6.getSourceAddress());
+
+        NeighborSolicitation ns =
+                (NeighborSolicitation) ipv6.getPayload().getPayload();
+        assertArrayEquals(sourceMac2.toBytes(), ns.getOptions().get(0).data());
+
+        assertArrayEquals(TARGET_IPV6_ADDR.toOctets(), ns.getTargetAddress());
+    }
+
     class TestPacketService extends PacketServiceAdapter {
 
         List<OutboundPacket> packets = new ArrayList<>();
index 4bf32f4..3f40de0 100644 (file)
@@ -157,7 +157,7 @@ public class IntentManagerTest {
     private static class MockInstallableIntent extends FlowRuleIntent {
 
         public MockInstallableIntent() {
-            super(APPID, Collections.singletonList(new MockFlowRule(100)));
+            super(APPID, Collections.singletonList(new MockFlowRule(100)), Collections.emptyList());
         }
     }
 
index eb7a393..03d664d 100644 (file)
@@ -31,8 +31,7 @@ import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentTestsMocks;
 import org.onosproject.net.intent.LinkCollectionIntent;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.topology.LinkWeight;
-import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.PathServiceAdapter;
 
 import java.util.HashSet;
 import java.util.List;
@@ -60,7 +59,7 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes
     /**
      * Mock path service for creating paths within the test.
      */
-    private static class MockPathService implements PathService {
+    private static class MockPathService extends PathServiceAdapter {
 
         final String[] pathHops;
 
@@ -86,11 +85,6 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes
 
             return result;
         }
-
-        @Override
-        public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
-            return null;
-        }
     }
 
     /**
index 2f40b37..38a116d 100644 (file)
@@ -27,18 +27,12 @@ import org.onosproject.net.DefaultLink;
 import org.onosproject.net.DefaultPath;
 import org.onosproject.net.Link;
 import org.onosproject.net.OchSignalType;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentExtensionService;
-import org.onosproject.net.intent.IntentTestsMocks;
 import org.onosproject.net.intent.MockIdGenerator;
 import org.onosproject.net.intent.OpticalPathIntent;
-import org.onosproject.net.provider.ProviderId;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -63,16 +57,11 @@ public class OpticalPathIntentCompilerTest {
     private final IdGenerator idGenerator = new MockIdGenerator();
     private OpticalPathIntentCompiler sut;
 
-    private final TrafficSelector selector = DefaultTrafficSelector.builder().build();
-    private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
     private final ApplicationId appId = new TestApplicationId("test");
-    private final ProviderId pid = new ProviderId("of", "test");
     private final ConnectPoint d1p1 = connectPoint("s1", 0);
     private final ConnectPoint d2p0 = connectPoint("s2", 0);
     private final ConnectPoint d2p1 = connectPoint("s2", 1);
     private final ConnectPoint d3p1 = connectPoint("s3", 1);
-    private final ConnectPoint d3p0 = connectPoint("s3", 10);
-    private final ConnectPoint d1p0 = connectPoint("s1", 10);
 
     private final List<Link> links = Arrays.asList(
             new DefaultLink(PID, d1p1, d2p0, DIRECT),
@@ -103,7 +92,6 @@ public class OpticalPathIntentCompilerTest {
         intentExtensionService.registerCompiler(OpticalPathIntent.class, sut);
         intentExtensionService.unregisterCompiler(OpticalPathIntent.class);
         sut.intentManager = intentExtensionService;
-        sut.resourceService = new IntentTestsMocks.MockResourceService();
 
         replay(coreService, intentExtensionService);
     }
index 2a2d0b5..1911da5 100644 (file)
@@ -23,13 +23,11 @@ import org.onosproject.net.ElementId;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.Path;
-import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.HostServiceAdapter;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.topology.LinkWeight;
 import org.onosproject.net.topology.PathService;
 import org.onosproject.net.topology.Topology;
-import org.onosproject.net.topology.TopologyService;
 import org.onosproject.net.topology.TopologyServiceAdapter;
 
 import java.util.HashMap;
@@ -137,7 +135,7 @@ public class PathManagerTest {
     }
 
     // Fake entity to give out paths.
-    private class FakeTopoMgr extends TopologyServiceAdapter implements TopologyService {
+    private class FakeTopoMgr extends TopologyServiceAdapter {
         Set<Path> paths = new HashSet<>();
 
         @Override
@@ -152,7 +150,7 @@ public class PathManagerTest {
     }
 
     // Fake entity to give out hosts.
-    private class FakeHostMgr extends HostServiceAdapter implements HostService {
+    private class FakeHostMgr extends HostServiceAdapter  {
         private Map<HostId, Host> hosts = new HashMap<>();
 
         @Override
index f3cd28d..56133a0 100644 (file)
@@ -114,7 +114,7 @@ public class TopologyManagerTest {
                              link("c", 2, "d", 1), link("d", 1, "c", 2),
                              link("d", 2, "a", 2), link("a", 2, "d", 2),
                              link("e", 1, "f", 1), link("f", 1, "e", 1));
-        GraphDescription data = new DefaultGraphDescription(4321L, devices, links);
+        GraphDescription data = new DefaultGraphDescription(4321L, System.currentTimeMillis(), devices, links);
         providerService.topologyChanged(data, null);
     }
 
diff --git a/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badComponent.json b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badComponent.json
new file mode 100644 (file)
index 0000000..5c0ac35
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "org.onosproject.proxyarp.ProxyArp2": {
+    "testProperty": true
+  }
+}
\ No newline at end of file
diff --git a/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badConfig.json b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badConfig.json
new file mode 100644 (file)
index 0000000..a76552e
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "fooComponent": {
+    badconfig
+  }
+}
\ No newline at end of file
diff --git a/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/basic.json b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/basic.json
new file mode 100644 (file)
index 0000000..dd32924
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "fooComponent": {
+    "testProperty": true
+  }
+}
\ No newline at end of file
index ff3e36a..52a999a 100644 (file)
@@ -38,7 +38,7 @@ public interface Database extends DatabaseProxy<String, byte[]>, Resource<Databa
    * options specified in {@code cluster.conf} will override those in {cluster-defaults.conf}.<p>
    *
    * Additionally, the database will be constructed with an database configuration that searches the classpath for
-   * three configuration files - {@code {name}}, {@code database}, {@code database-defaults}, {@code resource}, and
+   * three configuration files - {@code name}, {@code database}, {@code database-defaults}, {@code resource}, and
    * {@code resource-defaults} - in that order. The first resource is a configuration resource with the same name
    * as the map resource. If the resource is namespaced - e.g. `databases.my-database.conf` - then resource
    * configurations will be loaded according to namespaces as well; for example, `databases.conf`.
@@ -54,7 +54,7 @@ public interface Database extends DatabaseProxy<String, byte[]>, Resource<Databa
    * Creates a new database.<p>
    *
    * The database will be constructed with an database configuration that searches the classpath for
-   * three configuration files - {@code {name}}, {@code database}, {@code database-defaults}, {@code resource}, and
+   * three configuration files - {@code name}, {@code database}, {@code database-defaults}, {@code resource}, and
    * {@code resource-defaults} - in that order. The first resource is a configuration resource with the same name
    * as the database resource. If the resource is namespaced - e.g. `databases.my-database.conf` - then resource
    * configurations will be loaded according to namespaces as well; for example, `databases.conf`.
index fbc2c88..6ea7c22 100644 (file)
@@ -443,7 +443,10 @@ public class DatabaseManager implements StorageService, StorageAdminService {
         public void event(ApplicationEvent event) {
             if (event.type() == APP_UNINSTALLED || event.type() == APP_DEACTIVATED) {
                 ApplicationId appId = event.subject().id();
-                List<DefaultAsyncConsistentMap> mapsToRemove = ImmutableList.copyOf(mapsByApplication.get(appId));
+                List<DefaultAsyncConsistentMap> mapsToRemove;
+                synchronized (mapsByApplication) {
+                    mapsToRemove = ImmutableList.copyOf(mapsByApplication.get(appId));
+                }
                 mapsToRemove.forEach(DatabaseManager.this::unregisterMap);
                 if (event.type() == APP_UNINSTALLED) {
                     mapsToRemove.stream().filter(map -> map.purgeOnUninstall()).forEach(map -> map.clear());
index 95f9e39..1d81f99 100644 (file)
 
 package org.onosproject.store.consistent.impl;
 
+import org.onosproject.store.service.Transaction;
+import org.onosproject.store.service.Versioned;
+
 import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 
-import org.onosproject.store.service.Transaction;
-import org.onosproject.store.service.Versioned;
-
 /**
  * Database proxy.
  */
@@ -45,6 +45,7 @@ public interface DatabaseProxy<K, V> {
 
     /**
      * Returns the number of entries in map.
+     *
      * @param mapName map name
      * @return A completable future to be completed with the result once complete.
      */
@@ -62,7 +63,7 @@ public interface DatabaseProxy<K, V> {
      * Checks whether the map contains a key.
      *
      * @param mapName map name
-     * @param key key to check.
+     * @param key     key to check.
      * @return A completable future to be completed with the result once complete.
      */
     CompletableFuture<Boolean> mapContainsKey(String mapName, K key);
@@ -71,7 +72,7 @@ public interface DatabaseProxy<K, V> {
      * Checks whether the map contains a value.
      *
      * @param mapName map name
-     * @param value The value to check.
+     * @param value   The value to check.
      * @return A completable future to be completed with the result once complete.
      */
     CompletableFuture<Boolean> mapContainsValue(String mapName, V value);
@@ -80,7 +81,7 @@ public interface DatabaseProxy<K, V> {
      * Gets a value from the map.
      *
      * @param mapName map name
-     * @param key The key to get.
+     * @param key     The key to get.
      * @return A completable future to be completed with the result once complete.
      */
     CompletableFuture<Versioned<V>> mapGet(String mapName, K key);
@@ -88,11 +89,11 @@ public interface DatabaseProxy<K, V> {
     /**
      * Updates the map.
      *
-     * @param mapName map name
-     * @param key           The key to set
-     * @param valueMatch    match for checking existing value
-     * @param versionMatch  match for checking existing version
-     * @param value         new value
+     * @param mapName      map name
+     * @param key          The key to set
+     * @param valueMatch   match for checking existing value
+     * @param versionMatch match for checking existing version
+     * @param value        new value
      * @return A completable future to be completed with the result once complete
      */
     CompletableFuture<Result<UpdateResult<K, V>>> mapUpdate(
@@ -130,11 +131,11 @@ public interface DatabaseProxy<K, V> {
      */
     CompletableFuture<Set<Map.Entry<K, Versioned<V>>>> mapEntrySet(String mapName);
 
-     /**
+    /**
      * Atomically add the given value to current value of the specified counter.
      *
      * @param counterName counter name
-     * @param delta value to add
+     * @param delta       value to add
      * @return updated value
      */
     CompletableFuture<Long> counterAddAndGet(String counterName, long delta);
@@ -143,11 +144,31 @@ public interface DatabaseProxy<K, V> {
      * Atomically add the given value to current value of the specified counter.
      *
      * @param counterName counter name
-     * @param delta value to add
+     * @param delta       value to add
      * @return previous value
      */
     CompletableFuture<Long> counterGetAndAdd(String counterName, long delta);
 
+
+    /**
+     * Atomically sets the given value to current value of the specified counter.
+     *
+     * @param counterName counter name
+     * @param value       value to set
+     * @return void future
+     */
+    CompletableFuture<Void> counterSet(String counterName, long value);
+
+    /**
+     * Atomically sets the given counter to the specified update value if and only if the current value is equal to the
+     * expected value.
+     * @param counterName counter name
+     * @param expectedValue value to use for equivalence check
+     * @param update value to set if expected value is current value
+     * @return true if an update occurred, false otherwise
+     */
+    CompletableFuture<Boolean> counterCompareAndSet(String counterName, long expectedValue, long update);
+
     /**
      * Returns the current value of the specified atomic counter.
      *
@@ -158,6 +179,7 @@ public interface DatabaseProxy<K, V> {
 
     /**
      * Returns the size of queue.
+     *
      * @param queueName queue name
      * @return queue size
      */
@@ -165,14 +187,16 @@ public interface DatabaseProxy<K, V> {
 
     /**
      * Inserts an entry into the queue.
+     *
      * @param queueName queue name
-     * @param entry queue entry
+     * @param entry     queue entry
      * @return void future
      */
     CompletableFuture<Void> queuePush(String queueName, byte[] entry);
 
     /**
      * Removes an entry from the queue if the queue is non-empty.
+     *
      * @param queueName queue name
      * @return entry future. Can be completed with null if queue is empty
      */
@@ -180,6 +204,7 @@ public interface DatabaseProxy<K, V> {
 
     /**
      * Returns but does not remove an entry from the queue.
+     *
      * @param queueName queue name
      * @return entry. Can be null if queue is empty
      */
index b3dd1c4..1136428 100644 (file)
 
 package org.onosproject.store.consistent.impl;
 
-import java.util.Collection;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import org.onosproject.store.service.Transaction;
-import org.onosproject.store.service.Versioned;
-
 import net.kuujo.copycat.state.Command;
 import net.kuujo.copycat.state.Initializer;
 import net.kuujo.copycat.state.Query;
 import net.kuujo.copycat.state.StateContext;
+import org.onosproject.store.service.Transaction;
+import org.onosproject.store.service.Versioned;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * Database state.
@@ -82,6 +81,9 @@ public interface DatabaseState<K, V> {
   @Command
   Long counterAddAndGet(String counterName, long delta);
 
+  @Command
+  Boolean counterCompareAndSet(String counterName, long expectedValue, long updateValue);
+
   @Command
   Long counterGetAndAdd(String counterName, long delta);
 
index 7a439c3..d851eaa 100644 (file)
@@ -18,6 +18,7 @@ package org.onosproject.store.consistent.impl;
 import org.onosproject.store.service.AsyncAtomicCounter;
 
 import java.util.concurrent.CompletableFuture;
+
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
@@ -38,6 +39,8 @@ public class DefaultAsyncAtomicCounter implements AsyncAtomicCounter {
     private static final String GET_AND_ADD = "getAndAdd";
     private static final String ADD_AND_GET = "addAndGet";
     private static final String GET = "get";
+    private static final String SET = "set";
+    private static final String COMPARE_AND_SET = "compareAndSet";
 
     public DefaultAsyncAtomicCounter(String name,
                                      Database database,
@@ -72,13 +75,27 @@ public class DefaultAsyncAtomicCounter implements AsyncAtomicCounter {
     public CompletableFuture<Long> getAndAdd(long delta) {
         final MeteringAgent.Context timer = monitor.startTimer(GET_AND_ADD);
         return database.counterGetAndAdd(name, delta)
-                       .whenComplete((r, e) -> timer.stop(e));
+                .whenComplete((r, e) -> timer.stop(e));
     }
 
     @Override
     public CompletableFuture<Long> addAndGet(long delta) {
         final MeteringAgent.Context timer = monitor.startTimer(ADD_AND_GET);
         return database.counterAddAndGet(name, delta)
-                       .whenComplete((r, e) -> timer.stop(e));
+                .whenComplete((r, e) -> timer.stop(e));
+    }
+
+    @Override
+    public CompletableFuture<Void> set(long value) {
+        final MeteringAgent.Context timer = monitor.startTimer(SET);
+        return database.counterSet(name, value)
+                .whenComplete((r, e) -> timer.stop(e));
+    }
+
+    @Override
+    public CompletableFuture<Boolean> compareAndSet(long expectedValue, long updateValue) {
+        final MeteringAgent.Context timer = monitor.startTimer(COMPARE_AND_SET);
+        return database.counterCompareAndSet(name, expectedValue, updateValue)
+                .whenComplete((r, e) -> timer.stop(e));
     }
 }
index 64886e4..2d6a956 100644 (file)
@@ -62,6 +62,16 @@ public class DefaultAtomicCounter implements AtomicCounter {
         return complete(asyncCounter.getAndAdd(delta));
     }
 
+    @Override
+    public void set(long value) {
+        complete(asyncCounter.set(value));
+    }
+
+    @Override
+    public boolean compareAndSet(long expectedValue, long updateValue) {
+        return complete(asyncCounter.compareAndSet(expectedValue, updateValue));
+    }
+
     @Override
     public long get() {
         return complete(asyncCounter.get());
index 4d9776e..2a50fbd 100644 (file)
 
 package org.onosproject.store.consistent.impl;
 
-import net.kuujo.copycat.state.StateMachine;
+import com.google.common.collect.Sets;
 import net.kuujo.copycat.resource.internal.AbstractResource;
 import net.kuujo.copycat.resource.internal.ResourceManager;
+import net.kuujo.copycat.state.StateMachine;
 import net.kuujo.copycat.state.internal.DefaultStateMachine;
 import net.kuujo.copycat.util.concurrent.Futures;
 import net.kuujo.copycat.util.function.TriConsumer;
+import org.onosproject.store.service.Transaction;
+import org.onosproject.store.service.Versioned;
 
 import java.util.Collection;
 import java.util.Map;
@@ -30,11 +33,6 @@ import java.util.concurrent.CompletableFuture;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
-import org.onosproject.store.service.Transaction;
-import org.onosproject.store.service.Versioned;
-
-import com.google.common.collect.Sets;
-
 /**
  * Default database.
  */
@@ -44,7 +42,7 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab
     private final Set<Consumer<StateMachineUpdate>> consumers = Sets.newCopyOnWriteArraySet();
     private final TriConsumer<String, Object, Object> watcher = new InternalStateMachineWatcher();
 
-    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public DefaultDatabase(ResourceManager context) {
         super(context);
         this.stateMachine = new DefaultStateMachine(context,
@@ -66,7 +64,7 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab
      * return the completed future result.
      *
      * @param supplier The supplier to call if the database is open.
-     * @param <T> The future result type.
+     * @param <T>      The future result type.
      * @return A completable future that if this database is closed is immediately failed.
      */
     protected <T> CompletableFuture<T> checkOpen(Supplier<CompletableFuture<T>> supplier) {
@@ -152,6 +150,16 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab
         return checkOpen(() -> proxy.counterGetAndAdd(counterName, delta));
     }
 
+    @Override
+    public CompletableFuture<Void> counterSet(String counterName, long value) {
+        return checkOpen(() -> proxy.counterSet(counterName, value));
+    }
+
+    @Override
+    public CompletableFuture<Boolean> counterCompareAndSet(String counterName, long expectedValue, long update) {
+        return checkOpen(() -> proxy.counterCompareAndSet(counterName, expectedValue, update));
+    }
+
     @Override
     public CompletableFuture<Long> queueSize(String queueName) {
         return checkOpen(() -> proxy.queueSize(queueName));
index 9a55ffb..8943fc8 100644 (file)
 
 package org.onosproject.store.consistent.impl;
 
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import net.kuujo.copycat.state.Initializer;
+import net.kuujo.copycat.state.StateContext;
+import org.onosproject.store.service.DatabaseUpdate;
+import org.onosproject.store.service.Transaction;
+import org.onosproject.store.service.Versioned;
+
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Queue;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
-import java.util.Set;
-
-import org.onosproject.store.service.DatabaseUpdate;
-import org.onosproject.store.service.Transaction;
-import org.onosproject.store.service.Versioned;
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
-import net.kuujo.copycat.state.Initializer;
-import net.kuujo.copycat.state.StateContext;
 
 /**
  * Default database state.
@@ -194,6 +193,11 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> {
         return getCounter(counterName).getAndAdd(delta);
     }
 
+    @Override
+    public Boolean counterCompareAndSet(String counterName, long expectedValue, long updateValue) {
+        return getCounter(counterName).compareAndSet(expectedValue, updateValue);
+    }
+
     @Override
     public Long counterGet(String counterName) {
         return getCounter(counterName).get();
diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/MutexExecutionManager.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/MutexExecutionManager.java
new file mode 100644 (file)
index 0000000..d8593e3
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.consistent.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.cluster.ClusterEvent;
+import org.onosproject.cluster.ClusterEventListener;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode.State;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.ConsistentMapException;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.MutexExecutionService;
+import org.onosproject.store.service.MutexTask;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * Implementation of a MutexExecutionService.
+ */
+@Component(immediate = true)
+@Service
+public class MutexExecutionManager implements MutexExecutionService {
+
+    private final Logger log = getLogger(getClass());
+
+    protected ConsistentMap<String, MutexState> lockMap;
+    protected NodeId localNodeId;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private final MapEventListener<String, MutexState> mapEventListener = new InternalLockMapEventListener();
+    private final ClusterEventListener clusterEventListener = new InternalClusterEventListener();
+
+    private Map<String, CompletableFuture<MutexState>> pending = Maps.newConcurrentMap();
+    private Map<String, InnerMutexTask> activeTasks = Maps.newConcurrentMap();
+
+    @Activate
+    public void activate() {
+        localNodeId = clusterService.getLocalNode().id();
+        lockMap = storageService.<String, MutexState>consistentMapBuilder()
+                    .withName("onos-mutexes")
+                    .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API), MutexState.class))
+                    .withPartitionsDisabled()
+                    .build();
+        lockMap.addListener(mapEventListener);
+        clusterService.addListener(clusterEventListener);
+        releaseOldLocks();
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        lockMap.removeListener(mapEventListener);
+        pending.values().forEach(future -> future.cancel(true));
+        activeTasks.forEach((k, v) -> {
+            v.stop();
+            unlock(k);
+        });
+        clusterService.removeListener(clusterEventListener);
+        log.info("Stopped");
+    }
+
+    @Override
+    public CompletableFuture<Void> execute(MutexTask task, String exclusionPath, Executor executor) {
+        return lock(exclusionPath)
+                    .thenApply(state -> activeTasks.computeIfAbsent(exclusionPath,
+                                                                    k -> new InnerMutexTask(exclusionPath,
+                                                                                            task,
+                                                                                            state.term())))
+                    .thenAcceptAsync(t -> t.start(), executor)
+                    .whenComplete((r, e) -> unlock(exclusionPath));
+    }
+
+    protected CompletableFuture<MutexState> lock(String exclusionPath) {
+        CompletableFuture<MutexState> future =
+                pending.computeIfAbsent(exclusionPath, k -> new CompletableFuture<>());
+        tryLock(exclusionPath);
+        return future;
+    }
+
+    /**
+     * Attempts to acquire lock for a path. If lock is held by some other node, adds this node to
+     * the wait list.
+     * @param exclusionPath exclusion path
+     */
+    protected void tryLock(String exclusionPath) {
+        Tools.retryable(() -> lockMap.asJavaMap()
+                                     .compute(exclusionPath,
+                                              (k, v) -> MutexState.admit(v, localNodeId)),
+                                              ConsistentMapException.ConcurrentModification.class,
+                                              Integer.MAX_VALUE,
+                                              100).get();
+    }
+
+    /**
+     * Releases lock for the specific path. This operation is idempotent.
+     * @param exclusionPath exclusion path
+     */
+    protected void unlock(String exclusionPath) {
+        Tools.retryable(() -> lockMap.asJavaMap()
+                                     .compute(exclusionPath, (k, v) -> MutexState.evict(v, localNodeId)),
+                        ConsistentMapException.ConcurrentModification.class,
+                        Integer.MAX_VALUE,
+                        100).get();
+    }
+
+    /**
+     * Detects and releases all locks held by this node.
+     */
+    private void releaseOldLocks() {
+        Maps.filterValues(lockMap.asJavaMap(), state -> localNodeId.equals(state.holder()))
+            .keySet()
+            .forEach(path -> {
+                log.info("Detected zombie task still holding lock for {}. Releasing lock.", path);
+                unlock(path);
+            });
+    }
+
+    private class InternalLockMapEventListener implements MapEventListener<String, MutexState> {
+
+        @Override
+        public void event(MapEvent<String, MutexState> event) {
+            log.debug("Received {}", event);
+            if (event.type() == MapEvent.Type.UPDATE || event.type() == MapEvent.Type.INSERT) {
+                pending.computeIfPresent(event.key(), (k, future) -> {
+                    MutexState state = Versioned.valueOrElse(event.value(), null);
+                    if (state != null && localNodeId.equals(state.holder())) {
+                        log.debug("Local node is now owner for {}", event.key());
+                        future.complete(state);
+                        return null;
+                    } else {
+                        return future;
+                    }
+                });
+                InnerMutexTask task = activeTasks.get(event.key());
+                if (task != null && task.term() < Versioned.valueOrElse(event.value(), null).term()) {
+                    task.stop();
+                }
+            }
+        }
+    }
+
+    private class InternalClusterEventListener implements ClusterEventListener {
+
+        @Override
+        public void event(ClusterEvent event) {
+            if (event.type() == ClusterEvent.Type.INSTANCE_DEACTIVATED ||
+                    event.type() == ClusterEvent.Type.INSTANCE_REMOVED) {
+                NodeId nodeId = event.subject().id();
+                log.debug("{} is no longer active. Attemping to clean up its locks.", nodeId);
+                lockMap.asJavaMap().forEach((k, v) -> {
+                    if (v.contains(nodeId)) {
+                        lockMap.compute(k, (path, state) -> MutexState.evict(v, nodeId));
+                    }
+                });
+            }
+            long activeNodes = clusterService.getNodes()
+                                             .stream()
+                                             .map(node -> clusterService.getState(node.id()))
+                                             .filter(State.ACTIVE::equals)
+                                             .count();
+            if (clusterService.getNodes().size() > 1 && activeNodes == 1) {
+                log.info("This node is partitioned away from the cluster. Stopping all inflight executions");
+                activeTasks.forEach((k, v) -> {
+                    v.stop();
+                });
+            }
+        }
+    }
+
+    private static final class MutexState {
+
+        private final NodeId holder;
+        private final List<NodeId> waitList;
+        private final long term;
+
+        public static MutexState admit(MutexState state, NodeId nodeId) {
+            if (state == null) {
+                return new MutexState(nodeId, 1L, Lists.newArrayList());
+            } else if (state.holder() == null) {
+                return new MutexState(nodeId, state.term() + 1, Lists.newArrayList());
+            } else {
+                if (!state.contains(nodeId)) {
+                    NodeId newHolder = state.holder();
+                    List<NodeId> newWaitList = Lists.newArrayList(state.waitList());
+                    newWaitList.add(nodeId);
+                    return new MutexState(newHolder, state.term(), newWaitList);
+                } else {
+                    return state;
+                }
+            }
+        }
+
+        public static MutexState evict(MutexState state, NodeId nodeId) {
+            return state.evict(nodeId);
+        }
+
+        public MutexState evict(NodeId nodeId) {
+            if (nodeId.equals(holder)) {
+                if (waitList.isEmpty()) {
+                    return new MutexState(null, term, waitList);
+                }
+                List<NodeId> newWaitList = Lists.newArrayList(waitList);
+                NodeId newHolder = newWaitList.remove(0);
+                return new MutexState(newHolder, term + 1, newWaitList);
+            } else {
+                NodeId newHolder = holder;
+                List<NodeId> newWaitList = Lists.newArrayList(waitList);
+                newWaitList.remove(nodeId);
+                return new MutexState(newHolder, term, newWaitList);
+            }
+        }
+
+        public NodeId holder() {
+            return holder;
+        }
+
+        public List<NodeId> waitList() {
+            return waitList;
+        }
+
+        public long term() {
+            return term;
+        }
+
+        private boolean contains(NodeId nodeId) {
+            return (nodeId.equals(holder) || waitList.contains(nodeId));
+        }
+
+        private MutexState(NodeId holder, long term, List<NodeId> waitList) {
+            this.holder = holder;
+            this.term = term;
+            this.waitList = Lists.newArrayList(waitList);
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(getClass())
+                    .add("holder", holder)
+                    .add("term", term)
+                    .add("waitList", waitList)
+                    .toString();
+        }
+    }
+
+    private class InnerMutexTask implements MutexTask {
+        private final MutexTask task;
+        private final String mutexPath;
+        private final long term;
+
+        public InnerMutexTask(String mutexPath, MutexTask task, long term) {
+            this.mutexPath = mutexPath;
+            this.term = term;
+            this.task = task;
+        }
+
+        public long term() {
+            return term;
+        }
+
+        @Override
+        public void start() {
+            log.debug("Starting execution for mutex task guarded by {}", mutexPath);
+            task.start();
+            log.debug("Finished execution for mutex task guarded by {}", mutexPath);
+        }
+
+        @Override
+        public void stop() {
+            log.debug("Stopping execution for mutex task guarded by {}", mutexPath);
+            task.stop();
+        }
+    }
+}
\ No newline at end of file
index a294681..f741b36 100644 (file)
 
 package org.onosproject.store.consistent.impl;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import net.kuujo.copycat.Task;
+import net.kuujo.copycat.cluster.Cluster;
+import net.kuujo.copycat.resource.ResourceState;
+import org.onosproject.store.service.DatabaseUpdate;
+import org.onosproject.store.service.Transaction;
+import org.onosproject.store.service.Versioned;
+
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -28,18 +39,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import org.onosproject.store.service.DatabaseUpdate;
-import org.onosproject.store.service.Transaction;
-import org.onosproject.store.service.Versioned;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import net.kuujo.copycat.Task;
-import net.kuujo.copycat.cluster.Cluster;
-import net.kuujo.copycat.resource.ResourceState;
 import static com.google.common.base.Preconditions.checkState;
 
 /**
@@ -100,10 +99,10 @@ public class PartitionedDatabase implements Database {
         return CompletableFuture.allOf(partitions
                 .stream()
                 .map(db -> db.counters()
-                             .thenApply(m -> {
-                                 counters.putAll(m);
-                                 return null;
-                             }))
+                        .thenApply(m -> {
+                            counters.putAll(m);
+                            return null;
+                        }))
                 .toArray(CompletableFuture[]::new))
             .thenApply(v -> counters);
     }
@@ -113,9 +112,9 @@ public class PartitionedDatabase implements Database {
         checkState(isOpen.get(), DB_NOT_OPEN);
         AtomicInteger totalSize = new AtomicInteger(0);
         return CompletableFuture.allOf(partitions
-                    .stream()
-                    .map(p -> p.mapSize(mapName).thenApply(totalSize::addAndGet))
-                    .toArray(CompletableFuture[]::new))
+                                               .stream()
+                                               .map(p -> p.mapSize(mapName).thenApply(totalSize::addAndGet))
+                                               .toArray(CompletableFuture[]::new))
                 .thenApply(v -> totalSize.get());
     }
 
@@ -136,10 +135,10 @@ public class PartitionedDatabase implements Database {
         checkState(isOpen.get(), DB_NOT_OPEN);
         AtomicBoolean containsValue = new AtomicBoolean(false);
         return CompletableFuture.allOf(partitions
-                    .stream()
-                    .map(p -> p.mapContainsValue(mapName, value)
-                               .thenApply(v -> containsValue.compareAndSet(false, v)))
-                    .toArray(CompletableFuture[]::new))
+                                               .stream()
+                                               .map(p -> p.mapContainsValue(mapName, value)
+                                                       .thenApply(v -> containsValue.compareAndSet(false, v)))
+                                               .toArray(CompletableFuture[]::new))
                 .thenApply(v -> containsValue.get());
     }
 
@@ -196,9 +195,9 @@ public class PartitionedDatabase implements Database {
         checkState(isOpen.get(), DB_NOT_OPEN);
         Set<Entry<String, Versioned<byte[]>>> entrySet = Sets.newConcurrentHashSet();
         return CompletableFuture.allOf(partitions
-                    .stream()
-                    .map(p -> p.mapEntrySet(mapName).thenApply(entrySet::addAll))
-                    .toArray(CompletableFuture[]::new))
+                                               .stream()
+                                               .map(p -> p.mapEntrySet(mapName).thenApply(entrySet::addAll))
+                                               .toArray(CompletableFuture[]::new))
                 .thenApply(v -> entrySet);
     }
 
@@ -220,6 +219,19 @@ public class PartitionedDatabase implements Database {
         return partitioner.getPartition(counterName, counterName).counterGetAndAdd(counterName, delta);
     }
 
+    @Override
+    public CompletableFuture<Void> counterSet(String counterName, long value) {
+        checkState(isOpen.get(), DB_NOT_OPEN);
+        return partitioner.getPartition(counterName, counterName).counterSet(counterName, value);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> counterCompareAndSet(String counterName, long expectedValue, long updateValue) {
+        checkState(isOpen.get(), DB_NOT_OPEN);
+        return partitioner.getPartition(counterName, counterName).
+                counterCompareAndSet(counterName, expectedValue, updateValue);
+
+    }
 
     @Override
     public CompletableFuture<Long> queueSize(String queueName) {
@@ -268,8 +280,8 @@ public class PartitionedDatabase implements Database {
         AtomicBoolean status = new AtomicBoolean(true);
         return CompletableFuture.allOf(subTransactions.entrySet()
                 .stream()
-                .map(entry -> entry
-                        .getKey()
+                                               .map(entry -> entry
+                                                       .getKey()
                         .prepare(entry.getValue())
                         .thenApply(v -> status.compareAndSet(true, v)))
                 .toArray(CompletableFuture[]::new))
@@ -282,15 +294,15 @@ public class PartitionedDatabase implements Database {
         AtomicBoolean success = new AtomicBoolean(true);
         List<UpdateResult<String, byte[]>> allUpdates = Lists.newArrayList();
         return CompletableFuture.allOf(subTransactions.entrySet()
-                                   .stream()
-                                   .map(entry -> entry.getKey().commit(entry.getValue())
-                                                           .thenAccept(response -> {
-                                                               success.set(success.get() && response.success());
-                                                               if (success.get()) {
-                                                                   allUpdates.addAll(response.updates());
-                                                               }
-                                                           }))
-                                   .toArray(CompletableFuture[]::new))
+                                               .stream()
+                                               .map(entry -> entry.getKey().commit(entry.getValue())
+                                                       .thenAccept(response -> {
+                                                           success.set(success.get() && response.success());
+                                                           if (success.get()) {
+                                                               allUpdates.addAll(response.updates());
+                                                           }
+                                                       }))
+                                               .toArray(CompletableFuture[]::new))
                                .thenApply(v -> success.get() ?
                                        CommitResponse.success(allUpdates) : CommitResponse.failure());
     }
@@ -301,7 +313,7 @@ public class PartitionedDatabase implements Database {
         return CompletableFuture.allOf(subTransactions.entrySet()
                 .stream()
                 .map(entry -> entry.getKey().rollback(entry.getValue()))
-                .toArray(CompletableFuture[]::new))
+                                               .toArray(CompletableFuture[]::new))
             .thenApply(v -> true);
     }
 
@@ -384,3 +396,4 @@ public class PartitionedDatabase implements Database {
         partitions.forEach(p -> p.unregisterConsumer(consumer));
     }
 }
+
index 2859b62..f1e0dbd 100644 (file)
@@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-
 import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.util.AbstractAccumulator;
 import org.onlab.util.KryoNamespace;
@@ -33,18 +32,15 @@ import org.onosproject.store.Timestamp;
 import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
 import org.onosproject.store.cluster.messaging.MessageSubject;
 import org.onosproject.store.impl.LogicalTimestamp;
-import org.onosproject.store.service.WallClockTimestamp;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.serializers.KryoSerializer;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMapEvent;
 import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.WallClockTimestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
-import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -67,6 +63,8 @@ import static com.google.common.base.Preconditions.checkState;
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
+import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE;
 
 /**
  * Distributed Map implementation which uses optimistic replication and gossip
@@ -359,7 +357,7 @@ public class EventuallyConsistentMapImpl<K, V>
                 valueMatches = Objects.equals(value.get(), existing.get());
             }
             if (existing == null) {
-                log.debug("ECMap Remove: Existing value for key {} is already null", k);
+                log.trace("ECMap Remove: Existing value for key {} is already null", k);
             }
             if (valueMatches) {
                 if (existing == null) {
@@ -523,7 +521,7 @@ public class EventuallyConsistentMapImpl<K, V>
             return;
         }
         peers.forEach(node ->
-            senderPending.computeIfAbsent(node, unusedKey -> new EventAccumulator(node)).add(event)
+                        senderPending.computeIfAbsent(node, unusedKey -> new EventAccumulator(node)).add(event)
         );
     }
 
@@ -576,8 +574,10 @@ public class EventuallyConsistentMapImpl<K, V>
             return;
         }
         try {
-            log.debug("Received anti-entropy advertisement from {} for {} with {} entries in it",
-                    mapName, ad.sender(), ad.digest().size());
+            if (log.isTraceEnabled()) {
+                log.trace("Received anti-entropy advertisement from {} for {} with {} entries in it",
+                        mapName, ad.sender(), ad.digest().size());
+            }
             antiEntropyCheckLocalItems(ad).forEach(this::notifyListeners);
 
             if (!lightweightAntiEntropy) {
@@ -675,4 +675,4 @@ public class EventuallyConsistentMapImpl<K, V>
             });
         }
     }
-}
\ No newline at end of file
+}
index de7a3ac..8cd63e7 100644 (file)
@@ -16,6 +16,7 @@
 package org.onosproject.store.flow.impl;
 
 import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
@@ -57,6 +58,7 @@ import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.flow.FlowRuleStore;
 import org.onosproject.net.flow.FlowRuleStoreDelegate;
 import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.store.AbstractStore;
 import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
 import org.onosproject.store.cluster.messaging.ClusterMessage;
@@ -64,9 +66,16 @@ import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
 import org.onosproject.store.flow.ReplicaInfoEvent;
 import org.onosproject.store.flow.ReplicaInfoEventListener;
 import org.onosproject.store.flow.ReplicaInfoService;
+import org.onosproject.store.impl.MastershipBasedTimestamp;
+import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.serializers.KryoSerializer;
 import org.onosproject.store.serializers.StoreSerializer;
 import org.onosproject.store.serializers.custom.DistributedStoreSerializers;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
@@ -151,6 +160,13 @@ public class NewDistributedFlowRuleStore
     private final ScheduledExecutorService backupSenderExecutor =
             Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/flow", "backup-sender"));
 
+    private EventuallyConsistentMap<DeviceId, List<TableStatisticsEntry>> deviceTableStats;
+    private final EventuallyConsistentMapListener<DeviceId, List<TableStatisticsEntry>> tableStatsListener =
+            new InternalTableStatsListener();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
     protected static final StoreSerializer SERIALIZER = new KryoSerializer() {
         @Override
         protected void setupKryoPool() {
@@ -161,6 +177,11 @@ public class NewDistributedFlowRuleStore
         }
     };
 
+    protected static final KryoNamespace.Builder SERIALIZER_BUILDER = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(MastershipBasedTimestamp.class);
+
+
     private IdGenerator idGenerator;
     private NodeId local;
 
@@ -186,6 +207,15 @@ public class NewDistributedFlowRuleStore
                     TimeUnit.MILLISECONDS);
         }
 
+        deviceTableStats = storageService.<DeviceId, List<TableStatisticsEntry>>eventuallyConsistentMapBuilder()
+                .withName("onos-flow-table-stats")
+                .withSerializer(SERIALIZER_BUILDER)
+                .withAntiEntropyPeriod(5, TimeUnit.SECONDS)
+                .withTimestampProvider((k, v) -> new WallClockTimestamp())
+                .withTombstonesDisabled()
+                .build();
+        deviceTableStats.addListener(tableStatsListener);
+
         logConfig("Started");
     }
 
@@ -197,6 +227,8 @@ public class NewDistributedFlowRuleStore
         }
         configService.unregisterProperties(getClass(), false);
         unregisterMessageHandlers();
+        deviceTableStats.removeListener(tableStatsListener);
+        deviceTableStats.destroy();
         messageHandlingExecutor.shutdownNow();
         backupSenderExecutor.shutdownNow();
         log.info("Stopped");
@@ -786,4 +818,36 @@ public class NewDistributedFlowRuleStore
             return backedupDevices;
         }
     }
+
+    @Override
+    public FlowRuleEvent updateTableStatistics(DeviceId deviceId,
+                                               List<TableStatisticsEntry> tableStats) {
+        deviceTableStats.put(deviceId, tableStats);
+        return null;
+    }
+
+    @Override
+    public Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId) {
+        NodeId master = mastershipService.getMasterFor(deviceId);
+
+        if (master == null) {
+            log.debug("Failed to getTableStats: No master for {}", deviceId);
+            return Collections.emptyList();
+        }
+
+        List<TableStatisticsEntry> tableStats = deviceTableStats.get(deviceId);
+        if (tableStats == null) {
+            return Collections.emptyList();
+        }
+        return ImmutableList.copyOf(tableStats);
+    }
+
+    private class InternalTableStatsListener
+        implements EventuallyConsistentMapListener<DeviceId, List<TableStatisticsEntry>> {
+        @Override
+        public void event(EventuallyConsistentMapEvent<DeviceId,
+                          List<TableStatisticsEntry>> event) {
+            //TODO: Generate an event to listeners (do we need?)
+        }
+    }
 }
index 97333eb..a999ee7 100644 (file)
@@ -28,19 +28,11 @@ import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.KryoNamespace;
 import org.onlab.util.NewConcurrentHashMap;
 import org.onosproject.cluster.ClusterService;
-import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.MastershipRole;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.L0ModificationInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.group.DefaultGroup;
 import org.onosproject.net.group.DefaultGroupBucket;
 import org.onosproject.net.group.DefaultGroupDescription;
@@ -61,9 +53,7 @@ import org.onosproject.net.group.StoredGroupEntry;
 import org.onosproject.store.AbstractStore;
 import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
 import org.onosproject.store.service.MultiValuedTimestamp;
-import org.onosproject.store.serializers.DeviceIdSerializer;
 import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.serializers.URISerializer;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
 import org.onosproject.store.service.EventuallyConsistentMapEvent;
@@ -71,7 +61,6 @@ import org.onosproject.store.service.EventuallyConsistentMapListener;
 import org.onosproject.store.service.StorageService;
 import org.slf4j.Logger;
 
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -139,9 +128,12 @@ public class DistributedGroupStore
 
     private final AtomicLong sequenceNumber = new AtomicLong(0);
 
+    private KryoNamespace clusterMsgSerializer;
+
     @Activate
     public void activate() {
         kryoBuilder = new KryoNamespace.Builder()
+            .register(KryoNamespaces.API)
             .register(DefaultGroup.class,
                       DefaultGroupBucket.class,
                       DefaultGroupDescription.class,
@@ -158,38 +150,9 @@ public class DistributedGroupStore
                       GroupStoreKeyMapKey.class,
                       GroupStoreIdMapKey.class,
                       GroupStoreMapKey.class
-                    )
-            .register(new URISerializer(), URI.class)
-            .register(new DeviceIdSerializer(), DeviceId.class)
-            .register(PortNumber.class)
-            .register(DefaultApplicationId.class)
-            .register(DefaultTrafficTreatment.class,
-                      Instructions.DropInstruction.class,
-                      Instructions.OutputInstruction.class,
-                      Instructions.GroupInstruction.class,
-                      Instructions.TableTypeTransition.class,
-                      FlowRule.Type.class,
-                      L0ModificationInstruction.class,
-                      L0ModificationInstruction.L0SubType.class,
-                      L0ModificationInstruction.ModLambdaInstruction.class,
-                      L2ModificationInstruction.class,
-                      L2ModificationInstruction.L2SubType.class,
-                      L2ModificationInstruction.ModEtherInstruction.class,
-                      L2ModificationInstruction.PushHeaderInstructions.class,
-                      L2ModificationInstruction.ModVlanIdInstruction.class,
-                      L2ModificationInstruction.ModVlanPcpInstruction.class,
-                      L2ModificationInstruction.ModMplsLabelInstruction.class,
-                      L2ModificationInstruction.ModMplsTtlInstruction.class,
-                      L3ModificationInstruction.class,
-                      L3ModificationInstruction.L3SubType.class,
-                      L3ModificationInstruction.ModIPInstruction.class,
-                      L3ModificationInstruction.ModIPv6FlowLabelInstruction.class,
-                      L3ModificationInstruction.ModTtlInstruction.class,
-                      org.onlab.packet.MplsLabel.class
-                    )
-            .register(org.onosproject.cluster.NodeId.class)
-            .register(KryoNamespaces.BASIC)
-            .register(KryoNamespaces.MISC);
+            );
+
+        clusterMsgSerializer = kryoBuilder.build();
 
         messageHandlingExecutor = Executors.
                 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
@@ -197,7 +160,7 @@ public class DistributedGroupStore
                                                   "message-handlers"));
 
         clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
-                kryoBuilder.build()::deserialize,
+                clusterMsgSerializer::deserialize,
                 this::process,
                 messageHandlingExecutor);
 
@@ -233,6 +196,7 @@ public class DistributedGroupStore
 
     @Deactivate
     public void deactivate() {
+        clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
         groupStoreEntriesByKey.destroy();
         auditPendingReqQueue.destroy();
         log.info("Stopped");
@@ -313,8 +277,6 @@ public class DistributedGroupStore
     @Override
     public Iterable<Group> getGroups(DeviceId deviceId) {
         // flatten and make iterator unmodifiable
-        log.debug("getGroups: for device {} total number of groups {}",
-                  deviceId, getGroupStoreKeyMap().values().size());
         return FluentIterable.from(getGroupStoreKeyMap().values())
                 .filter(input -> input.deviceId().equals(deviceId))
                 .transform(input -> input);
@@ -322,8 +284,6 @@ public class DistributedGroupStore
 
     private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
         // flatten and make iterator unmodifiable
-        log.debug("getGroups: for device {} total number of groups {}",
-                  deviceId, getGroupStoreKeyMap().values().size());
         return FluentIterable.from(getGroupStoreKeyMap().values())
                 .filter(input -> input.deviceId().equals(deviceId));
     }
@@ -411,7 +371,7 @@ public class DistributedGroupStore
 
             clusterCommunicator.unicast(groupOp,
                     GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
-                    m -> kryoBuilder.build().serialize(m),
+                    clusterMsgSerializer::serialize,
                     mastershipService.getMasterFor(groupDesc.deviceId())).whenComplete((result, error) -> {
                         if (error != null) {
                             log.warn("Failed to send request to master: {} to {}",
@@ -609,7 +569,7 @@ public class DistributedGroupStore
 
             clusterCommunicator.unicast(groupOp,
                     GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
-                    m -> kryoBuilder.build().serialize(m),
+                    clusterMsgSerializer::serialize,
                     mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
                         if (error !=  null) {
                             log.warn("Failed to send request to master: {} to {}",
@@ -741,7 +701,7 @@ public class DistributedGroupStore
 
             clusterCommunicator.unicast(groupOp,
                     GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
-                    m -> kryoBuilder.build().serialize(m),
+                    clusterMsgSerializer::serialize,
                     mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
                         if (error != null) {
                             log.warn("Failed to send request to master: {} to {}",
index d0b827c..f9c9689 100644 (file)
@@ -27,6 +27,7 @@ import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.RE
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
@@ -67,7 +68,6 @@ import org.onosproject.store.service.StorageService;
 import org.slf4j.Logger;
 
 import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.SetMultimap;
@@ -196,6 +196,35 @@ public class ECHostStore
         return host != null ? new HostEvent(HOST_REMOVED, host) : null;
     }
 
+    @Override
+    public HostEvent removeIp(HostId hostId, IpAddress ipAddress) {
+        DefaultHost host = hosts.compute(hostId, (id, existingHost) -> {
+            if (existingHost != null) {
+                checkState(Objects.equals(hostId.mac(), existingHost.mac()),
+                        "Existing and new MAC addresses differ.");
+                checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()),
+                        "Existing and new VLANs differ.");
+
+                Set<IpAddress> addresses = existingHost.ipAddresses();
+                if (addresses != null && addresses.contains(ipAddress)) {
+                    addresses = new HashSet<>(existingHost.ipAddresses());
+                    addresses.remove(ipAddress);
+                    return new DefaultHost(existingHost.providerId(),
+                            hostId,
+                            existingHost.mac(),
+                            existingHost.vlan(),
+                            existingHost.location(),
+                            ImmutableSet.copyOf(addresses),
+                            existingHost.annotations());
+                } else {
+                    return existingHost;
+                }
+            }
+            return null;
+        });
+        return host != null ? new HostEvent(HOST_UPDATED, host) : null;
+    }
+
     @Override
     public int getHostCount() {
         return hosts.size();
@@ -228,17 +257,23 @@ public class ECHostStore
 
     @Override
     public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
-        return ImmutableSet.copyOf(locations.get(connectPoint));
+        synchronized (locations) {
+            return ImmutableSet.copyOf(locations.get(connectPoint));
+        }
     }
 
     @Override
     public Set<Host> getConnectedHosts(DeviceId deviceId) {
-        return ImmutableMultimap.copyOf(locations)
-                .entries()
-                .stream()
-                .filter(entry -> entry.getKey().deviceId().equals(deviceId))
-                .map(entry -> entry.getValue())
-                .collect(Collectors.toSet());
+        Set<Host> filtered;
+        synchronized (locations) {
+            filtered = locations
+                    .entries()
+                    .stream()
+                    .filter(entry -> entry.getKey().deviceId().equals(deviceId))
+                    .map(entry -> entry.getValue())
+                    .collect(Collectors.toSet());
+        }
+        return ImmutableSet.copyOf(filtered);
     }
 
     private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) {
index fa3a075..1e5db99 100644 (file)
@@ -193,7 +193,7 @@ public class GossipIntentStore
     private Collection<NodeId> getPeerNodes(Key key, IntentData data) {
         NodeId master = partitionService.getLeader(key);
         NodeId origin = (data != null) ? data.origin() : null;
-        if (master == null || origin == null) {
+        if (data != null && (master == null || origin == null)) {
             log.debug("Intent {} missing master and/or origin; master = {}, origin = {}",
                       key, master, origin);
         }
index 105c77d..47aa85c 100644 (file)
@@ -826,7 +826,7 @@ public class GossipLinkStore
         public void handle(ClusterMessage message) {
 
             log.trace("Received link event from peer: {}", message.sender());
-            InternalLinkEvent event = (InternalLinkEvent) SERIALIZER.decode(message.payload());
+            InternalLinkEvent event = SERIALIZER.decode(message.payload());
 
             ProviderId providerId = event.providerId();
             Timestamped<LinkDescription> linkDescription = event.linkDescription();
@@ -845,7 +845,7 @@ public class GossipLinkStore
         public void handle(ClusterMessage message) {
 
             log.trace("Received link removed event from peer: {}", message.sender());
-            InternalLinkRemovedEvent event = (InternalLinkRemovedEvent) SERIALIZER.decode(message.payload());
+            InternalLinkRemovedEvent event = SERIALIZER.decode(message.payload());
 
             LinkKey linkKey = event.linkKey();
             Timestamp timestamp = event.timestamp();
index d4c89c9..f0f3eb5 100644 (file)
@@ -15,7 +15,9 @@
  */
 package org.onosproject.store.packet.impl;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -41,14 +43,13 @@ import org.onosproject.store.serializers.KryoSerializer;
 import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.Versioned;
 import org.slf4j.Logger;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.onlab.util.Tools.groupedThreads;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -117,6 +118,7 @@ public class DistributedPacketStore
     public void deactivate() {
         communicationService.removeSubscriber(PACKET_OUT_SUBJECT);
         messageHandlingExecutor.shutdown();
+        tracker = null;
         log.info("Stopped");
     }
 
@@ -143,13 +145,13 @@ public class DistributedPacketStore
     }
 
     @Override
-    public boolean requestPackets(PacketRequest request) {
-        return tracker.add(request);
+    public void requestPackets(PacketRequest request) {
+        tracker.add(request);
     }
 
     @Override
-    public boolean cancelPackets(PacketRequest request) {
-        return tracker.remove(request);
+    public void cancelPackets(PacketRequest request) {
+        tracker.remove(request);
     }
 
     @Override
@@ -169,33 +171,50 @@ public class DistributedPacketStore
                     .build();
         }
 
-        public boolean add(PacketRequest request) {
-            Versioned<Set<PacketRequest>> old = requests.get(request.selector());
-            if (old != null && old.value().contains(request)) {
-                return false;
+        public void add(PacketRequest request) {
+            AtomicBoolean firstRequest = new AtomicBoolean(false);
+            requests.compute(request.selector(), (s, existingRequests) -> {
+                if (existingRequests == null) {
+                    firstRequest.set(true);
+                    return ImmutableSet.of(request);
+                } else if (!existingRequests.contains(request)) {
+                    return ImmutableSet.<PacketRequest>builder()
+                                       .addAll(existingRequests)
+                                       .add(request)
+                                       .build();
+                } else {
+                    return existingRequests;
+                }
+            });
+
+            if (firstRequest.get() && delegate != null) {
+                // The instance that makes the first request will push to all devices
+                delegate.requestPackets(request);
             }
-            // FIXME: add retry logic using a random delay
-            Set<PacketRequest> newSet = new HashSet<>();
-            newSet.add(request);
-            if (old == null) {
-                return requests.putIfAbsent(request.selector(), newSet) == null;
-            }
-            newSet.addAll(old.value());
-            return requests.replace(request.selector(), old.version(), newSet);
         }
 
-        public boolean remove(PacketRequest request) {
-            Versioned<Set<PacketRequest>> old = requests.get(request.selector());
-            if (old == null || !old.value().contains(request)) {
-                return false;
-            }
-            // FIXME: add retry logic using a random delay
-            Set<PacketRequest> newSet = new HashSet<>(old.value());
-            newSet.remove(request);
-            if (newSet.isEmpty()) {
-                return requests.remove(request.selector(), old.version());
+        public void remove(PacketRequest request) {
+            AtomicBoolean removedLast = new AtomicBoolean(false);
+            requests.computeIfPresent(request.selector(), (s, existingRequests) -> {
+                if (existingRequests.contains(request)) {
+                    Set<PacketRequest> newRequests = Sets.newHashSet(existingRequests);
+                    newRequests.remove(request);
+                    if (newRequests.size() > 0) {
+                        return ImmutableSet.copyOf(newRequests);
+                    } else {
+                        removedLast.set(true);
+                        return null;
+                    }
+                } else {
+                    return existingRequests;
+                }
+            });
+
+            if (removedLast.get() && delegate != null) {
+                // The instance that removes the last request will remove from all devices
+                delegate.cancelPackets(request);
             }
-            return requests.replace(request.selector(), old.version(), newSet);
+
         }
 
         public List<PacketRequest> requests() {
@@ -204,6 +223,5 @@ public class DistributedPacketStore
             list.sort((o1, o2) -> o1.priority().priorityValue() - o2.priority().priorityValue());
             return list;
         }
-
     }
 }
diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentIntentSetMultimap.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentIntentSetMultimap.java
new file mode 100644 (file)
index 0000000..87e6721
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.resource.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.resource.device.IntentSetMultimap;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * A collection that maps Intent IDs as keys to values as Intent IDs,
+ * where each key may associated with multiple values without duplication.
+ */
+@Component(immediate = true, enabled = true)
+@Service
+public class ConsistentIntentSetMultimap implements IntentSetMultimap {
+    private final Logger log = getLogger(getClass());
+
+    private static final String INTENT_MAPPING = "IntentMapping";
+
+    private static final Serializer SERIALIZER = Serializer.using(KryoNamespaces.API);
+
+    private ConsistentMap<IntentId, Set<IntentId>> intentMapping;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Activate
+    public void activate() {
+        intentMapping = storageService.<IntentId, Set<IntentId>>consistentMapBuilder()
+                .withName(INTENT_MAPPING)
+                .withSerializer(SERIALIZER)
+                .build();
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public Set<IntentId> getMapping(IntentId intentId) {
+        Versioned<Set<IntentId>> result = intentMapping.get(intentId);
+
+        if (result != null) {
+            return result.value();
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean allocateMapping(IntentId keyIntentId, IntentId valIntentId) {
+        Versioned<Set<IntentId>> versionedIntents = intentMapping.get(keyIntentId);
+
+        if (versionedIntents == null) {
+            Set<IntentId> newSet = new HashSet<>();
+            newSet.add(valIntentId);
+            intentMapping.put(keyIntentId, newSet);
+        } else {
+            versionedIntents.value().add(valIntentId);
+        }
+
+        return true;
+    }
+
+    @Override
+    public void releaseMapping(IntentId intentId) {
+        for (IntentId intent : intentMapping.keySet()) {
+            // TODO: optimize by checking for identical src & dst
+            Set<IntentId> mapping = intentMapping.get(intent).value();
+            if (mapping.remove(intentId)) {
+                return;
+            }
+        }
+    }
+
+}
index 3a29635..11137aa 100644 (file)
@@ -40,7 +40,6 @@ import org.onosproject.net.Link;
 import org.onosproject.net.LinkKey;
 import org.onosproject.net.Port;
 import org.onosproject.net.intent.IntentId;
-import org.onosproject.net.link.LinkService;
 import org.onosproject.net.resource.link.BandwidthResource;
 import org.onosproject.net.resource.link.BandwidthResourceAllocation;
 import org.onosproject.net.resource.link.LambdaResource;
@@ -69,7 +68,6 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
 import static org.slf4j.LoggerFactory.getLogger;
 import static org.onosproject.net.AnnotationKeys.BANDWIDTH;
 
@@ -107,9 +105,6 @@ public class ConsistentLinkResourceStore extends
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected StorageService storageService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected LinkService linkService;
-
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
 
@@ -139,29 +134,30 @@ public class ConsistentLinkResourceStore extends
         return storageService.transactionContextBuilder().build();
     }
 
-    private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
-        if (type == ResourceType.BANDWIDTH) {
-            return ImmutableSet.of(getBandwidthResourceCapacity(link));
-        }
-        if (type == ResourceType.LAMBDA) {
-            return getLambdaResourceCapacity(link);
+    private Set<ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
+        switch (type) {
+            case BANDWIDTH:
+                return ImmutableSet.of(getBandwidthResourceCapacity(link));
+            case LAMBDA:
+                return getLambdaResourceCapacity(link);
+            case MPLS_LABEL:
+                return getMplsResourceCapacity();
+            default:
+                return ImmutableSet.of();
         }
-        if (type == ResourceType.MPLS_LABEL) {
-            return getMplsResourceCapacity();
-        }
-        return ImmutableSet.of();
     }
 
-    private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
-        Set<LambdaResourceAllocation> allocations = new HashSet<>();
+    private Set<ResourceAllocation> getLambdaResourceCapacity(Link link) {
         Port port = deviceService.getPort(link.src().deviceId(), link.src().port());
-        if (port instanceof OmsPort) {
-            OmsPort omsPort = (OmsPort) port;
+        if (!(port instanceof OmsPort)) {
+            return Collections.emptySet();
+        }
 
-            // Assume fixed grid for now
-            for (int i = 0; i < omsPort.totalChannels(); i++) {
-                allocations.add(new LambdaResourceAllocation(LambdaResource.valueOf(i)));
-            }
+        OmsPort omsPort = (OmsPort) port;
+        Set<ResourceAllocation> allocations = new HashSet<>();
+        // Assume fixed grid for now
+        for (int i = 0; i < omsPort.totalChannels(); i++) {
+            allocations.add(new LambdaResourceAllocation(LambdaResource.valueOf(i)));
         }
         return allocations;
     }
@@ -170,26 +166,23 @@ public class ConsistentLinkResourceStore extends
 
         // if Link annotation exist, use them
         // if all fails, use DEFAULT_BANDWIDTH
-        BandwidthResource bandwidth = null;
+        BandwidthResource bandwidth = DEFAULT_BANDWIDTH;
         String strBw = link.annotations().value(BANDWIDTH);
-        if (strBw != null) {
-            try {
-                bandwidth = new BandwidthResource(Bandwidth.mbps(Double.parseDouble(strBw)));
-            } catch (NumberFormatException e) {
-                // do nothings
-                bandwidth = null;
-            }
+        if (strBw == null) {
+            return new BandwidthResourceAllocation(bandwidth);
         }
 
-        if (bandwidth == null) {
-            // fall back, use fixed default
+        try {
+            bandwidth = new BandwidthResource(Bandwidth.mbps(Double.parseDouble(strBw)));
+        } catch (NumberFormatException e) {
+            // do nothings, use default bandwidth
             bandwidth = DEFAULT_BANDWIDTH;
         }
         return new BandwidthResourceAllocation(bandwidth);
     }
 
-    private Set<MplsLabelResourceAllocation> getMplsResourceCapacity() {
-        Set<MplsLabelResourceAllocation> allocations = new HashSet<>();
+    private Set<ResourceAllocation> getMplsResourceCapacity() {
+        Set<ResourceAllocation> allocations = new HashSet<>();
         //Ignoring reserved labels of 0 through 15
         for (int i = MIN_UNRESERVED_LABEL; i <= MAX_UNRESERVED_LABEL; i++) {
             allocations.add(new MplsLabelResourceAllocation(MplsLabel
@@ -199,13 +192,11 @@ public class ConsistentLinkResourceStore extends
         return allocations;
     }
 
-    private Map<ResourceType, Set<? extends ResourceAllocation>> getResourceCapacity(Link link) {
-        Map<ResourceType, Set<? extends ResourceAllocation>> caps = new HashMap<>();
+    private Map<ResourceType, Set<ResourceAllocation>> getResourceCapacity(Link link) {
+        Map<ResourceType, Set<ResourceAllocation>> caps = new HashMap<>();
         for (ResourceType type : ResourceType.values()) {
-            Set<? extends ResourceAllocation> cap = getResourceCapacity(type, link);
-            if (cap != null) {
-                caps.put(type, cap);
-            }
+            Set<ResourceAllocation> cap = getResourceCapacity(type, link);
+            caps.put(type, cap);
         }
         return caps;
     }
@@ -216,106 +207,80 @@ public class ConsistentLinkResourceStore extends
 
         tx.begin();
         try {
-            Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link);
-            Set<ResourceAllocation> allFree = new HashSet<>();
-            freeResources.values().forEach(allFree::addAll);
-            return allFree;
+            Map<ResourceType, Set<ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link);
+            return freeResources.values().stream()
+                    .flatMap(Collection::stream)
+                    .collect(Collectors.toSet());
         } finally {
             tx.abort();
         }
     }
 
-    private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) {
+    private Map<ResourceType, Set<ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) {
         checkNotNull(tx);
         checkNotNull(link);
 
-        Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>();
-        final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link);
-        final Iterable<LinkResourceAllocations> allocations = getAllocations(tx, link);
+        Map<ResourceType, Set<ResourceAllocation>> free = new HashMap<>();
+        final Map<ResourceType, Set<ResourceAllocation>> caps = getResourceCapacity(link);
+        final List<LinkResourceAllocations> allocations = ImmutableList.copyOf(getAllocations(tx, link));
 
-        for (ResourceType type : ResourceType.values()) {
-            // there should be class/category of resources
+        Set<ResourceAllocation> bw = caps.get(ResourceType.BANDWIDTH);
+        Set<ResourceAllocation> value = getFreeBandwidthResources(link, bw, allocations);
+        free.put(ResourceType.BANDWIDTH, value);
 
-            switch (type) {
-                case BANDWIDTH:
-                    Set<? extends ResourceAllocation> bw = caps.get(type);
-                    if (bw == null || bw.isEmpty()) {
-                        bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
-                    }
+        Set<ResourceAllocation> lmd = caps.get(ResourceType.LAMBDA);
+        Set<ResourceAllocation> freeL = getFreeResources(link, lmd, allocations,
+                LambdaResourceAllocation.class);
+        free.put(ResourceType.LAMBDA, freeL);
 
-                    BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
-                    double freeBw = cap.bandwidth().toDouble();
-
-                    // enumerate current allocations, subtracting resources
-                    for (LinkResourceAllocations alloc : allocations) {
-                        Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
-                        for (ResourceAllocation a : types) {
-                            if (a instanceof BandwidthResourceAllocation) {
-                                BandwidthResourceAllocation bwA = (BandwidthResourceAllocation) a;
-                                freeBw -= bwA.bandwidth().toDouble();
-                            }
-                        }
-                    }
+        Set<ResourceAllocation> mpls = caps.get(ResourceType.MPLS_LABEL);
+        Set<ResourceAllocation> freeLabel = getFreeResources(link, mpls, allocations,
+                MplsLabelResourceAllocation.class);
+        free.put(ResourceType.MPLS_LABEL, freeLabel);
 
-                    free.put(type, Sets.newHashSet(
-                            new BandwidthResourceAllocation(new BandwidthResource(Bandwidth.bps(freeBw)))));
-                    break;
-                case LAMBDA:
-                    Set<? extends ResourceAllocation> lmd = caps.get(type);
-                    if (lmd == null || lmd.isEmpty()) {
-                        // nothing left
-                        break;
-                    }
-                    Set<LambdaResourceAllocation> freeL = new HashSet<>();
-                    for (ResourceAllocation r : lmd) {
-                        if (r instanceof LambdaResourceAllocation) {
-                            freeL.add((LambdaResourceAllocation) r);
-                        }
-                    }
-
-                    // enumerate current allocations, removing resources
-                    for (LinkResourceAllocations alloc : allocations) {
-                        Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
-                        for (ResourceAllocation a : types) {
-                            if (a instanceof LambdaResourceAllocation) {
-                                freeL.remove(a);
-                            }
-                        }
-                    }
+        return free;
+    }
 
-                    free.put(type, freeL);
-                    break;
-                case MPLS_LABEL:
-                    Set<? extends ResourceAllocation> mpls = caps.get(type);
-                    if (mpls == null || mpls.isEmpty()) {
-                        // nothing left
-                        break;
-                    }
-                    Set<MplsLabelResourceAllocation> freeLabel = new HashSet<>();
-                    for (ResourceAllocation r : mpls) {
-                        if (r instanceof MplsLabelResourceAllocation) {
-                            freeLabel.add((MplsLabelResourceAllocation) r);
-                        }
-                    }
+    private Set<ResourceAllocation> getFreeBandwidthResources(Link link, Set<ResourceAllocation> bw,
+                                                              List<LinkResourceAllocations> allocations) {
+        if (bw == null || bw.isEmpty()) {
+            bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
+        }
 
-                    // enumerate current allocations, removing resources
-                    for (LinkResourceAllocations alloc : allocations) {
-                        Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
-                        for (ResourceAllocation a : types) {
-                            if (a instanceof MplsLabelResourceAllocation) {
-                                freeLabel.remove(a);
-                            }
-                        }
-                    }
+        BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
+        double freeBw = cap.bandwidth().toDouble();
+
+        // enumerate current allocations, subtracting resources
+        double allocatedBw = allocations.stream()
+                .flatMap(x -> x.getResourceAllocation(link).stream())
+                .filter(x -> x instanceof BandwidthResourceAllocation)
+                .map(x -> (BandwidthResourceAllocation) x)
+                .mapToDouble(x -> x.bandwidth().toDouble())
+                .sum();
+        freeBw -= allocatedBw;
+        return Sets.newHashSet(
+                new BandwidthResourceAllocation(new BandwidthResource(Bandwidth.bps(freeBw))));
+    }
 
-                    free.put(type, freeLabel);
-                    break;
-                default:
-                    log.debug("unsupported ResourceType {}", type);
-                    break;
-            }
+    private Set<ResourceAllocation> getFreeResources(Link link,
+                                                     Set<ResourceAllocation> resources,
+                                                     List<LinkResourceAllocations> allocations,
+                                                     Class<? extends ResourceAllocation> cls) {
+        if (resources == null || resources.isEmpty()) {
+            // nothing left
+            return Collections.emptySet();
         }
-        return free;
+        Set<ResourceAllocation> freeL = resources.stream()
+                .filter(cls::isInstance)
+                .collect(Collectors.toSet());
+
+        // enumerate current allocations, removing resources
+        List<ResourceAllocation> allocated = allocations.stream()
+                .flatMap(x -> x.getResourceAllocation(link).stream())
+                .filter(cls::isInstance)
+                .collect(Collectors.toList());
+        freeL.removeAll(allocated);
+        return freeL;
     }
 
     @Override
@@ -329,6 +294,9 @@ public class ConsistentLinkResourceStore extends
             intentAllocs.put(allocations.intentId(), allocations);
             allocations.links().forEach(link -> allocateLinkResource(tx, link, allocations));
             tx.commit();
+        } catch (TransactionException | ResourceAllocationException e) {
+            log.error("Exception thrown, rolling back", e);
+            tx.abort();
         } catch (Exception e) {
             log.error("Exception thrown, rolling back", e);
             tx.abort();
@@ -340,15 +308,13 @@ public class ConsistentLinkResourceStore extends
             LinkResourceAllocations allocations) {
         // requested resources
         Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link);
-        Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(tx, link);
+        Map<ResourceType, Set<ResourceAllocation>> available = getFreeResourcesEx(tx, link);
         for (ResourceAllocation req : reqs) {
-            Set<? extends ResourceAllocation> avail = available.get(req.type());
+            Set<ResourceAllocation> avail = available.get(req.type());
             if (req instanceof BandwidthResourceAllocation) {
                 // check if allocation should be accepted
                 if (avail.isEmpty()) {
-                    checkState(!avail.isEmpty(),
-                               "There's no Bandwidth resource on %s?",
-                               link);
+                    throw new ResourceAllocationException(String.format("There's no Bandwidth resource on %s?", link));
                 }
                 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
                 double bwLeft = bw.bandwidth().toDouble();
@@ -395,12 +361,7 @@ public class ConsistentLinkResourceStore extends
         if (before == null) {
             List<LinkResourceAllocations> after = new ArrayList<>();
             after.add(allocations);
-            before = linkAllocs.putIfAbsent(linkKey, after);
-            if (before != null) {
-                // concurrent allocation detected, retry transaction : is this needed?
-                log.warn("Concurrent Allocation, retrying");
-                throw new TransactionException();
-            }
+            linkAllocs.putIfAbsent(linkKey, after);
         } else {
             List<LinkResourceAllocations> after = new ArrayList<>(before.size() + 1);
             after.addAll(before);
@@ -500,19 +461,18 @@ public class ConsistentLinkResourceStore extends
         checkNotNull(link);
         final LinkKey key = LinkKey.linkKey(link);
         TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
-        List<LinkResourceAllocations> res = null;
 
-        res = linkAllocs.get(key);
-        if (res == null) {
-            res = linkAllocs.putIfAbsent(key, new ArrayList<>());
+        List<LinkResourceAllocations> res = linkAllocs.get(key);
+        if (res != null) {
+            return res;
+        }
 
-            if (res == null) {
-                return Collections.emptyList();
-            } else {
-                return res;
-            }
+        res = linkAllocs.putIfAbsent(key, new ArrayList<>());
+        if (res == null) {
+            return Collections.emptyList();
+        } else {
+            return res;
         }
-        return res;
     }
 
 }
diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedFlowStatisticStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedFlowStatisticStore.java
new file mode 100644 (file)
index 0000000..0cd4a83
--- /dev/null
@@ -0,0 +1,289 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.store.statistic.impl;\r
+\r
+import com.google.common.base.Objects;\r
+import org.apache.felix.scr.annotations.Activate;\r
+import org.apache.felix.scr.annotations.Component;\r
+import org.apache.felix.scr.annotations.Deactivate;\r
+import org.apache.felix.scr.annotations.Reference;\r
+import org.apache.felix.scr.annotations.ReferenceCardinality;\r
+import org.apache.felix.scr.annotations.Service;\r
+import org.onlab.util.KryoNamespace;\r
+import org.onlab.util.Tools;\r
+import org.onosproject.cluster.ClusterService;\r
+import org.onosproject.cluster.NodeId;\r
+import org.onosproject.mastership.MastershipService;\r
+import org.onosproject.net.ConnectPoint;\r
+import org.onosproject.net.DeviceId;\r
+import org.onosproject.net.PortNumber;\r
+import org.onosproject.net.flow.FlowEntry;\r
+import org.onosproject.net.flow.FlowRule;\r
+import org.onosproject.net.flow.instructions.Instruction;\r
+import org.onosproject.net.flow.instructions.Instructions;\r
+import org.onosproject.net.statistic.FlowStatisticStore;\r
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;\r
+import org.onosproject.store.serializers.KryoNamespaces;\r
+import org.onosproject.store.serializers.KryoSerializer;\r
+import org.slf4j.Logger;\r
+\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+import java.util.Set;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import static org.onlab.util.Tools.groupedThreads;\r
+import static org.onosproject.store.statistic.impl.StatisticStoreMessageSubjects.GET_CURRENT;\r
+import static org.onosproject.store.statistic.impl.StatisticStoreMessageSubjects.GET_PREVIOUS;\r
+import static org.slf4j.LoggerFactory.getLogger;\r
+\r
+/**\r
+ * Maintains flow statistics using RPC calls to collect stats from remote instances\r
+ * on demand.\r
+ */\r
+@Component(immediate = true)\r
+@Service\r
+public class DistributedFlowStatisticStore implements FlowStatisticStore {\r
+    private final Logger log = getLogger(getClass());\r
+\r
+    // TODO: Make configurable.\r
+    private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 4;\r
+\r
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)\r
+    protected MastershipService mastershipService;\r
+\r
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)\r
+    protected ClusterCommunicationService clusterCommunicator;\r
+\r
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)\r
+    protected ClusterService clusterService;\r
+\r
+    private Map<ConnectPoint, Set<FlowEntry>> previous =\r
+            new ConcurrentHashMap<>();\r
+\r
+    private Map<ConnectPoint, Set<FlowEntry>> current =\r
+            new ConcurrentHashMap<>();\r
+\r
+    protected static final KryoSerializer SERIALIZER = new KryoSerializer() {\r
+        @Override\r
+        protected void setupKryoPool() {\r
+            serializerPool = KryoNamespace.newBuilder()\r
+                    .register(KryoNamespaces.API)\r
+                    .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)\r
+                            // register this store specific classes here\r
+                    .build();\r
+        }\r
+    };\r
+\r
+    private NodeId local;\r
+    private ExecutorService messageHandlingExecutor;\r
+\r
+    private static final long STATISTIC_STORE_TIMEOUT_MILLIS = 3000;\r
+\r
+    @Activate\r
+    public void activate() {\r
+        local = clusterService.getLocalNode().id();\r
+\r
+        messageHandlingExecutor = Executors.newFixedThreadPool(\r
+                MESSAGE_HANDLER_THREAD_POOL_SIZE,\r
+                groupedThreads("onos/store/statistic", "message-handlers"));\r
+\r
+        clusterCommunicator.addSubscriber(\r
+                GET_CURRENT, SERIALIZER::decode, this::getCurrentStatisticInternal, SERIALIZER::encode,\r
+                messageHandlingExecutor);\r
+\r
+        clusterCommunicator.addSubscriber(\r
+                GET_CURRENT, SERIALIZER::decode, this::getPreviousStatisticInternal, SERIALIZER::encode,\r
+                messageHandlingExecutor);\r
+\r
+        log.info("Started");\r
+    }\r
+\r
+    @Deactivate\r
+    public void deactivate() {\r
+        clusterCommunicator.removeSubscriber(GET_PREVIOUS);\r
+        clusterCommunicator.removeSubscriber(GET_CURRENT);\r
+        messageHandlingExecutor.shutdown();\r
+        log.info("Stopped");\r
+    }\r
+\r
+    @Override\r
+    public synchronized void removeFlowStatistic(FlowRule rule) {\r
+        ConnectPoint cp = buildConnectPoint(rule);\r
+        if (cp == null) {\r
+            return;\r
+        }\r
+\r
+        // remove this rule if present from current map\r
+        current.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e;  });\r
+\r
+        // remove this on if present from previous map\r
+        previous.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; });\r
+    }\r
+\r
+    @Override\r
+    public synchronized void addFlowStatistic(FlowEntry rule) {\r
+        ConnectPoint cp = buildConnectPoint(rule);\r
+        if (cp == null) {\r
+            return;\r
+        }\r
+\r
+        // create one if absent and add this rule\r
+        current.putIfAbsent(cp, new HashSet<>());\r
+        current.computeIfPresent(cp, (c, e) -> { e.add(rule); return e; });\r
+\r
+        // remove previous one if present\r
+        previous.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; });\r
+    }\r
+\r
+    public synchronized void updateFlowStatistic(FlowEntry rule) {\r
+        ConnectPoint cp = buildConnectPoint(rule);\r
+        if (cp == null) {\r
+            return;\r
+        }\r
+\r
+        Set<FlowEntry> curr = current.get(cp);\r
+        if (curr == null) {\r
+            addFlowStatistic(rule);\r
+        } else {\r
+            Optional<FlowEntry> f = curr.stream().filter(c -> rule.equals(c)).\r
+                    findAny();\r
+            if (f.isPresent() && rule.bytes() < f.get().bytes()) {\r
+                log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" +\r
+                        " Invalid Flow Update! Will be removed!!" +\r
+                        " curr flowId=" + Long.toHexString(rule.id().value()) +\r
+                        ", prev flowId=" + Long.toHexString(f.get().id().value()) +\r
+                        ", curr bytes=" + rule.bytes() + ", prev bytes=" + f.get().bytes() +\r
+                        ", curr life=" + rule.life() + ", prev life=" + f.get().life() +\r
+                        ", curr lastSeen=" + rule.lastSeen() + ", prev lastSeen=" + f.get().lastSeen());\r
+                // something is wrong! invalid flow entry, so delete it\r
+                removeFlowStatistic(rule);\r
+                return;\r
+            }\r
+            Set<FlowEntry> prev = previous.get(cp);\r
+            if (prev == null) {\r
+                prev = new HashSet<>();\r
+                previous.put(cp, prev);\r
+            }\r
+\r
+            // previous one is exist\r
+            if (f.isPresent()) {\r
+                // remove old one and add new one\r
+                prev.remove(rule);\r
+                if (!prev.add(f.get())) {\r
+                    log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" +\r
+                                    " flowId={}, add failed into previous.",\r
+                            Long.toHexString(rule.id().value()));\r
+                }\r
+            }\r
+\r
+            // remove old one and add new one\r
+            curr.remove(rule);\r
+            if (!curr.add(rule)) {\r
+                log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" +\r
+                                " flowId={}, add failed into current.",\r
+                        Long.toHexString(rule.id().value()));\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public Set<FlowEntry> getCurrentFlowStatistic(ConnectPoint connectPoint) {\r
+        final DeviceId deviceId = connectPoint.deviceId();\r
+\r
+        NodeId master = mastershipService.getMasterFor(deviceId);\r
+        if (master == null) {\r
+            log.warn("No master for {}", deviceId);\r
+            return Collections.emptySet();\r
+        }\r
+\r
+        if (Objects.equal(local, master)) {\r
+            return getCurrentStatisticInternal(connectPoint);\r
+        } else {\r
+            return Tools.futureGetOrElse(clusterCommunicator.sendAndReceive(\r
+                            connectPoint,\r
+                            GET_CURRENT,\r
+                            SERIALIZER::encode,\r
+                            SERIALIZER::decode,\r
+                            master),\r
+                    STATISTIC_STORE_TIMEOUT_MILLIS,\r
+                    TimeUnit.MILLISECONDS,\r
+                    Collections.emptySet());\r
+        }\r
+    }\r
+\r
+    private synchronized Set<FlowEntry> getCurrentStatisticInternal(ConnectPoint connectPoint) {\r
+        return current.get(connectPoint);\r
+    }\r
+\r
+    @Override\r
+    public Set<FlowEntry> getPreviousFlowStatistic(ConnectPoint connectPoint) {\r
+        final DeviceId deviceId = connectPoint.deviceId();\r
+\r
+        NodeId master = mastershipService.getMasterFor(deviceId);\r
+        if (master == null) {\r
+            log.warn("No master for {}", deviceId);\r
+            return Collections.emptySet();\r
+        }\r
+\r
+        if (Objects.equal(local, master)) {\r
+            return getPreviousStatisticInternal(connectPoint);\r
+        } else {\r
+            return Tools.futureGetOrElse(clusterCommunicator.sendAndReceive(\r
+                            connectPoint,\r
+                            GET_PREVIOUS,\r
+                            SERIALIZER::encode,\r
+                            SERIALIZER::decode,\r
+                            master),\r
+                    STATISTIC_STORE_TIMEOUT_MILLIS,\r
+                    TimeUnit.MILLISECONDS,\r
+                    Collections.emptySet());\r
+        }\r
+    }\r
+\r
+    private synchronized Set<FlowEntry> getPreviousStatisticInternal(ConnectPoint connectPoint) {\r
+        return previous.get(connectPoint);\r
+    }\r
+\r
+    private ConnectPoint buildConnectPoint(FlowRule rule) {\r
+        PortNumber port = getOutput(rule);\r
+\r
+        if (port == null) {\r
+            return null;\r
+        }\r
+        ConnectPoint cp = new ConnectPoint(rule.deviceId(), port);\r
+        return cp;\r
+    }\r
+\r
+    private PortNumber getOutput(FlowRule rule) {\r
+        for (Instruction i : rule.treatment().allInstructions()) {\r
+            if (i.type() == Instruction.Type.OUTPUT) {\r
+                Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;\r
+                return out.port();\r
+            }\r
+            if (i.type() == Instruction.Type.DROP) {\r
+                return PortNumber.P0;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+}
\ No newline at end of file
index 487fad9..da4e3cc 100644 (file)
@@ -21,6 +21,7 @@ import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Collections;
+import java.util.Map;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -40,6 +41,7 @@ import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
+import org.onosproject.net.DisjointPath;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.topology.ClusterId;
 import org.onosproject.net.topology.DefaultGraphDescription;
@@ -74,7 +76,6 @@ public class DistributedTopologyStore
         implements TopologyStore {
 
     private final Logger log = getLogger(getClass());
-
     private volatile DefaultTopology current =
             new DefaultTopology(ProviderId.NONE,
                                 new DefaultGraphDescription(0L, System.currentTimeMillis(),
@@ -166,6 +167,29 @@ public class DistributedTopologyStore
         return defaultTopology(topology).getPaths(src, dst, weight);
     }
 
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) {
+        return defaultTopology(topology).getDisjointPaths(src, dst);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                              LinkWeight weight) {
+        return defaultTopology(topology).getDisjointPaths(src, dst, weight);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                              Map<Link, Object> riskProfile) {
+        return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile);
+    }
+
+    @Override
+    public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst,
+                                              LinkWeight weight, Map<Link, Object> riskProfile) {
+        return defaultTopology(topology).getDisjointPaths(src, dst, weight, riskProfile);
+    }
+
     @Override
     public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
         return defaultTopology(topology).isInfrastructure(connectPoint);
diff --git a/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/host/impl/ECHostStoreTest.java b/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/host/impl/ECHostStoreTest.java
new file mode 100644 (file)
index 0000000..a7077a8
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.host.impl;
+
+import junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.Timestamp;
+import org.onosproject.store.service.LogicalClockService;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests for the ECHostStore.
+ */
+public class ECHostStoreTest extends TestCase {
+
+    private ECHostStore ecXHostStore;
+
+    private static final HostId HOSTID = HostId.hostId(MacAddress.valueOf("1a:1a:1a:1a:1a:1a"));
+
+    private static final IpAddress IP1 = IpAddress.valueOf("10.2.0.2");
+    private static final IpAddress IP2 = IpAddress.valueOf("10.2.0.3");
+
+    private static final ProviderId PID = new ProviderId("of", "foo");
+
+    @Before
+    public void setUp() {
+        ecXHostStore = new ECHostStore();
+
+        ecXHostStore.storageService = new TestStorageService();
+        ecXHostStore.clockService = new TestLogicalClockService();
+        ecXHostStore.activate();
+    }
+
+    @After
+    public void tearDown() {
+        ecXHostStore.deactivate();
+    }
+
+    /**
+     * Tests the removeIp method call.
+     */
+    @Test
+    public void testRemoveIp() {
+        Set<IpAddress> ips = new HashSet<>();
+        ips.add(IP1);
+        ips.add(IP2);
+
+        HostDescription description = new DefaultHostDescription(HOSTID.mac(),
+                                                                    HOSTID.vlanId(),
+                                                                    HostLocation.NONE,
+                                                                    ips);
+        ecXHostStore.createOrUpdateHost(PID, HOSTID, description, false);
+        ecXHostStore.removeIp(HOSTID, IP1);
+        Host host = ecXHostStore.getHost(HOSTID);
+
+        assertFalse(host.ipAddresses().contains(IP1));
+        assertTrue(host.ipAddresses().contains(IP2));
+    }
+
+    /**
+     * Mocks the LogicalClockService class.
+     */
+    class TestLogicalClockService implements LogicalClockService {
+        @Override
+        public Timestamp getTimestamp() {
+            return null;
+        }
+    }
+}
\ No newline at end of file
index 66ee7be..5b5056c 100644 (file)
@@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-
 import org.onlab.packet.ChassisId;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ip4Address;
@@ -85,11 +84,11 @@ import org.onosproject.net.device.PortStatistics;
 import org.onosproject.net.flow.CompletedBatchOperation;
 import org.onosproject.net.flow.DefaultFlowEntry;
 import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTableStatisticsEntry;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowId;
-import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleBatchEntry;
 import org.onosproject.net.flow.FlowRuleBatchEvent;
 import org.onosproject.net.flow.FlowRuleBatchOperation;
@@ -97,6 +96,7 @@ import org.onosproject.net.flow.FlowRuleBatchRequest;
 import org.onosproject.net.flow.FlowRuleEvent;
 import org.onosproject.net.flow.FlowRuleExtPayLoad;
 import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.EthCriterion;
 import org.onosproject.net.flow.criteria.EthTypeCriterion;
@@ -118,7 +118,6 @@ import org.onosproject.net.flow.criteria.MetadataCriterion;
 import org.onosproject.net.flow.criteria.MplsCriterion;
 import org.onosproject.net.flow.criteria.OchSignalCriterion;
 import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
-import org.onosproject.net.flow.criteria.OpticalSignalTypeCriterion;
 import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.SctpPortCriterion;
 import org.onosproject.net.flow.criteria.TcpPortCriterion;
@@ -302,7 +301,6 @@ public final class KryoNamespaces {
                     DefaultHostDescription.class,
                     DefaultFlowEntry.class,
                     StoredFlowEntry.class,
-                    FlowRule.Type.class,
                     DefaultFlowRule.class,
                     DefaultFlowEntry.class,
                     DefaultPacketRequest.class,
@@ -339,11 +337,11 @@ public final class KryoNamespaces {
                     IndexedLambdaCriterion.class,
                     OchSignalCriterion.class,
                     OchSignalTypeCriterion.class,
-                    OpticalSignalTypeCriterion.class,
                     Criterion.class,
                     Criterion.Type.class,
                     DefaultTrafficTreatment.class,
                     Instructions.DropInstruction.class,
+                    Instructions.NoActionInstruction.class,
                     Instructions.OutputInstruction.class,
                     Instructions.GroupInstruction.class,
                     Instructions.TableTypeTransition.class,
@@ -425,7 +423,9 @@ public final class KryoNamespaces {
                     DefaultAnnotations.class,
                     PortStatistics.class,
                     DefaultPortStatistics.class,
-                    IntentDomainId.class
+                    IntentDomainId.class,
+                    TableStatisticsEntry.class,
+                    DefaultTableStatisticsEntry.class
             )
             .register(new DefaultApplicationIdSerializer(), DefaultApplicationId.class)
             .register(new URISerializer(), URI.class)
index 34329f1..1890de1 100644 (file)
@@ -46,6 +46,9 @@ org.onosproject.pcep*
 org.onosproject.aaa
 org.onosproject.acl*
 org.onosproject.cip*
-org.onos.acl*
+org.onosproject.acl*
 org.onosproject.vtn*
+org.onosproject.cord*
+org.onosproject.mfwd*
+org.onosproject.mcast*
 org.onosproject.flowanalyzer
index 8c4a493..71aacd0 100644 (file)
@@ -1,6 +1,5 @@
 org.onosproject.app.*
 
-org.onos.acl*
 org.onosproject.acl*
 org.onosproject.aaa
 org.onosproject.fwd
@@ -25,3 +24,7 @@ org.onosproject.cordfabric*
 org.onosproject.xosintegration*
 org.onosproject.cip*
 org.onosproject.vtn*
+org.onosproject.cord*
+org.onosproject.mcast*
+org.onosproject.mfwd*
+org.onosproject.igmp.impl
index 749a68c..56a39a8 100644 (file)
@@ -55,8 +55,8 @@
          <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-core-serializers</artifactId>
-            <version>1.4.0-SNAPSHOT</version>
-        </dependency>
+             <version>${project.version}</version>
+         </dependency>
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-ovsdb-api</artifactId>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-ovsdb-api</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java
new file mode 100644 (file)
index 0000000..a62b93c
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.driver.handshaker;
+
+import org.projectfloodlight.openflow.protocol.OFExpPort;
+import org.projectfloodlight.openflow.protocol.OFExpPortDescReply;
+import org.projectfloodlight.openflow.protocol.OFExpPortDescRequest;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+import org.onosproject.net.Device;
+import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
+import org.onosproject.openflow.controller.PortDescPropertyType;
+import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
+import org.projectfloodlight.openflow.protocol.OFObject;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+
+/**
+ * Open Flow Optical Switch handshaker - for Open Flow 13.
+ */
+public class OFOpticalSwitch13 extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
+
+    private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
+    private List<OFExpPort> expPortDes = new ArrayList<>();
+
+    @Override
+    public Boolean supportNxRole() {
+        return false;
+    }
+
+    @Override
+    public void startDriverHandshake() {
+        log.info("Starting driver handshake for sw {}", getStringId());
+        if (startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeAlreadyStarted();
+        }
+        startDriverHandshakeCalled = true;
+
+        log.debug("sendHandshakeOFExperimenterPortDescRequest for sw {}", getStringId());
+
+        try {
+            sendHandshakeOFExperimenterPortDescRequest();
+        } catch (IOException e) {
+            log.error("Failed to send handshaker message OFExperimenterPortDescRequestfor sw {}, {}",
+                     getStringId(), e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+     @Override
+    public void processDriverHandshakeMessage(OFMessage m) {
+        if (!startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeNotStarted();
+        }
+        if (driverHandshakeComplete.get()) {
+            throw new SwitchDriverSubHandshakeCompleted(m);
+        }
+
+        log.debug("processDriverHandshakeMessage for sw {}", getStringId());
+
+        switch (m.getType()) {
+        case STATS_REPLY: // multipart message is reported as STAT
+            processOFMultipartReply((OFStatsReply) m);
+            break;
+        default:
+            log.warn("Received message {} during switch-driver " +
+                    "subhandshake " + "from switch {} ... " +
+                    "Ignoring message", m,
+                    getStringId());
+        }
+    }
+
+    private void processOFMultipartReply(OFStatsReply stats) {
+        log.debug("Received message {} during switch-driver " +
+                   "subhandshake " + "from switch {} ... " +
+                   stats,
+                   getStringId());
+
+         if (stats.getStatsType() == OFStatsType.EXPERIMENTER) {
+             try {
+               OFExpPortDescReply expPortDescReply =  (OFExpPortDescReply) stats;
+               expPortDes.addAll(expPortDescReply.getEntries());
+               if (!expPortDescReply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+                   driverHandshakeComplete.set(true);
+                   return;
+               }
+              } catch (ClassCastException e) {
+                  log.error("Unexspected Experimenter Multipart message type {} "
+                          , stats.getClass().getName());
+            }
+        }
+    }
+
+
+    @Override
+    public boolean isDriverHandshakeComplete() {
+        return driverHandshakeComplete.get();
+    }
+
+    private void sendHandshakeOFExperimenterPortDescRequest() throws
+            IOException {
+
+        OFExpPortDescRequest preq = factory()
+                .buildExpPortDescRequest()
+                .setXid(getNextTransactionId())
+                .build();
+
+        log.debug("Sending experimented port description " +
+                "message " +
+                "{}",
+                preq.toString());
+
+        this.sendHandshakeMessage(preq);
+    }
+
+    @Override
+    public Device.Type deviceType() {
+        return Device.Type.ROADM;
+    }
+
+    /*
+     * OduClt ports are reported as regular ETH ports.
+     */
+    @Override
+    public List<OFPortDesc> getPorts() {
+        return ImmutableList.copyOf(
+                ports.stream().flatMap(p -> p.getEntries().stream())
+                .collect(Collectors.toList()));
+    }
+
+    @Override
+    public List<? extends OFObject> getPortsOf(PortDescPropertyType type) {
+        return ImmutableList.copyOf(expPortDes);
+    }
+
+   @Override
+    public Set<PortDescPropertyType> getPortTypes() {
+        return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT);
+    }
+
+}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java
new file mode 100644 (file)
index 0000000..a00d3db
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.driver.ovsdb;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerConfig;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.ovsdb.controller.OvsdbBridge;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.onlab.util.Tools.delay;
+
+/**
+ * Implementation of controller config which allows to get and set controllers.
+ */
+public class OvsdbControllerConfig extends AbstractHandlerBehaviour implements ControllerConfig {
+    @Override
+    public List<ControllerInfo> getControllers() {
+        DriverHandler handler = handler();
+        OvsdbClientService clientService = getOvsdbClientService(handler);
+        Set<ControllerInfo> controllers = clientService.getControllers(
+                handler().data().deviceId());
+        return new ArrayList<>(controllers);
+    }
+
+    @Override
+    public void setControllers(List<ControllerInfo> controllers) {
+        DriverHandler handler = handler();
+        OvsdbClientService clientService = getOvsdbClientService(handler);
+        if (!clientService.getControllers(handler().data().deviceId())
+                .equals(controllers)) {
+            clientService.setControllersWithDeviceId(handler().
+                    data().deviceId(), controllers);
+        }
+    }
+
+    // Used for getting OvsdbClientService.
+    private OvsdbClientService getOvsdbClientService(DriverHandler handler) {
+        OvsdbController ovsController = handler.get(OvsdbController.class);
+        DeviceService deviceService = handler.get(DeviceService.class);
+        DeviceId ofDeviceId = handler.data().deviceId();
+        String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
+                .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
+        String targetIp = mgmtAddress[0];
+        TpPort targetPort = null;
+        if (mgmtAddress.length > 1) {
+            targetPort = TpPort.tpPort(Integer.parseInt(mgmtAddress[1]));
+        }
+
+        List<OvsdbNodeId> nodeIds = ovsController.getNodeIds().stream()
+                .filter(nodeId -> nodeId.getIpAddress().equals(targetIp))
+                .collect(Collectors.toList());
+        if (nodeIds.size() == 0) {
+            //TODO decide what port?
+            ovsController.connect(IpAddress.valueOf(targetIp),
+                                  targetPort == null ? TpPort.tpPort(6640) : targetPort);
+            delay(1000); //FIXME... connect is async
+        }
+        List<OvsdbClientService> clientServices = ovsController.getNodeIds().stream()
+                .filter(nodeId -> nodeId.getIpAddress().equals(targetIp))
+                .map(ovsController::getOvsdbClient)
+                .filter(cs -> cs.getBridges().stream().anyMatch(b -> dpidMatches(b, ofDeviceId)))
+                .collect(Collectors.toList());
+        checkState(clientServices.size() > 0, "No clientServices found");
+        //FIXME add connection to management address if null --> done ?
+        return clientServices.size() > 0 ? clientServices.get(0) : null;
+    }
+
+    private static boolean dpidMatches(OvsdbBridge bridge, DeviceId deviceId) {
+        String bridgeDpid = "of:" + bridge.datapathId().value();
+        String ofDpid = deviceId.toString();
+        return bridgeDpid.equals(ofDpid);
+    }
+}
\ No newline at end of file
index ac307c2..5059d4b 100644 (file)
@@ -30,6 +30,8 @@
             manufacturer="Nicira, Inc\." hwVersion="Open vSwitch" swVersion="2\..*">
         <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
                    impl="org.onosproject.driver.handshaker.NiciraSwitchHandshaker"/>
+        <behaviour api="org.onosproject.net.behaviour.ControllerConfig"
+                   impl="org.onosproject.driver.ovsdb.OvsdbControllerConfig"/>
     </driver>
     <driver name="ovs-corsa" extends="ovs"
             manufacturer="Corsa" hwVersion="emulation" swVersion="0.0.0">
         <behaviour api="org.onosproject.net.behaviour.Pipeliner"
                    impl="org.onosproject.driver.pipeline.OpenVSwitchPipeline"/>
     </driver>
+    <driver name="eci" extends="default"
+            manufacturer="ECI Telecom" hwVersion="optical" swVersion="V_1_0">
+        <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
+                   impl="org.onosproject.driver.handshaker.OFOpticalSwitch13"/>
+    </driver>
 </drivers>
 
diff --git a/framework/src/onos/drivers/src/test/java/org/onosproject/driver/ovsdb/OvsdbControllerConfigTest.java b/framework/src/onos/drivers/src/test/java/org/onosproject/driver/ovsdb/OvsdbControllerConfigTest.java
new file mode 100644 (file)
index 0000000..4a91efc
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.driver.ovsdb;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerConfig;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.driver.DefaultDriver;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.ovsdb.controller.driver.OvsdbClientServiceAdapter;
+import org.onosproject.ovsdb.controller.driver.OvsdbControllerAdapter;
+
+/**
+ * Created by Andrea on 10/7/15.
+ */
+public class OvsdbControllerConfigTest {
+
+
+    private static final DeviceId DEVICE_ID = DeviceId.deviceId("foo");
+
+    private DefaultDriver ddc;
+    private DefaultDriverData data;
+    private DefaultDriverHandler handler;
+
+    private TestDeviceService deviceService = new TestDeviceService();
+    private TestOvsdbController controller = new TestOvsdbController();
+    private TestOvsdbClient client = new TestOvsdbClient();
+
+    private OvsdbControllerConfig controllerConfig;
+
+
+    @Before
+    public void setUp() {
+        controllerConfig = new OvsdbControllerConfig();
+
+        ddc = new DefaultDriver("foo.bar", null, "Circus", "lux", "1.2a",
+                                ImmutableMap.of(ControllerConfig.class,
+                                                OvsdbControllerConfig.class),
+                                ImmutableMap.of("foo", "bar"));
+        data = new DefaultDriverData(ddc, DEVICE_ID);
+        handler = new DefaultDriverHandler(data);
+        //handler.controllerConfig.setHandler(handler);
+        //TODO setTestService directory on handler
+        //TODO setup ovsdb fake controller with fake ovsdbclient
+        //TODO setup fake device service
+    }
+
+    @Test
+    public void testGetControllers() throws Exception {
+//        DriverService driverService = new Driv
+//        AbstractBehaviour ab = new AbstractBehaviour();
+//        DriverHandler handler = handler();
+//        List<ControllerInfo> controllersList =
+//              controllerConfig.getControllers(DeviceId.deviceId("0000000000000018"));
+//        log.info("controllers " + controllersList);
+
+    }
+
+    @Test
+    public void testSetControllers() throws Exception {
+
+    }
+
+
+    private class TestDeviceService extends DeviceServiceAdapter {
+
+    }
+
+    private class TestOvsdbController extends OvsdbControllerAdapter {
+
+
+    }
+
+    private class TestOvsdbClient extends OvsdbClientServiceAdapter {
+
+    }
+}
\ No newline at end of file
index af2b47d..592336c 100644 (file)
 package org.onosproject.incubator.net.config.basics;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.annotations.Beta;
 import com.google.common.collect.Sets;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
-import org.onosproject.net.config.Config;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.Config;
 import org.onosproject.net.host.InterfaceIpAddress;
 
 import java.util.Set;
@@ -37,7 +39,6 @@ public class InterfaceConfig extends Config<ConnectPoint> {
     public static final String MAC = "mac";
     public static final String VLAN = "vlan";
 
-    public static final String IP_MISSING_ERROR = "Must have at least one IP address";
     public static final String MAC_MISSING_ERROR = "Must have a MAC address for each interface";
     public static final String CONFIG_VALUE_ERROR = "Error parsing config value";
 
@@ -53,9 +54,6 @@ public class InterfaceConfig extends Config<ConnectPoint> {
         try {
             for (JsonNode intfNode : array) {
                 Set<InterfaceIpAddress> ips = getIps(intfNode);
-                if (ips.isEmpty()) {
-                    throw new ConfigException(IP_MISSING_ERROR);
-                }
 
                 if (intfNode.path(MAC).isMissingNode()) {
                     throw new ConfigException(MAC_MISSING_ERROR);
@@ -63,10 +61,7 @@ public class InterfaceConfig extends Config<ConnectPoint> {
 
                 MacAddress mac = MacAddress.valueOf(intfNode.path(MAC).asText());
 
-                VlanId vlan = VlanId.NONE;
-                if (!intfNode.path(VLAN).isMissingNode()) {
-                    vlan = VlanId.vlanId(Short.valueOf(intfNode.path(VLAN).asText()));
-                }
+                VlanId vlan = getVlan(intfNode);
 
                 interfaces.add(new Interface(subject, ips, mac, vlan));
             }
@@ -77,13 +72,64 @@ public class InterfaceConfig extends Config<ConnectPoint> {
         return interfaces;
     }
 
+    /**
+     * Adds an interface to the config.
+     *
+     * @param intf interface to add
+     */
+    public void addInterface(Interface intf) {
+        ObjectNode intfNode = array.addObject();
+        intfNode.put(MAC, intf.mac().toString());
+
+        if (!intf.ipAddresses().isEmpty()) {
+            intfNode.set(IPS, putIps(intf.ipAddresses()));
+        }
+
+        if (!intf.vlan().equals(VlanId.NONE)) {
+            intfNode.put(VLAN, intf.vlan().toString());
+        }
+    }
+
+    /**
+     * Removes an interface from the config.
+     *
+     * @param intf interface to remove
+     */
+    public void removeInterface(Interface intf) {
+        for (int i = 0; i < array.size(); i++) {
+            if (intf.vlan().equals(getVlan(node))) {
+                array.remove(i);
+                break;
+            }
+        }
+    }
+
+    private VlanId getVlan(JsonNode node) {
+        VlanId vlan = VlanId.NONE;
+        if (!node.path(VLAN).isMissingNode()) {
+            vlan = VlanId.vlanId(Short.valueOf(node.path(VLAN).asText()));
+        }
+        return vlan;
+    }
+
     private Set<InterfaceIpAddress> getIps(JsonNode node) {
         Set<InterfaceIpAddress> ips = Sets.newHashSet();
 
         JsonNode ipsNode = node.get(IPS);
-        ipsNode.forEach(jsonNode -> ips.add(InterfaceIpAddress.valueOf(jsonNode.asText())));
+        if (ipsNode != null) {
+            ipsNode.forEach(jsonNode ->
+                    ips.add(InterfaceIpAddress.valueOf(jsonNode.asText())));
+        }
 
         return ips;
     }
 
+    private ArrayNode putIps(Set<InterfaceIpAddress> intfIpAddresses) {
+        ArrayNode ipArray = mapper.createArrayNode();
+
+        intfIpAddresses.forEach(i -> ipArray.add(i.toString()));
+
+        return ipArray;
+    }
+
 }
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/DomainIntentResource.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/DomainIntentResource.java
new file mode 100644 (file)
index 0000000..ea1660e
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.domain;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.tunnel.DomainTunnelId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Path;
+
+/**
+ * A variant of intent resource specialized for use on the intra-domain level.  It contains a lower level path.
+ */
+public class DomainIntentResource extends IntentResource {
+
+    private final Path domainPath;
+
+    private final DomainTunnelId domainTunnelId;
+
+    private final IntentDomainId intentDomainId;
+
+    /**
+     * Constructor for a domain intent resource.
+     *
+     * @param primitive      the primitive associated with this resource
+     * @param domainTunnelId the id of this tunnel (used as a sorting mechanism)
+     * @param domainId       the ID of the intent domain containing this tunnel
+     * @param appId          the id of the application which created this tunnel
+     * @param ingress        the fist connect point associated with this tunnel (order is irrelevant as long as it is
+     *                       consistent with the path)
+     * @param egress         the second connect point associated with this tunnel (order is irrelevant as long as it is
+     *                       consistent with the path)
+     * @param path           the path followed through the domain
+     */
+    public DomainIntentResource(IntentPrimitive primitive, DomainTunnelId domainTunnelId, IntentDomainId domainId,
+                                ApplicationId appId, ConnectPoint ingress, ConnectPoint egress, Path path) {
+        super(primitive, appId, ingress, egress);
+
+        this.domainPath = path;
+        this.domainTunnelId = domainTunnelId;
+        this.intentDomainId = domainId;
+    }
+
+    /**
+     * Returns the domain path associated with this resource at creation.
+     *
+     * @return this resource's domain level path or if this resource backs a network tunnel then null.
+     */
+    public Path path() {
+        return domainPath;
+    }
+
+    /**
+     * Returns the tunnel ID associated with this domain at creation.
+     *
+     * @return this resource's tunnel ID.
+     */
+    public DomainTunnelId tunnelId() {
+        return domainTunnelId;
+    }
+
+    /**
+     * Returns the domain ID associated with this resource at creation.
+     *
+     * @return this resource's domain ID.
+     */
+    public IntentDomainId domainId() {
+        return intentDomainId;
+    }
+
+}
index 51265f7..a19add6 100644 (file)
@@ -34,51 +34,51 @@ public interface IntentDomainProvider {
      *
      * @param domain intent domain for the request
      * @param primitive intent primitive
-     * @return request contexts that contain resources to satisfy the intent
+     * @return intent resources that specify paths that satisfy the request.
      */
     //TODO Consider an iterable and/or holds (only hold one or two reservation(s) at a time)
-    List<RequestContext> request(IntentDomain domain, IntentPrimitive primitive);
+    List<DomainIntentResource> request(IntentDomain domain, IntentPrimitive primitive);
 
     /**
      * Request that the provider attempt to modify an existing resource to satisfy
      * a new intent primitive. The application must apply the context before
      * the intent resource can be used.
      *
-     * @param resource existing resource
-     * @param newPrimitive intent primitive
+     * @param oldResource the resource to be replaced
+     * @param newResource the resource to be applied
      * @return request contexts that contain resources to satisfy the intent
      */
-    List<RequestContext> modify(IntentResource resource, IntentPrimitive newPrimitive);
+    DomainIntentResource modify(DomainIntentResource oldResource, DomainIntentResource newResource);
 
     /**
      * Requests that the provider release an intent resource.
      *
      * @param resource intent resource
      */
-    void release(IntentResource resource);
+    void release(DomainIntentResource resource);
 
     /**
-     * Requests that the provider apply the intent resource in the request context.
+     * Requests that the provider apply the path from the intent resource.
      *
-     * @param context request context
+     * @param domainIntentResource request context
      * @return intent resource that satisfies the intent
      */
-    IntentResource apply(RequestContext context);
+    DomainIntentResource apply(DomainIntentResource domainIntentResource);
 
     /**
-     * Requests that the provider cancel the request. Requests that are not applied
+     * Requests that the provider cancel the path. Requests that are not applied
      * will be eventually timed out by the provider.
      *
-     * @param context request context
+     * @param domainIntentResource the intent resource whose path should be cancelled.
      */
-    void cancel(RequestContext context);
+    void cancel(DomainIntentResource domainIntentResource);
 
     /**
      * Returns all intent resources held by the provider.
      *
      * @return set of intent resources
      */
-    Set<IntentResource> getResources();
+    Set<DomainIntentResource> getResources();
 }
 
 
index 9cd9aac..627c863 100644 (file)
 package org.onosproject.incubator.net.domain;
 
 import com.google.common.annotations.Beta;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+
 
 /**
  * The abstract base class for the resource that satisfies an intent primitive.
  */
 @Beta
-public class IntentResource {
+public abstract class IntentResource {
 
     private final IntentPrimitive primitive;
-    private final long tunnelId;
-    private final IntentDomainId domainId;
+
+    private final ApplicationId appId;
+    private final ConnectPoint ingress;
+    private final ConnectPoint egress;
+
+    //* QUESTIONABLE ADDITIONS *//
 
     // TODO add other common fields
     //String ingressTag;
     //String egressTag;
     //etc.
 
-    public IntentResource(IntentPrimitive primitive, long tunnelId, IntentDomainId domainId) {
+    public IntentResource(IntentPrimitive primitive, ApplicationId appId,
+                          ConnectPoint ingress, ConnectPoint egress) {
+        this.appId = appId;
+        this.ingress = ingress;
+        this.egress = egress;
         this.primitive = primitive;
-        this.tunnelId = tunnelId;
-        this.domainId = domainId;
     }
 
+    //TODO when is same package tunnelID should be of type tunnelID and netTunnelId not long.
+
+
     /**
-     * Returns the intent primitive associated with this resource as creation.
+     * Returns the intent primitive associated with this resource at creation.
      *
-     * @return this resource's intent primitive
+     * @return this resource's intent primitive.
      */
     public IntentPrimitive primitive() {
         return primitive;
     }
 
     /**
-     * Returns the tunnel ID associated with this resource as creation.
+     * Returns the application ID associated with this resource at creation.
      *
-     * @return this resource's tunnel ID
+     * @return this resource's application ID.
      */
-    public long tunnelId() {
-        return tunnelId;
+    public ApplicationId appId() {
+        return appId;
     }
 
     /**
-     * Returns the domain ID associated with this resource as creation.
+     * Returns the ingress connect point associated with this resource at creation.
      *
-     * @return this resource's domain ID
+     * @return this resource's ingress connect point.
      */
-    public IntentDomainId domainId() {
-        return domainId;
+    public ConnectPoint ingress() {
+        return ingress;
     }
 
+    /**
+     * Returns the egress connect point associated with this resource at creation.
+     *
+     * @return this resource's connect point.
+     */
+    public ConnectPoint egress() {
+        return egress;
+    }
 }
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/NetworkIntentResource.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/NetworkIntentResource.java
new file mode 100644 (file)
index 0000000..ac4445b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.domain;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.tunnel.NetworkTunnelId;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * A variant of intent resource specialized for use on the inter-domain level.  It contains a higher level path.
+ */
+public class NetworkIntentResource extends IntentResource {
+
+    private final org.onlab.graph.Path<DomainVertex, DomainEdge> netPath;
+
+    private NetworkTunnelId networkTunnelId;
+
+    /**
+     * Constructor for a network intent resource.
+     *
+     * @param primitive      the primitive associated with this resource
+     * @param networkTunnelId the id of this tunnel (used as a sorting mechanism)
+     * @param appId          the id of the application which created this tunnel
+     * @param ingress        the fist connect point associated with this tunnel (order is irrelevant as long as it is
+     *                       consistent with the path)
+     * @param egress         the second connect point associated with this tunnel (order is irrelevant as long as it is
+     *                       consistent with the path)
+     * @param path           the path followed through the graph of domain vertices and domain edges
+     */
+    public NetworkIntentResource(IntentPrimitive primitive, NetworkTunnelId networkTunnelId, ApplicationId appId,
+                                 ConnectPoint ingress, ConnectPoint egress,
+                                 org.onlab.graph.Path<DomainVertex, DomainEdge> path) {
+        super(primitive, appId, ingress, egress);
+
+        this.networkTunnelId = networkTunnelId;
+        this.netPath = path;
+    }
+
+    /**
+     * Returns the network path associated with this resource at creation.
+     *
+     * @return this resource's network lever path or if this resource backs a domain level tunnel then null.
+     */
+    public org.onlab.graph.Path<DomainVertex, DomainEdge> path() {
+        return netPath;
+    }
+
+    /**
+     * Returns ths network ID associated with this network tunnel at creation.
+     *
+     * @return thsi resource's tunnel ID.
+     */
+    public NetworkTunnelId tunnelId() {
+        return this.networkTunnelId;
+    }
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/InterfaceAdminService.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/InterfaceAdminService.java
new file mode 100644 (file)
index 0000000..56d5aec
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.intf;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Provides a means to modify the interfaces configuration.
+ */
+public interface InterfaceAdminService {
+    /**
+     * Adds a new interface configuration to the system.
+     *
+     * @param intf interface to add
+     */
+    void add(Interface intf);
+
+    /**
+     * Removes an interface configuration from the system.
+     *
+     * @param connectPoint connect point of the interface
+     * @param vlanId vlan id
+     */
+    void remove(ConnectPoint connectPoint, VlanId vlanId);
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/DomainTunnelId.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/DomainTunnelId.java
new file mode 100644 (file)
index 0000000..430823c
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.tunnel;
+
+/**
+ * A wrapper class for a long used to identify domain level tunnels.
+ */
+public final class DomainTunnelId {
+
+    private final long value;
+
+    /**
+     * Creates a tunnel identifier from the specified tunnel.
+     *
+     * @param value long value
+     * @return domain tunnel identifier
+     */
+    public static DomainTunnelId valueOf(long value) {
+        return new DomainTunnelId(value);
+    }
+
+    /**
+     * Creates a tunnel identifier from the specified tunnel.
+     *
+     * @param value long value as a string
+     * @return domain tunnel identifier
+     */
+    public static DomainTunnelId valueOf(String value) {
+        return new DomainTunnelId(Long.parseLong(value));
+    }
+
+    /**
+     * Constructor for serializer.
+     */
+    protected DomainTunnelId() {
+        this.value = 0;
+    }
+
+    /**
+     * Constructs the Domain ID corresponding to a given long value.
+     *
+     * @param value the underlying value of this domain ID
+     */
+    public DomainTunnelId(long value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the backing value of this domain ID.
+     *
+     * @return the long value
+     */
+    public long id() {
+        return value;
+    }
+
+    @Override
+    public int hashCode() {
+        return Long.hashCode(value);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof DomainTunnelId)) {
+            return false;
+        }
+        DomainTunnelId that = (DomainTunnelId) obj;
+        return this.value == that.value;
+    }
+
+    @Override
+    public String toString() {
+        return "0x" + Long.toHexString(value);
+    }
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/NetworkTunnelId.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/NetworkTunnelId.java
new file mode 100644 (file)
index 0000000..a3de788
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.tunnel;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Representation of a Network Tunnel Id.
+ */
+@Beta
+public final class NetworkTunnelId {
+    private final long value;
+
+    /**
+     * Creates an tunnel identifier from the specified tunnel.
+     *
+     * @param value long value
+     * @return tunnel identifier
+     */
+    public static NetworkTunnelId valueOf(long value) {
+        return new NetworkTunnelId(value);
+    }
+
+    public static NetworkTunnelId valueOf(String value) {
+        return new NetworkTunnelId(Long.parseLong(value));
+    }
+
+    /**
+     * Constructor for serializer.
+     */
+    NetworkTunnelId() {
+        this.value = 0;
+    }
+
+    /**
+     * Constructs the ID corresponding to a given long value.
+     *
+     * @param value the underlying value of this ID
+     */
+    public NetworkTunnelId(long value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the backing value.
+     *
+     * @return the value
+     */
+    public long id() {
+        return value;
+    }
+
+    @Override
+    public int hashCode() {
+        return Long.hashCode(value);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof NetworkTunnelId)) {
+            return false;
+        }
+        NetworkTunnelId that = (NetworkTunnelId) obj;
+        return this.value == that.value;
+    }
+
+    @Override
+    public String toString() {
+        return "0x" + Long.toHexString(value);
+    }
+
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java
new file mode 100644 (file)
index 0000000..54a22a4
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual;
+
+import org.onlab.packet.ChassisId;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.*;
+
+/**
+ * Default representation of a virtual device.
+ */
+public class DefaultVirtualDevice extends DefaultDevice implements VirtualDevice {
+
+    private static final String VIRTUAL = "virtual";
+    private static final ProviderId PID = new ProviderId(VIRTUAL, VIRTUAL);
+
+    private final NetworkId networkId;
+
+    /**
+     * Creates a network element attributed to the specified provider.
+     *
+     * @param networkId network identifier
+     * @param id        device identifier
+     */
+    public DefaultVirtualDevice(NetworkId networkId, DeviceId id) {
+        super(PID, id, Type.VIRTUAL, VIRTUAL, VIRTUAL, VIRTUAL, VIRTUAL,
+              new ChassisId(0));
+        this.networkId = networkId;
+    }
+
+    @Override
+    public NetworkId networkId() {
+        return networkId;
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * super.hashCode() + Objects.hash(networkId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultVirtualDevice) {
+            DefaultVirtualDevice that = (DefaultVirtualDevice) obj;
+            return super.equals(that) && Objects.equals(this.networkId, that.networkId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("networkId", networkId).toString();
+    }
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java
new file mode 100644 (file)
index 0000000..c114191
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default implementation of the virtual network descriptor.
+ */
+public class DefaultVirtualNetwork implements VirtualNetwork {
+
+    private final NetworkId id;
+    private final TenantId tenantId;
+
+    /**
+     * Creates a new virtual network descriptor.
+     *
+     * @param id       network identifier
+     * @param tenantId tenant identifier
+     */
+    public DefaultVirtualNetwork(NetworkId id, TenantId tenantId) {
+        this.id = id;
+        this.tenantId = tenantId;
+    }
+
+    @Override
+    public NetworkId id() {
+        return id;
+    }
+
+    @Override
+    public TenantId tenantId() {
+        return tenantId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, tenantId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultVirtualNetwork) {
+            DefaultVirtualNetwork that = (DefaultVirtualNetwork) obj;
+            return Objects.equals(this.id, that.id)
+                    && Objects.equals(this.tenantId, that.tenantId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("tenantId", tenantId)
+                .toString();
+    }
+}
index 1c74b92..791b8e2 100644 (file)
@@ -23,13 +23,6 @@ import com.google.common.annotations.Beta;
 @Beta
 public interface VirtualElement {
 
-    /**
-     * Returns the identifier of the tenant to which this virtual element belongs.
-     *
-     * @return tenant identifier
-     */
-    TenantId tenantId();
-
     /**
      * Returns the network identifier to which this virtual element belongs.
      *
index 1e3648b..07c399c 100644 (file)
 package org.onosproject.incubator.net.virtual;
 
 import com.google.common.annotations.Beta;
-import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceDescription;
-import org.onosproject.net.device.PortDescription;
-import org.onosproject.net.link.LinkDescription;
 
 import java.util.Set;
 
@@ -76,12 +73,12 @@ public interface VirtualNetworkAdminService extends VirtualNetworkService {
      * Creates a new virtual device within the specified network. The device id
      * must be unique within the bounds of the network.
      *
-     * @param networkId   network identifier
-     * @param description device description
+     * @param networkId network identifier
+     * @param deviceId  device identifier
      * @return newly created device
      * @throws org.onlab.util.ItemNotFoundException if no such network found
      */
-    VirtualDevice createVirtualDevice(NetworkId networkId, DeviceDescription description);
+    VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId);
 
     /**
      * Removes the specified virtual device and all its ports and affiliated links.
@@ -96,14 +93,16 @@ public interface VirtualNetworkAdminService extends VirtualNetworkService {
     /**
      * Creates a new virtual link within the specified network.
      *
-     * @param networkId   network identifier
-     * @param description link description
-     * @param realizedBy  tunnel using which this link is realized
+     * @param networkId  network identifier
+     * @param src        source connection point
+     * @param dst        destination connection point
+     * @param realizedBy identifier of the tunnel using which this link is realized
      * @return newly created virtual link
      * @throws org.onlab.util.ItemNotFoundException if no such network found
      */
-    VirtualLink createVirtualLink(NetworkId networkId, LinkDescription description,
-                                  Tunnel realizedBy);
+    VirtualLink createVirtualLink(NetworkId networkId,
+                                  ConnectPoint src, ConnectPoint dst,
+                                  TunnelId realizedBy);
 
     // TODO: Discuss whether we should provide an alternate createVirtualLink
     // which is backed by a Path instead; I'm leaning towards not doing that.
@@ -119,20 +118,17 @@ public interface VirtualNetworkAdminService extends VirtualNetworkService {
     void removeVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
 
     /**
-     * Creates a new virtual port on the specified device. Note that the port
-     * description can only request the resources which the underlying port
-     * port is capable of providing. It is, however, permissible to request
-     * only portion of those resources.
+     * Creates a new virtual port on the specified device.
      *
-     * @param networkId   network identifier
-     * @param deviceId    device identifier
-     * @param description port description
-     * @param realizedBy  underlying port using which this virtual port is realized
+     * @param networkId  network identifier
+     * @param deviceId   device identifier
+     * @param portNumber port number
+     * @param realizedBy underlying port using which this virtual port is realized
      * @return newly created port
      * @throws org.onlab.util.ItemNotFoundException if no such network or device found
      */
     VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId,
-                                  PortDescription description, Port realizedBy);
+                                  PortNumber portNumber, Port realizedBy);
 
     /**
      * Removes the specified virtual port.
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java
new file mode 100644 (file)
index 0000000..7e076e0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes virtual network event.
+ */
+public class VirtualNetworkEvent extends AbstractEvent<VirtualNetworkEvent.Type, NetworkId> {
+
+    /**
+     * Type of virtual network events.
+     */
+    public enum Type {
+        /**
+         * Signifies that a new tenant identifier was registered.
+         */
+        TENANT_REGISTERED,
+        /**
+         * Signifies that a tenant identifier was unregistered.
+         */
+        TENANT_UNREGISTERED,
+        /**
+         * Signifies that a new virtual network was added.
+         */
+        NETWORK_ADDED,
+        /**
+         * Signifies that a virtual network was updated.
+         */
+        NETWORK_UPDATED,
+        /**
+         * Signifies that a virtual network was removed.
+         */
+        NETWORK_REMOVED
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject and the
+     * current time.
+     *
+     * @param type        event type
+     * @param subject     event subject
+     */
+    public VirtualNetworkEvent(Type type, NetworkId subject) {
+        super(type, subject);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified subject and time.
+     *
+     * @param type        device event type
+     * @param subject     event subject
+     * @param time        occurrence time
+     */
+    public VirtualNetworkEvent(Type type, NetworkId subject, long time) {
+        super(type, subject, time);
+    }
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java
new file mode 100644 (file)
index 0000000..707ca8a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Represents entity capable of receiving virtual network events.
+ */
+public interface VirtualNetworkListener extends EventListener<VirtualNetworkEvent> {
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java
new file mode 100644 (file)
index 0000000..8e5b19a
--- /dev/null
@@ -0,0 +1,31 @@
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.provider.Provider;
+
+/**
+ * Entity capable of providing traffic isolation constructs for use in
+ * implementation of virtual devices and virtual links.
+ */
+public interface VirtualNetworkProvider extends Provider {
+
+    /**
+     * Creates a network tunnel for all traffic from the specified source
+     * connection point to the indicated destination connection point.
+     *
+     * @param networkId virtual network identifier
+     * @param src       source connection point
+     * @param dst       destination connection point
+     */
+    TunnelId createTunnel(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+
+    /**
+     * Destroys the specified network tunnel.
+     *
+     * @param networkId virtual network identifier
+     * @param tunnelId  tunnel identifier
+     */
+    void destroyTunnel(NetworkId networkId, TunnelId tunnelId);
+
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderRegistry.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderRegistry.java
new file mode 100644 (file)
index 0000000..4e89316
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.net.provider.ProviderRegistry;
+
+/**
+ * Abstraction of a virtual network provider registry.
+ */
+public interface VirtualNetworkProviderRegistry
+        extends ProviderRegistry<VirtualNetworkProvider, VirtualNetworkProviderService> {
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java
new file mode 100644 (file)
index 0000000..cba933c
--- /dev/null
@@ -0,0 +1,11 @@
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.net.provider.ProviderService;
+
+/**
+ * Service through which virtual network providers can inject information into
+ * the core.
+ */
+public interface VirtualNetworkProviderService extends ProviderService<VirtualNetworkProvider> {
+    // TODO: Add methods for notification of core about damaged tunnels, etc.
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java
new file mode 100644 (file)
index 0000000..49ad2f2
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.store.Store;
+
+import java.util.Set;
+
+/**
+ * Mechanism for distributing and storing virtual network model information.
+ */
+public interface VirtualNetworkStore
+        extends Store<VirtualNetworkEvent, VirtualNetworkStoreDelegate> {
+
+    /**
+     * Adds a new tenant ID to the store.
+     *
+     * @param tenantId tenant identifier
+     */
+    void addTenantId(TenantId tenantId);
+
+    /**
+     * Removes the specified tenant ID from the store.
+     *
+     * @param tenantId tenant identifier
+     */
+    void removeTenantId(TenantId tenantId);
+
+    /**
+     * Returns set of registered tenant IDs.
+     *
+     * @return set of tenant identifiers
+     */
+    Set<TenantId> getTenantIds();
+
+    /**
+     * Adds a new virtual network for the specified tenant to the store.
+     *
+     * @param tenantId tenant identifier
+     * @return the virtual network
+     */
+    VirtualNetwork addNetwork(TenantId tenantId);
+
+    /**
+     * Removes the specified virtual network from the store.
+     *
+     * @param networkId network identifier
+     */
+    void removeNetwork(NetworkId networkId);
+
+    /**
+     * Adds a new virtual device to the store. This device will have no ports.
+     *
+     * @param networkId network identifier
+     * @param deviceId  device identifier
+     * @return the virtual device
+     */
+    VirtualDevice addDevice(NetworkId networkId, DeviceId deviceId);
+
+    /**
+     * Renmoves the specified virtual device from the given network.
+     *
+     * @param networkId network identifier
+     * @param deviceId  device identifier
+     */
+    void removeDevice(NetworkId networkId, DeviceId deviceId);
+
+    /**
+     * Adds a new virtual link.
+     *
+     * @param networkId  network identifier
+     * @param src        source end-point of the link
+     * @param dst        destination end-point of the link
+     * @param realizedBy underlying tunnel using which this link is realized
+     * @return the virtual link
+     */
+    VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst,
+                        TunnelId realizedBy);
+
+    /**
+     * Removes the specified link from the store.
+     *
+     * @param networkId network identifier
+     * @param src       source connection point
+     * @param dst       destination connection point
+     */
+    void removeLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst);
+
+    /**
+     * Adds a new virtual port to the network.
+     *
+     * @param networkId  network identifier
+     * @param deviceId   device identifier
+     * @param portNumber port number
+     * @param realizedBy underlying port which realizes the virtual port
+     * @return the virtual port
+     */
+    VirtualPort addPort(NetworkId networkId, DeviceId deviceId,
+                        PortNumber portNumber, Port realizedBy);
+
+    /**
+     * Removes the specified port from the given device and network.
+     *
+     * @param networkId  network identifier
+     * @param deviceId   device identifier
+     * @param portNumber port number
+     */
+    void removePort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber);
+
+    /**
+     * Returns the list of networks.
+     *
+     * @param tenantId tenant identifier
+     * @return set of virtual networks
+     */
+    Set<VirtualNetwork> getNetworks(TenantId tenantId);
+
+    /**
+     * Returns the list of devices in the specified virtual network.
+     *
+     * @param networkId network identifier
+     * @return set of virtual devices
+     */
+    Set<VirtualDevice> getDevices(NetworkId networkId);
+
+    /**
+     * Returns the list of virtual links in the specified virtual network.
+     *
+     * @param networkId network identifier
+     * @return set of virtual links
+     */
+    Set<VirtualLink> getLinks(NetworkId networkId);
+
+    /**
+     * Returns the list of ports of the specified virtual device.
+     *
+     * @param networkId network identifier
+     * @param deviceId   device identifier
+     * @return set of virtual networks
+     */
+    Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId);
+
+}
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java
new file mode 100644 (file)
index 0000000..e57c3d3
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * Network configuration store delegate abstraction.
+ */
+public interface VirtualNetworkStoreDelegate extends StoreDelegate<VirtualNetworkEvent> {
+}
index f82cdbf..0439d03 100644 (file)
@@ -29,6 +29,7 @@ import org.onlab.packet.VlanId;
 import org.onosproject.incubator.net.config.basics.ConfigException;
 import org.onosproject.incubator.net.config.basics.InterfaceConfig;
 import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceAdminService;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.NetworkConfigEvent;
@@ -50,7 +51,8 @@ import static java.util.stream.Collectors.toSet;
  */
 @Service
 @Component(immediate = true)
-public class InterfaceManager implements InterfaceService {
+public class InterfaceManager implements InterfaceService,
+        InterfaceAdminService {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -153,6 +155,54 @@ public class InterfaceManager implements InterfaceService {
         interfaces.remove(port);
     }
 
+    @Override
+    public void add(Interface intf) {
+        if (interfaces.containsKey(intf.connectPoint())) {
+            boolean conflict = interfaces.get(intf.connectPoint()).stream()
+                    .filter(i -> i.connectPoint().equals(intf.connectPoint()))
+                    .filter(i -> i.mac().equals(intf.mac()))
+                    .filter(i -> i.vlan().equals(intf.vlan()))
+                    .findAny().isPresent();
+
+            if (conflict) {
+                log.error("Can't add interface because it conflicts with existing config");
+                return;
+            }
+        }
+
+        InterfaceConfig config =
+                configService.addConfig(intf.connectPoint(), CONFIG_CLASS);
+
+        config.addInterface(intf);
+
+        configService.applyConfig(intf.connectPoint(), CONFIG_CLASS, config.node());
+    }
+
+    @Override
+    public void remove(ConnectPoint connectPoint, VlanId vlanId) {
+        Optional<Interface> intf = interfaces.get(connectPoint).stream()
+                .filter(i -> i.vlan().equals(vlanId))
+                .findAny();
+
+        if (!intf.isPresent()) {
+            log.error("Can't find interface {}/{} to remove", connectPoint, vlanId);
+            return;
+        }
+
+        InterfaceConfig config = configService.addConfig(intf.get().connectPoint(), CONFIG_CLASS);
+        config.removeInterface(intf.get());
+
+        try {
+            if (config.getInterfaces().isEmpty()) {
+                configService.removeConfig(connectPoint, CONFIG_CLASS);
+            } else {
+                configService.applyConfig(intf.get().connectPoint(), CONFIG_CLASS, config.node());
+            }
+        } catch (ConfigException e) {
+            log.error("Error reading interfaces JSON", e);
+        }
+    }
+
     /**
      * Listener for network config events.
      */
diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastData.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastData.java
new file mode 100644 (file)
index 0000000..946d8c6
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.mcast.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Simple entity maintaining a mapping between a source and a collection of sink
+ * connect points.
+ */
+public final class MulticastData {
+
+    private final ConnectPoint source;
+    private final List<ConnectPoint> sinks;
+    private final boolean isEmpty;
+
+    private MulticastData() {
+        this.source = null;
+        this.sinks = Collections.EMPTY_LIST;
+        isEmpty = true;
+    }
+
+    public MulticastData(ConnectPoint source, List<ConnectPoint> sinks) {
+        this.source = checkNotNull(source, "Multicast source cannot be null.");
+        this.sinks = checkNotNull(sinks, "List of sinks cannot be null.");
+        isEmpty = false;
+    }
+
+    public MulticastData(ConnectPoint source, ConnectPoint sink) {
+        this.source = checkNotNull(source, "Multicast source cannot be null.");
+        this.sinks = Lists.newArrayList(checkNotNull(sink, "Sink cannot be null."));
+        isEmpty = false;
+    }
+
+    public MulticastData(ConnectPoint source) {
+        this.source = checkNotNull(source, "Multicast source cannot be null.");
+        this.sinks = Lists.newArrayList();
+        isEmpty = false;
+    }
+
+    public ConnectPoint source() {
+        return source;
+    }
+
+    public List<ConnectPoint> sinks() {
+        return ImmutableList.copyOf(sinks);
+    }
+
+    public void appendSink(ConnectPoint sink) {
+        sinks.add(sink);
+    }
+
+    public boolean removeSink(ConnectPoint sink) {
+        return sinks.remove(sink);
+    }
+
+    public boolean isEmpty() {
+        return isEmpty;
+    }
+
+    public static MulticastData empty() {
+        return new MulticastData();
+    }
+
+}
diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManager.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManager.java
new file mode 100644 (file)
index 0000000..f73dfe4
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.mcast.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.mcast.McastEvent;
+import org.onosproject.net.mcast.McastListener;
+import org.onosproject.net.mcast.McastRoute;
+import org.onosproject.net.mcast.MulticastRouteService;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * An implementation of a multicast route table.
+ */
+@Component(immediate = true)
+@Service
+public class MulticastRouteManager
+        extends AbstractListenerManager<McastEvent, McastListener>
+        implements MulticastRouteService {
+    //TODO: add MulticastRouteAdminService
+
+    private static final String MCASTRIB = "mcast-rib-table";
+
+    private Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private CoreService coreService;
+
+
+    protected ApplicationId appId;
+    protected ConsistentMap<McastRoute, MulticastData> mcastRoutes;
+
+    @Activate
+    public void activate() {
+
+        eventDispatcher.addSink(McastEvent.class, listenerRegistry);
+
+        appId = coreService.registerApplication("org.onosproject.mcastrib");
+
+        mcastRoutes = storageService.<McastRoute, MulticastData>consistentMapBuilder()
+                .withApplicationId(appId)
+                .withName(MCASTRIB)
+                .withSerializer(Serializer.using(KryoNamespace.newBuilder().register(
+                        MulticastData.class,
+                        McastRoute.class,
+                        McastRoute.Type.class,
+                        IpPrefix.class,
+                        List.class,
+                        ConnectPoint.class
+                ).build())).build();
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public void add(McastRoute route) {
+        mcastRoutes.put(route, MulticastData.empty());
+        post(new McastEvent(McastEvent.Type.ROUTE_ADDED, route, null, null));
+    }
+
+    @Override
+    public void remove(McastRoute route) {
+        mcastRoutes.remove(route);
+        post(new McastEvent(McastEvent.Type.ROUTE_REMOVED, route, null, null));
+    }
+
+    @Override
+    public void addSource(McastRoute route, ConnectPoint connectPoint) {
+        Versioned<MulticastData> d = mcastRoutes.compute(route, (k, v) -> {
+            if (v.isEmpty()) {
+                return new MulticastData(connectPoint);
+            } else {
+                log.warn("Route {} is already in use.", route);
+                return v;
+            }
+        });
+
+        if (d != null) {
+            post(new McastEvent(McastEvent.Type.SOURCE_ADDED,
+                                route, null, connectPoint));
+        }
+    }
+
+    @Override
+    public void addSink(McastRoute route, ConnectPoint connectPoint) {
+        AtomicReference<ConnectPoint> source = new AtomicReference<>();
+        mcastRoutes.compute(route, (k, v) -> {
+            if (!v.isEmpty()) {
+                v.appendSink(connectPoint);
+                source.set(v.source());
+            } else {
+                log.warn("Route {} does not exist");
+            }
+            return v;
+        });
+
+        if (source.get() != null) {
+            post(new McastEvent(McastEvent.Type.SINK_ADDED, route,
+                                connectPoint, source.get()));
+        }
+    }
+
+
+    @Override
+    public void removeSink(McastRoute route, ConnectPoint connectPoint) {
+        AtomicReference<ConnectPoint> source = new AtomicReference<>();
+        mcastRoutes.compute(route, (k, v) -> {
+            if (v.removeSink(connectPoint)) {
+                source.set(v.source());
+            }
+            return v;
+        });
+
+        if (source.get() != null) {
+            post(new McastEvent(McastEvent.Type.SINK_REMOVED, route,
+                                connectPoint, source.get()));
+        }
+    }
+
+    @Override
+    public ConnectPoint fetchSource(McastRoute route) {
+        MulticastData d = mcastRoutes.asJavaMap().getOrDefault(route,
+                                                               MulticastData.empty());
+        return d.source();
+    }
+
+    @Override
+    public List<ConnectPoint> fetchSinks(McastRoute route) {
+        MulticastData d = mcastRoutes.asJavaMap().getOrDefault(route,
+                                                               MulticastData.empty());
+        return d.sinks();
+    }
+}
diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/package-info.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/package-info.java
new file mode 100644 (file)
index 0000000..464cf70
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * An implementation of a multicast RIB.
+ */
+package org.onosproject.incubator.net.mcast.impl;
\ No newline at end of file
index 575a715..5c5c11c 100644 (file)
@@ -15,6 +15,7 @@
  */
 package org.onosproject.incubator.net.meter.impl;
 
+import com.google.common.collect.Maps;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -27,6 +28,7 @@ import org.onosproject.net.meter.Meter;
 import org.onosproject.net.meter.MeterEvent;
 import org.onosproject.net.meter.MeterFailReason;
 import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterKey;
 import org.onosproject.net.meter.MeterListener;
 import org.onosproject.net.meter.MeterOperation;
 import org.onosproject.net.meter.MeterProvider;
@@ -61,7 +63,7 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M
         MeterProvider, MeterProviderService>
         implements MeterService, MeterProviderRegistry {
 
-    private final String meterIdentifier = "meter-id-counter";
+    private static final String METERCOUNTERIDENTIFIER = "meter-id-counter-%s";
     private final Logger log = getLogger(getClass());
     private final MeterStoreDelegate delegate = new InternalMeterStoreDelegate();
 
@@ -71,15 +73,13 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MeterStore store;
 
-    private AtomicCounter meterIdCounter;
+    private Map<DeviceId, AtomicCounter> meterIdCounters
+            = Maps.newConcurrentMap();
 
     private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete;
 
     @Activate
     public void activate() {
-        meterIdCounter = storageService.atomicCounterBuilder()
-                .withName(meterIdentifier)
-                .build();
 
         store.setDelegate(delegate);
 
@@ -115,11 +115,13 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M
     @Override
     public Meter submit(MeterRequest request) {
 
+        MeterId id = allocateMeterId(request.deviceId());
+
         Meter.Builder mBuilder = DefaultMeter.builder()
                 .forDevice(request.deviceId())
                 .fromApp(request.appId())
                 .withBands(request.bands())
-                .withId(allocateMeterId())
+                .withId(id)
                 .withUnit(request.unit());
 
         if (request.isBurst()) {
@@ -152,8 +154,9 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M
     }
 
     @Override
-    public Meter getMeter(MeterId id) {
-        return store.getMeter(id);
+    public Meter getMeter(DeviceId deviceId, MeterId id) {
+        MeterKey key = MeterKey.key(deviceId, id);
+        return store.getMeter(key);
     }
 
     @Override
@@ -161,9 +164,21 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M
         return store.getAllMeters();
     }
 
-    private MeterId allocateMeterId() {
-        // FIXME: This will break one day.
-        return MeterId.meterId((int) meterIdCounter.incrementAndGet());
+    private MeterId allocateMeterId(DeviceId deviceId) {
+        long id = meterIdCounters.compute(deviceId, (k, v) -> {
+            if (v == null) {
+                return allocateCounter(k);
+            }
+            return v;
+        }).incrementAndGet();
+
+        return MeterId.meterId((int) id);
+    }
+
+    private AtomicCounter allocateCounter(DeviceId deviceId) {
+        return storageService.atomicCounterBuilder()
+                .withName(String.format(METERCOUNTERIDENTIFIER, deviceId))
+                .build();
     }
 
     private class InternalMeterProviderService
diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java
new file mode 100644 (file)
index 0000000..fe9f884
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.virtual.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProvider;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProviderRegistry;
+import org.onosproject.incubator.net.virtual.VirtualNetworkProviderService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkService;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStoreDelegate;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of the virtual network service.
+ */
+@Component(immediate = true)
+@Service
+public class VirtualNetworkManager
+        extends AbstractListenerProviderRegistry<VirtualNetworkEvent, VirtualNetworkListener,
+                                                 VirtualNetworkProvider, VirtualNetworkProviderService>
+        implements VirtualNetworkService, VirtualNetworkAdminService, VirtualNetworkProviderRegistry {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String TENANT_NULL = "Tenant ID cannot be null";
+    private static final String NETWORK_NULL = "Network ID cannot be null";
+    private static final String DEVICE_NULL = "Device ID cannot be null";
+    private static final String LINK_POINT_NULL = "Link end-point cannot be null";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VirtualNetworkStore store;
+
+    private VirtualNetworkStoreDelegate delegate = new InternalStoreDelegate();
+
+    // TODO: figure out how to coordinate "implementation" of a virtual network in a cluster
+
+    @Activate
+    protected void activate() {
+        store.setDelegate(delegate);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        store.unsetDelegate(delegate);
+        log.info("Stopped");
+    }
+
+    @Override
+    public void registerTenantId(TenantId tenantId) {
+        checkNotNull(tenantId, TENANT_NULL);
+        store.addTenantId(tenantId);
+    }
+
+    @Override
+    public void unregisterTenantId(TenantId tenantId) {
+        checkNotNull(tenantId, TENANT_NULL);
+        store.removeTenantId(tenantId);
+    }
+
+    @Override
+    public Set<TenantId> getTenantIds() {
+        return store.getTenantIds();
+    }
+
+    @Override
+    public VirtualNetwork createVirtualNetwork(TenantId tenantId) {
+        checkNotNull(tenantId, TENANT_NULL);
+        return store.addNetwork(tenantId);
+    }
+
+    @Override
+    public void removeVirtualNetwork(NetworkId networkId) {
+        checkNotNull(networkId, NETWORK_NULL);
+        store.removeNetwork(networkId);
+    }
+
+    @Override
+    public VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId) {
+        checkNotNull(networkId, NETWORK_NULL);
+        checkNotNull(deviceId, DEVICE_NULL);
+        return store.addDevice(networkId, deviceId);
+    }
+
+    @Override
+    public void removeVirtualDevice(NetworkId networkId, DeviceId deviceId) {
+        checkNotNull(networkId, NETWORK_NULL);
+        checkNotNull(deviceId, DEVICE_NULL);
+        store.removeDevice(networkId, deviceId);
+    }
+
+    @Override
+    public VirtualLink createVirtualLink(NetworkId networkId,
+                                         ConnectPoint src, ConnectPoint dst,
+                                         TunnelId realizedBy) {
+        checkNotNull(networkId, NETWORK_NULL);
+        checkNotNull(src, LINK_POINT_NULL);
+        checkNotNull(dst, LINK_POINT_NULL);
+        checkNotNull(realizedBy, "Tunnel ID cannot be null");
+        return store.addLink(networkId, src, dst, realizedBy);
+    }
+
+    @Override
+    public void removeVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+        checkNotNull(networkId, NETWORK_NULL);
+        checkNotNull(src, LINK_POINT_NULL);
+        checkNotNull(dst, LINK_POINT_NULL);
+        store.removeLink(networkId, src, dst);
+    }
+
+    @Override
+    public VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId,
+                                         PortNumber portNumber, Port realizedBy) {
+        checkNotNull(networkId, NETWORK_NULL);
+        checkNotNull(deviceId, DEVICE_NULL);
+        checkNotNull(portNumber, "Port description cannot be null");
+        return store.addPort(networkId, deviceId, portNumber, realizedBy);
+    }
+
+    @Override
+    public void removeVirtualPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) {
+        checkNotNull(networkId, NETWORK_NULL);
+        checkNotNull(deviceId, DEVICE_NULL);
+        checkNotNull(portNumber, "Port number cannot be null");
+        store.removePort(networkId, deviceId, portNumber);
+    }
+
+    @Override
+    public Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId) {
+        checkNotNull(tenantId, TENANT_NULL);
+        return store.getNetworks(tenantId);
+    }
+
+    @Override
+    public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
+        checkNotNull(networkId, NETWORK_NULL);
+        return store.getDevices(networkId);
+    }
+
+    @Override
+    public Set<VirtualLink> getVirtualLinks(NetworkId networkId) {
+        checkNotNull(networkId, NETWORK_NULL);
+        return store.getLinks(networkId);
+    }
+
+    @Override
+    public Set<VirtualPort> getVirtualPorts(NetworkId networkId, DeviceId deviceId) {
+        checkNotNull(networkId, NETWORK_NULL);
+        checkNotNull(deviceId, DEVICE_NULL);
+        return store.getPorts(networkId, deviceId);
+    }
+
+    @Override
+    public <T> T get(NetworkId networkId, Class<T> serviceClass) {
+        checkNotNull(networkId, NETWORK_NULL);
+        return null;
+    }
+
+    @Override
+    protected VirtualNetworkProviderService createProviderService(VirtualNetworkProvider provider) {
+        return new InternalVirtualNetworkProviderService(provider);
+    }
+
+    // Service issued to registered virtual network providers so that they
+    // can interact with the core.
+    private class InternalVirtualNetworkProviderService
+            extends AbstractProviderService<VirtualNetworkProvider>
+            implements VirtualNetworkProviderService {
+        InternalVirtualNetworkProviderService(VirtualNetworkProvider provider) {
+            super(provider);
+        }
+    }
+
+    // Auxiliary store delegate to receive notification about changes in
+    // the virtual network configuration store state - by the store itself.
+    private class InternalStoreDelegate implements VirtualNetworkStoreDelegate {
+        @Override
+        public void notify(VirtualNetworkEvent event) {
+            post(event);
+        }
+    }
+
+}
diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java
new file mode 100644 (file)
index 0000000..da4be5a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the virtual network subsystem.
+ */
+package org.onosproject.incubator.net.virtual.impl;
\ No newline at end of file
diff --git a/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java b/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java
new file mode 100644 (file)
index 0000000..545e21d
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.net.mcast.impl;
+
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.core.Version;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.mcast.McastEvent;
+import org.onosproject.net.mcast.McastListener;
+import org.onosproject.net.mcast.McastRoute;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.List;
+import java.util.Set;
+
+import static junit.framework.Assert.fail;
+import static junit.framework.TestCase.assertEquals;
+import static org.onosproject.net.NetTestTools.did;
+import static org.onosproject.net.NetTestTools.injectEventDispatcher;
+
+/**
+ * Tests for the multicast RIB.
+ */
+public class MulticastRouteManagerTest {
+
+    McastRoute r1 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"),
+                                          IpPrefix.valueOf("1.1.1.2/8"),
+                                          McastRoute.Type.IGMP);
+
+    McastRoute r11 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"),
+                                          IpPrefix.valueOf("1.1.1.2/8"),
+                                          McastRoute.Type.STATIC);
+
+    McastRoute r2 = new McastRoute(IpPrefix.valueOf("2.2.2.1/8"),
+                                          IpPrefix.valueOf("2.2.2.2/8"),
+                                          McastRoute.Type.PIM);
+
+    ConnectPoint cp1 = new ConnectPoint(did("1"), PortNumber.portNumber(1));
+
+    ConnectPoint cp2 = new ConnectPoint(did("2"), PortNumber.portNumber(2));
+
+    private TestMulticastListener listener = new TestMulticastListener();
+
+    private MulticastRouteManager manager;
+
+    private List<McastEvent> events;
+
+    @Before
+    public void setUp() throws Exception {
+        manager = new MulticastRouteManager();
+        injectEventDispatcher(manager, new TestEventDispatcher());
+        TestUtils.setField(manager, "storageService", new TestStorageService());
+        TestUtils.setField(manager, "coreService", new TestCoreService());
+        events  = Lists.newArrayList();
+        manager.activate();
+        manager.addListener(listener);
+    }
+
+    @After
+    public void tearDown() {
+        manager.removeListener(listener);
+        manager.deactivate();
+    }
+
+    @Test
+    public void testAdd() {
+        manager.add(r1);
+
+        assertEquals("Add failed", manager.mcastRoutes.size(), 1);
+        validateEvents(McastEvent.Type.ROUTE_ADDED);
+    }
+
+    @Test
+    public void testRemove() {
+        manager.add(r1);
+
+        manager.remove(r1);
+
+        assertEquals("Remove failed", manager.mcastRoutes.size(), 0);
+        validateEvents(McastEvent.Type.ROUTE_ADDED, McastEvent.Type.ROUTE_REMOVED);
+    }
+
+    @Test
+    public void testAddSource() {
+        manager.add(r1);
+
+        manager.addSource(r1, cp1);
+
+        validateEvents(McastEvent.Type.ROUTE_ADDED, McastEvent.Type.SOURCE_ADDED);
+        assertEquals("Route is not equal", cp1, manager.fetchSource(r1));
+    }
+
+    @Test
+    public void testAddSink() {
+        manager.add(r1);
+
+        manager.addSource(r1, cp1);
+        manager.addSink(r1, cp1);
+
+        validateEvents(McastEvent.Type.ROUTE_ADDED,
+                       McastEvent.Type.SOURCE_ADDED,
+                       McastEvent.Type.SINK_ADDED);
+        assertEquals("Route is not equal", Lists.newArrayList(cp1), manager.fetchSinks(r1));
+    }
+
+    @Test
+    public void testRemoveSink() {
+        manager.add(r1);
+
+        manager.addSource(r1, cp1);
+        manager.addSink(r1, cp1);
+        manager.addSink(r1, cp2);
+        manager.removeSink(r1, cp2);
+
+        validateEvents(McastEvent.Type.ROUTE_ADDED,
+                       McastEvent.Type.SOURCE_ADDED,
+                       McastEvent.Type.SINK_ADDED,
+                       McastEvent.Type.SINK_ADDED,
+                       McastEvent.Type.SINK_REMOVED);
+        assertEquals("Route is not equal", Lists.newArrayList(cp1), manager.fetchSinks(r1));
+    }
+
+    private void validateEvents(McastEvent.Type... evs) {
+        if (events.size() != evs.length) {
+            fail(String.format("Mismatch number of events# obtained -> %s : expected %s",
+                               events, evs));
+        }
+
+        for (int i = 0; i < evs.length; i++) {
+            if (evs[i] != events.get(i).type()) {
+                fail(String.format("Mismtached events# obtained -> %s : expected %s",
+                                   events, evs));
+            }
+        }
+    }
+
+    class TestMulticastListener implements McastListener {
+
+        @Override
+        public void event(McastEvent event) {
+            events.add(event);
+        }
+    }
+
+    private class TestCoreService implements CoreService {
+        @Override
+        public Version version() {
+            return null;
+        }
+
+        @Override
+        public Set<ApplicationId> getAppIds() {
+            return null;
+        }
+
+        @Override
+        public ApplicationId getAppId(Short id) {
+            return null;
+        }
+
+        @Override
+        public ApplicationId getAppId(String name) {
+            return null;
+        }
+
+        @Override
+        public ApplicationId registerApplication(String identifier) {
+            return new DefaultApplicationId(0, identifier);
+        }
+
+        @Override
+        public IdGenerator getIdGenerator(String topic) {
+            return null;
+        }
+    }
+}
index e0c0c86..76caebc 100644 (file)
@@ -130,7 +130,7 @@ public class MeterManagerTest {
         m2 = DefaultMeter.builder()
                 .forDevice(did("2"))
                 .fromApp(APP_ID)
-                .withId(MeterId.meterId(2))
+                .withId(MeterId.meterId(1))
                 .withUnit(Meter.Unit.KB_PER_SEC)
                 .withBands(Collections.singletonList(band))
                 .build();
@@ -167,7 +167,7 @@ public class MeterManagerTest {
 
         assertTrue("The meter was not added", manager.getAllMeters().size() == 1);
 
-        assertThat(manager.getMeter(MeterId.meterId(1)), is(m1));
+        assertThat(manager.getMeter(did("1"), MeterId.meterId(1)), is(m1));
     }
 
     @Test
@@ -175,7 +175,7 @@ public class MeterManagerTest {
         manager.submit(m1Request.add());
         manager.withdraw(m1Request.remove(), m1.id());
 
-        assertThat(manager.getMeter(MeterId.meterId(1)).state(),
+        assertThat(manager.getMeter(did("1"), MeterId.meterId(1)).state(),
                    is(MeterState.PENDING_REMOVE));
 
         providerService.pushMeterMetrics(m1.deviceId(), Collections.emptyList());
@@ -184,7 +184,16 @@ public class MeterManagerTest {
 
     }
 
+    @Test
+    public void testMultipleDevice() {
+        manager.submit(m1Request.add());
+        manager.submit(m2Request.add());
 
+        assertTrue("The meters were not added", manager.getAllMeters().size() == 2);
+
+        assertThat(manager.getMeter(did("1"), MeterId.meterId(1)), is(m1));
+        assertThat(manager.getMeter(did("2"), MeterId.meterId(1)), is(m2));
+    }
 
     public class TestApplicationId extends DefaultApplicationId {
         public TestApplicationId(int id, String name) {
index 32890cb..62a9467 100644 (file)
@@ -33,6 +33,7 @@ import org.onosproject.net.meter.Meter;
 import org.onosproject.net.meter.MeterEvent;
 import org.onosproject.net.meter.MeterFailReason;
 import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterKey;
 import org.onosproject.net.meter.MeterOperation;
 import org.onosproject.net.meter.MeterState;
 import org.onosproject.net.meter.MeterStore;
@@ -78,12 +79,12 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private ClusterService clusterService;
 
-    private ConsistentMap<MeterId, MeterData> meters;
+    private ConsistentMap<MeterKey, MeterData> meters;
     private NodeId local;
 
-    private MapEventListener mapListener = new InternalMapEventListener();
+    private MapEventListener<MeterKey, MeterData> mapListener = new InternalMapEventListener();
 
-    private Map<MeterId, CompletableFuture<MeterStoreResult>> futures =
+    private Map<MeterKey, CompletableFuture<MeterStoreResult>> futures =
             Maps.newConcurrentMap();
 
     @Activate
@@ -92,9 +93,10 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
         local = clusterService.getLocalNode().id();
 
 
-        meters = storageService.<MeterId, MeterData>consistentMapBuilder()
+        meters = storageService.<MeterKey, MeterData>consistentMapBuilder()
                     .withName(METERSTORE)
                     .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API),
+                                                     MeterKey.class,
                                                      MeterData.class,
                                                      DefaultMeter.class,
                                                      DefaultBand.class,
@@ -120,11 +122,12 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
     @Override
     public CompletableFuture<MeterStoreResult> storeMeter(Meter meter) {
         CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
-        futures.put(meter.id(), future);
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        futures.put(key, future);
         MeterData data = new MeterData(meter, null, local);
 
         try {
-            meters.put(meter.id(), data);
+            meters.put(key, data);
         } catch (StorageException e) {
             future.completeExceptionally(e);
         }
@@ -136,14 +139,15 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
     @Override
     public CompletableFuture<MeterStoreResult> deleteMeter(Meter meter) {
         CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
-        futures.put(meter.id(), future);
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        futures.put(key, future);
 
         MeterData data = new MeterData(meter, null, local);
 
         // update the state of the meter. It will be pruned by observing
         // that it has been removed from the dataplane.
         try {
-            if (meters.computeIfPresent(meter.id(), (k, v) -> data) == null) {
+            if (meters.computeIfPresent(key, (k, v) -> data) == null) {
                 future.complete(MeterStoreResult.success());
             }
         } catch (StorageException e) {
@@ -157,11 +161,12 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
     @Override
     public CompletableFuture<MeterStoreResult> updateMeter(Meter meter) {
         CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
-        futures.put(meter.id(), future);
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        futures.put(key, future);
 
         MeterData data = new MeterData(meter, null, local);
         try {
-            if (meters.computeIfPresent(meter.id(), (k, v) -> data) == null) {
+            if (meters.computeIfPresent(key, (k, v) -> data) == null) {
                 future.complete(MeterStoreResult.fail(MeterFailReason.INVALID_METER));
             }
         } catch (StorageException e) {
@@ -172,7 +177,8 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
 
     @Override
     public void updateMeterState(Meter meter) {
-        meters.computeIfPresent(meter.id(), (id, v) -> {
+        MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+        meters.computeIfPresent(key, (k, v) -> {
             DefaultMeter m = (DefaultMeter) v.meter();
             m.setState(meter.state());
             m.setProcessedPackets(meter.packetsSeen());
@@ -185,8 +191,8 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
     }
 
     @Override
-    public Meter getMeter(MeterId meterId) {
-        MeterData data = Versioned.valueOrElse(meters.get(meterId), null);
+    public Meter getMeter(MeterKey key) {
+        MeterData data = Versioned.valueOrElse(meters.get(key), null);
         return data == null ? null : data.meter();
     }
 
@@ -198,19 +204,22 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
 
     @Override
     public void failedMeter(MeterOperation op, MeterFailReason reason) {
-        meters.computeIfPresent(op.meter().id(), (k, v) ->
+        MeterKey key = MeterKey.key(op.meter().deviceId(), op.meter().id());
+        meters.computeIfPresent(key, (k, v) ->
                 new MeterData(v.meter(), reason, v.origin()));
     }
 
     @Override
     public void deleteMeterNow(Meter m) {
-        futures.remove(m.id());
-        meters.remove(m.id());
+        MeterKey key = MeterKey.key(m.deviceId(), m.id());
+        futures.remove(key);
+        meters.remove(key);
     }
 
-    private class InternalMapEventListener implements MapEventListener<MeterId, MeterData> {
+    private class InternalMapEventListener implements MapEventListener<MeterKey, MeterData> {
         @Override
-        public void event(MapEvent<MeterId, MeterData> event) {
+        public void event(MapEvent<MeterKey, MeterData> event) {
+            MeterKey key = event.key();
             MeterData data = event.value().value();
             NodeId master = mastershipService.getMasterFor(data.meter().deviceId());
             switch (event.type()) {
@@ -227,17 +236,17 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD
                                 } else if (data.reason().isPresent() && local.equals(data.origin())) {
                                     MeterStoreResult msr = MeterStoreResult.fail(data.reason().get());
                                     //TODO: No future -> no friend
-                                    futures.get(data.meter().id()).complete(msr);
+                                    futures.get(key).complete(msr);
                                 }
                                 break;
                             case ADDED:
                                 if (local.equals(data.origin()) && data.meter().state() == MeterState.PENDING_ADD) {
-                                    futures.remove(data.meter().id()).complete(MeterStoreResult.success());
+                                    futures.remove(key).complete(MeterStoreResult.success());
                                 }
                                 break;
                             case REMOVED:
                                 if (local.equals(data.origin()) && data.meter().state() == MeterState.PENDING_REMOVE) {
-                                    futures.remove(data.meter().id()).complete(MeterStoreResult.success());
+                                    futures.remove(key).complete(MeterStoreResult.success());
                                 }
                                 break;
                             default:
diff --git a/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
new file mode 100644 (file)
index 0000000..69e56c0
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.incubator.store.virtual.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
+import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.TenantId;
+import org.onosproject.incubator.net.virtual.VirtualDevice;
+import org.onosproject.incubator.net.virtual.VirtualLink;
+import org.onosproject.incubator.net.virtual.VirtualNetwork;
+import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
+import org.onosproject.incubator.net.virtual.VirtualNetworkStoreDelegate;
+import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.store.AbstractStore;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the network store.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedVirtualNetworkStore
+        extends AbstractStore<VirtualNetworkEvent, VirtualNetworkStoreDelegate>
+        implements VirtualNetworkStore {
+
+    private final Logger log = getLogger(getClass());
+
+    // TODO: track tenants by ID
+    // TODO: track networks by ID and by tenants
+    // TODO: track devices by network ID and device ID
+    // TODO: track devices by network ID
+    // TODO: setup block allocator for network IDs
+
+    // TODO: notify delegate
+
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public void addTenantId(TenantId tenantId) {
+    }
+
+    @Override
+    public void removeTenantId(TenantId tenantId) {
+    }
+
+    @Override
+    public Set<TenantId> getTenantIds() {
+        return null;
+    }
+
+    @Override
+    public VirtualNetwork addNetwork(TenantId tenantId) {
+        return new DefaultVirtualNetwork(genNetworkId(), tenantId);
+    }
+
+    private NetworkId genNetworkId() {
+        return NetworkId.networkId(0); // TODO: use a block allocator
+    }
+
+
+    @Override
+    public void removeNetwork(NetworkId networkId) {
+    }
+
+    @Override
+    public VirtualDevice addDevice(NetworkId networkId, DeviceId deviceId) {
+        return new DefaultVirtualDevice(networkId, deviceId);
+    }
+
+    @Override
+    public void removeDevice(NetworkId networkId, DeviceId deviceId) {
+    }
+
+    @Override
+    public VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId realizedBy) {
+        return null;
+    }
+
+    @Override
+    public void removeLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) {
+    }
+
+    @Override
+    public VirtualPort addPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber, Port realizedBy) {
+        return null;
+    }
+
+    @Override
+    public void removePort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) {
+    }
+
+    @Override
+    public Set<VirtualNetwork> getNetworks(TenantId tenantId) {
+        return null;
+    }
+
+    @Override
+    public Set<VirtualDevice> getDevices(NetworkId networkId) {
+        return null;
+    }
+
+    @Override
+    public Set<VirtualLink> getLinks(NetworkId networkId) {
+        return null;
+    }
+
+    @Override
+    public Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId) {
+        return null;
+    }
+}
diff --git a/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/package-info.java b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/package-info.java
new file mode 100644 (file)
index 0000000..12fa909
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of distributed virtual network store.
+ */
+package org.onosproject.incubator.store.virtual.impl;
index 238c400..af92a1d 100644 (file)
@@ -104,7 +104,7 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext
         } catch (BufferUnderflowException | NullPointerException |
                 DeserializationException e) {
             Logger log = LoggerFactory.getLogger(getClass());
-            log.warn("packet deserialization problem : {}", e.getMessage());
+            log.error("packet deserialization problem : {}", e.getMessage());
             return null;
         }
     }
index 0c28a6f..9d35515 100644 (file)
@@ -16,6 +16,8 @@
 
 package org.onosproject.openflow.controller.impl;
 
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import org.jboss.netty.bootstrap.ServerBootstrap;
 import org.jboss.netty.channel.ChannelPipelineFactory;
 import org.jboss.netty.channel.group.ChannelGroup;
@@ -37,13 +39,24 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.FileInputStream;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.util.Dictionary;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
+import static org.onlab.util.Tools.get;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.net.DeviceId.deviceId;
 import static org.onosproject.openflow.controller.Dpid.uri;
@@ -59,14 +72,16 @@ public class Controller {
 
     protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13);
     protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10);
+    private static final boolean TLS_DISABLED = false;
+    private static final short MIN_KS_LENGTH = 6;
 
     protected HashMap<String, String> controllerNodeIPsCache;
 
     private ChannelGroup cg;
 
     // Configuration options
-    protected int openFlowPort = 6633;
-    protected int workerThreads = 0;
+    protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653);
+    protected int workerThreads = 16;
 
     // Start time of the controller
     protected long systemStartTime;
@@ -75,9 +90,16 @@ public class Controller {
 
     private NioServerSocketChannelFactory execFactory;
 
+    protected String ksLocation;
+    protected String tsLocation;
+    protected char[] ksPwd;
+    protected char[] tsPwd;
+    private SSLEngine serverSSLEngine;
+
     // Perf. related configuration
     protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
     private DriverService driverService;
+    private boolean enableOFTLS = TLS_DISABLED;
 
     // ***************
     // Getters/Setters
@@ -127,13 +149,15 @@ public class Controller {
             bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
 
             ChannelPipelineFactory pfact =
-                    new OpenflowPipelineFactory(this, null);
+                    new OpenflowPipelineFactory(this, null, serverSSLEngine);
             bootstrap.setPipelineFactory(pfact);
-            InetSocketAddress sa = new InetSocketAddress(openFlowPort);
             cg = new DefaultChannelGroup();
-            cg.add(bootstrap.bind(sa));
+            openFlowPorts.forEach(port -> {
+                InetSocketAddress sa = new InetSocketAddress(port);
+                cg.add(bootstrap.bind(sa));
+                log.info("Listening for switch connections on {}", sa);
+            });
 
-            log.info("Listening for switch connections on {}", sa);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -155,19 +179,22 @@ public class Controller {
         }
     }
 
-    public void setConfigParams(Map<String, String> configParams) {
-        String ofPort = configParams.get("openflowport");
-        if (ofPort != null) {
-            this.openFlowPort = Integer.parseInt(ofPort);
+    public void setConfigParams(Dictionary<?, ?> properties) {
+        String ports = get(properties, "openflowPorts");
+        if (!Strings.isNullOrEmpty(ports)) {
+            this.openFlowPorts = Stream.of(ports.split(","))
+                                       .map(s -> Integer.parseInt(s))
+                                       .collect(Collectors.toList());
         }
+        log.debug("OpenFlow ports set to {}", this.openFlowPorts);
 
-        log.debug("OpenFlow port set to {}", this.openFlowPort);
-        String threads = configParams.get("workerthreads");
-        this.workerThreads = threads != null ? Integer.parseInt(threads) : 16;
+        String threads = get(properties, "workerThreads");
+        if (!Strings.isNullOrEmpty(threads)) {
+            this.workerThreads = Integer.parseInt(threads);
+        }
         log.debug("Number of worker threads set to {}", this.workerThreads);
     }
 
-
     /**
      * Initialize internal data structures.
      */
@@ -177,6 +204,68 @@ public class Controller {
         this.controllerNodeIPsCache = new HashMap<>();
 
         this.systemStartTime = System.currentTimeMillis();
+
+        try {
+            getTLSParameters();
+            if (enableOFTLS) {
+                initSSL();
+            }
+        } catch (Exception ex) {
+            log.error("SSL init failed: {}", ex.getMessage());
+        }
+
+    }
+
+    private void getTLSParameters() {
+        String tempString = System.getProperty("enableOFTLS");
+        enableOFTLS = Strings.isNullOrEmpty(tempString) ? TLS_DISABLED : Boolean.parseBoolean(tempString);
+        log.info("OpenFlow Security is {}", enableOFTLS ? "enabled" : "disabled");
+        if (enableOFTLS) {
+            ksLocation = System.getProperty("javax.net.ssl.keyStore");
+            if (Strings.isNullOrEmpty(ksLocation)) {
+                enableOFTLS = TLS_DISABLED;
+                return;
+            }
+            tsLocation = System.getProperty("javax.net.ssl.trustStore");
+            if (Strings.isNullOrEmpty(tsLocation)) {
+                enableOFTLS = TLS_DISABLED;
+                return;
+            }
+            ksPwd = System.getProperty("javax.net.ssl.keyStorePassword").toCharArray();
+            if (MIN_KS_LENGTH > ksPwd.length) {
+                enableOFTLS = TLS_DISABLED;
+                return;
+            }
+            tsPwd = System.getProperty("javax.net.ssl.trustStorePassword").toCharArray();
+            if (MIN_KS_LENGTH > tsPwd.length) {
+                enableOFTLS = TLS_DISABLED;
+                return;
+            }
+        }
+    }
+
+    private void initSSL() throws Exception {
+
+        TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        KeyStore ts = KeyStore.getInstance("JKS");
+        ts.load(new FileInputStream(tsLocation), tsPwd);
+        tmFactory.init(ts);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        KeyStore ks = KeyStore.getInstance("JKS");
+        ks.load(new FileInputStream(ksLocation), ksPwd);
+        kmf.init(ks, ksPwd);
+
+        SSLContext serverContext = SSLContext.getInstance("TLS");
+        serverContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
+
+        serverSSLEngine = serverContext.createSSLEngine();
+
+        serverSSLEngine.setNeedClientAuth(true);
+        serverSSLEngine.setUseClientMode(false);
+        serverSSLEngine.setEnabledProtocols(serverSSLEngine.getSupportedProtocols());
+        serverSSLEngine.setEnabledCipherSuites(serverSSLEngine.getSupportedCipherSuites());
+        serverSSLEngine.setEnableSessionCreation(true);
     }
 
     // **************
index 450aa82..1a088ff 100644 (file)
@@ -941,7 +941,8 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
         void processOFHello(OFChannelHandler h, OFHello m)
                 throws IOException, SwitchStateException {
             // we only expect hello in the WAIT_HELLO state
-            illegalMessageReceived(h, m);
+            log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
+                     h.channel.getRemoteAddress());
         }
 
         void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
index c82078a..d042994 100644 (file)
@@ -15,7 +15,6 @@
  */
 package org.onosproject.openflow.controller.impl;
 
-import com.google.common.base.Strings;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
@@ -48,6 +47,8 @@ import org.projectfloodlight.openflow.protocol.OFExperimenter;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
@@ -67,11 +68,8 @@ import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -80,13 +78,12 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-import static org.onlab.util.Tools.get;
 import static org.onlab.util.Tools.groupedThreads;
 
 @Component(immediate = true)
 @Service
 public class OpenFlowControllerImpl implements OpenFlowController {
-    private static final int DEFAULT_OFPORT = 6633;
+    private static final String DEFAULT_OFPORT = "6633,6653";
     private static final int DEFAULT_WORKER_THREADS = 16;
 
     private static final Logger log =
@@ -102,9 +99,9 @@ public class OpenFlowControllerImpl implements OpenFlowController {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService cfgService;
 
-    @Property(name = "openflowPort", intValue = DEFAULT_OFPORT,
-            label = "Port number used by OpenFlow protocol; default is 6633")
-    private int openflowPort = DEFAULT_OFPORT;
+    @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
+            label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
+    private String openflowPorts = DEFAULT_OFPORT;
 
     @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
             label = "Number of controller worker threads; default is 16")
@@ -134,6 +131,9 @@ public class OpenFlowControllerImpl implements OpenFlowController {
     protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
             ArrayListMultimap.create();
 
+    protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
+            ArrayListMultimap.create();
+
     protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
             ArrayListMultimap.create();
 
@@ -148,8 +148,7 @@ public class OpenFlowControllerImpl implements OpenFlowController {
     @Activate
     public void activate(ComponentContext context) {
         cfgService.registerProperties(getClass());
-        Map<String, String> properties = readComponentConfiguration(context);
-        ctrl.setConfigParams(properties);
+        ctrl.setConfigParams(context.getProperties());
         ctrl.start(agent, driverService);
     }
 
@@ -159,33 +158,10 @@ public class OpenFlowControllerImpl implements OpenFlowController {
         ctrl.stop();
     }
 
-    /**
-     * Extracts properties from the component configuration context.
-     *
-     * @param context the component context
-     */
-    private Map<String, String> readComponentConfiguration(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        Map<String, String> outProperties = new HashMap<>();
-
-        String port = get(properties, "openflowPort");
-        if (!Strings.isNullOrEmpty(port)) {
-            outProperties.put("openflowport", port);
-        }
-
-        String thread = get(properties, "workerThreads");
-        if (!Strings.isNullOrEmpty(thread)) {
-            outProperties.put("workerthreads", thread);
-        }
-
-        return outProperties;
-    }
-
     @Modified
     public void modified(ComponentContext context) {
-        Map<String, String> properties = readComponentConfiguration(context);
         ctrl.stop();
-        ctrl.setConfigParams(properties);
+        ctrl.setConfigParams(context.getProperties());
         ctrl.start(agent, driverService);
     }
 
@@ -259,6 +235,7 @@ public class OpenFlowControllerImpl implements OpenFlowController {
     @Override
     public void processPacket(Dpid dpid, OFMessage msg) {
         Collection<OFFlowStatsEntry> flowStats;
+        Collection<OFTableStatsEntry> tableStats;
         Collection<OFGroupStatsEntry> groupStats;
         Collection<OFGroupDescStatsEntry> groupDescStats;
         Collection<OFPortStatsEntry> portStats;
@@ -302,6 +279,16 @@ public class OpenFlowControllerImpl implements OpenFlowController {
                         OFFlowStatsReply.Builder rep =
                                 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
                         rep.setEntries(Lists.newLinkedList(flowStats));
+                        rep.setXid(reply.getXid());
+                        executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
+                    }
+                    break;
+                case TABLE:
+                    tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
+                    if (tableStats != null) {
+                        OFTableStatsReply.Builder rep =
+                                OFFactories.getFactory(msg.getVersion()).buildTableStatsReply();
+                        rep.setEntries(Lists.newLinkedList(tableStats));
                         executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
                     }
                     break;
@@ -423,6 +410,16 @@ public class OpenFlowControllerImpl implements OpenFlowController {
         return null;
     }
 
+    private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
+                                                                       OFTableStatsReply reply) {
+        //TODO: Get rid of synchronized
+        fullTableStats.putAll(dpid, reply.getEntries());
+        if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+            return fullTableStats.removeAll(dpid);
+        }
+        return null;
+    }
+
     private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
                                                                       OFGroupStatsReply reply) {
         //TODO: Get rid of synchronized
index c7ba105..1467520 100644 (file)
@@ -27,6 +27,10 @@ import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
 import org.jboss.netty.util.ExternalResourceReleasable;
 import org.jboss.netty.util.HashedWheelTimer;
 import org.jboss.netty.util.Timer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLEngine;
 
 /**
  * Creates a ChannelPipeline for a server-side openflow channel.
@@ -34,6 +38,9 @@ import org.jboss.netty.util.Timer;
 public class OpenflowPipelineFactory
     implements ChannelPipelineFactory, ExternalResourceReleasable {
 
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final SSLEngine sslEngine;
     protected Controller controller;
     protected ThreadPoolExecutor pipelineExecutor;
     protected Timer timer;
@@ -41,13 +48,15 @@ public class OpenflowPipelineFactory
     protected ReadTimeoutHandler readTimeoutHandler;
 
     public OpenflowPipelineFactory(Controller controller,
-                                   ThreadPoolExecutor pipelineExecutor) {
+                                   ThreadPoolExecutor pipelineExecutor,
+                                   SSLEngine sslEngine) {
         super();
         this.controller = controller;
         this.pipelineExecutor = pipelineExecutor;
         this.timer = new HashedWheelTimer();
         this.idleHandler = new IdleStateHandler(timer, 20, 25, 0);
         this.readTimeoutHandler = new ReadTimeoutHandler(timer, 30);
+        this.sslEngine = sslEngine;
     }
 
     @Override
@@ -55,6 +64,13 @@ public class OpenflowPipelineFactory
         OFChannelHandler handler = new OFChannelHandler(controller);
 
         ChannelPipeline pipeline = Channels.pipeline();
+        if (sslEngine != null) {
+            log.info("OpenFlow SSL enabled.");
+            pipeline.addLast("ssl",
+                             new org.jboss.netty.handler.ssl.SslHandler(sslEngine));
+        } else {
+            log.info("OpenFlow SSL disabled");
+        }
         pipeline.addLast("ofmessagedecoder", new OFMessageDecoder());
         pipeline.addLast("ofmessageencoder", new OFMessageEncoder());
         pipeline.addLast("idle", idleHandler);
index 7089216..c264ae9 100644 (file)
             <artifactId>netty-transport-native-epoll</artifactId>
             <version>${netty4.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-ovsdb-rfc</artifactId>
index ab88a24..f2ff070 100644 (file)
  */
 package org.onosproject.ovsdb.controller;
 
-import java.util.List;
-import java.util.Set;
-
+import com.google.common.util.concurrent.ListenableFuture;
 import org.onlab.packet.IpAddress;
-
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerInfo;
 import org.onosproject.ovsdb.rfc.jsonrpc.OvsdbRPC;
 import org.onosproject.ovsdb.rfc.message.OperationResult;
 import org.onosproject.ovsdb.rfc.message.TableUpdates;
 import org.onosproject.ovsdb.rfc.notation.Row;
+import org.onosproject.ovsdb.rfc.notation.UUID;
 import org.onosproject.ovsdb.rfc.operations.Operation;
 import org.onosproject.ovsdb.rfc.schema.DatabaseSchema;
 
-import com.google.common.util.concurrent.ListenableFuture;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Represents to provider facing side of a node.
@@ -84,11 +85,40 @@ public interface OvsdbClientService extends OvsdbRPC {
      */
     Set<OvsdbBridge> getBridges();
 
+    /**
+     * Gets controllers of the node.
+     *
+     * @return set of controllers; empty if no controller is find
+     */
+    Set<ControllerInfo> getControllers(DeviceId openflowDeviceId);
+
+    /**
+     * Sets the Controllers for the specified bridge.
+     * <p/>
+     * This method will replace the existing controller list with the new controller
+     * list.
+     *
+     * @param bridgeUuid bridge uuid
+     * @param controllers list of controllers
+     */
+    void setControllersWithUUID(UUID bridgeUuid, List<ControllerInfo> controllers);
+
+    /**
+     * Sets the Controllers for the specified device.
+     * <p/>
+     * This method will replace the existing controller list with the new controller
+     * list.
+     *
+     * @param deviceId device id (likely Openflow device)
+     * @param controllers list of controllers
+     */
+    void setControllersWithDeviceId(DeviceId deviceId, List<ControllerInfo> controllers);
+
     /**
      * Creates a port.
      *
      * @param bridgeName bridge name
-     * @param portName port name
+     * @param portName   port name
      */
     void createPort(String bridgeName, String portName);
 
@@ -96,7 +126,7 @@ public interface OvsdbClientService extends OvsdbRPC {
      * Drops a port.
      *
      * @param bridgeName bridge name
-     * @param portName port name
+     * @param portName   port name
      */
     void dropPort(String bridgeName, String portName);
 
@@ -125,7 +155,7 @@ public interface OvsdbClientService extends OvsdbRPC {
     /**
      * Gets the Port uuid.
      *
-     * @param portName port name
+     * @param portName   port name
      * @param bridgeUuid bridge uuid
      * @return port uuid, empty if no uuid is find
      */
@@ -143,7 +173,7 @@ public interface OvsdbClientService extends OvsdbRPC {
     /**
      * Gets the Controller uuid.
      *
-     * @param controllerName controller name
+     * @param controllerName   controller name
      * @param controllerTarget controller target
      * @return controller uuid, empty if no uuid is find
      */
@@ -169,7 +199,7 @@ public interface OvsdbClientService extends OvsdbRPC {
      * Gets the ovsdb table updates.
      *
      * @param dbName database name
-     * @param id random uuid
+     * @param id     random uuid
      * @return table updates
      */
     ListenableFuture<TableUpdates> monitorTables(String dbName, String id);
@@ -177,7 +207,7 @@ public interface OvsdbClientService extends OvsdbRPC {
     /**
      * Gets the ovsdb config operation result.
      *
-     * @param dbName database name
+     * @param dbName     database name
      * @param operations the list of operations
      * @return operation results
      */
@@ -187,7 +217,7 @@ public interface OvsdbClientService extends OvsdbRPC {
     /**
      * Gets the ovsdb database schema from local.
      *
-     * @param  dbName database name
+     * @param dbName database name
      * @return database schema
      */
     DatabaseSchema getDatabaseSchema(String dbName);
@@ -195,9 +225,9 @@ public interface OvsdbClientService extends OvsdbRPC {
     /**
      * Gets the ovsdb row from the local ovsdb store.
      *
-     * @param dbName database name
+     * @param dbName    database name
      * @param tableName table name
-     * @param uuid row uuid
+     * @param uuid      row uuid
      * @return row ovsdb row
      */
     Row getRow(String dbName, String tableName, String uuid);
@@ -205,19 +235,19 @@ public interface OvsdbClientService extends OvsdbRPC {
     /**
      * Removes the ovsdb row from the local ovsdb store.
      *
-     * @param dbName database name
+     * @param dbName    database name
      * @param tableName table name
-     * @param uuid row uuid
+     * @param uuid      row uuid
      */
     void removeRow(String dbName, String tableName, String uuid);
 
     /**
      * Updates the local ovsdb store.
      *
-     * @param dbName database name
+     * @param dbName    database name
      * @param tableName table name
-     * @param uuid row uuid
-     * @param row ovsdb row
+     * @param uuid      row uuid
+     * @param row       ovsdb row
      */
     void updateOvsdbStore(String dbName, String tableName, String uuid, Row row);
 
@@ -228,4 +258,9 @@ public interface OvsdbClientService extends OvsdbRPC {
      * @return ovsdb ports
      */
     Set<OvsdbPort> getLocalPorts(Iterable<String> ifaceids);
+
+    /**
+     * Disconnects the ovsdb server.
+     */
+    void disconnect();
 }
index c1133bb..e91c928 100644 (file)
@@ -60,7 +60,7 @@ public final class OvsdbConstant {
     public static final String EXTERNAL_ID_VM_MAC = "attached-mac";
 
     /** Openflow port. */
-    public static final int OFPORT = 6633;
+    public static final int OFPORT = 6653;
 
     /** Ovsdb port. */
     public static final int OVSDBPORT = 6640;
index 9e24524..24bfeae 100644 (file)
@@ -15,6 +15,9 @@
  */
 package org.onosproject.ovsdb.controller;
 
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+
 import java.util.List;
 
 /**
@@ -65,4 +68,12 @@ public interface OvsdbController {
      * @return OvsdbClient ovsdb node information
      */
     OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId);
+
+    /**
+     * Connect to the ovsdb server with given ip address and port number.
+     *
+     * @param ip ip address
+     * @param port port number
+     */
+    void connect(IpAddress ip, TpPort port);
 }
index 0c64cc0..3a84d00 100644 (file)
  */
 package org.onosproject.ovsdb.controller.driver;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 import io.netty.channel.Channel;
-
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutionException;
-
 import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerInfo;
 import org.onosproject.ovsdb.controller.OvsdbBridge;
 import org.onosproject.ovsdb.controller.OvsdbBridgeName;
 import org.onosproject.ovsdb.controller.OvsdbClientService;
@@ -71,14 +71,17 @@ import org.onosproject.ovsdb.rfc.utils.MutationUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.base.Function;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
 
 /**
  * An representation of an ovsdb client.
@@ -168,9 +171,8 @@ public class DefaultOvsdbClient
     /**
      * Gets the ovsdb row store.
      *
-     * @param dbName the ovsdb database name
+     * @param dbName    the ovsdb database name
      * @param tableName the ovsdb table name
-     *
      * @return ovsRowStore, empty if row store is find
      */
     private OvsdbRowStore getRowStore(String dbName, String tableName) {
@@ -184,9 +186,9 @@ public class DefaultOvsdbClient
     /**
      * Gets the ovsdb row.
      *
-     * @param dbName the ovsdb database name
+     * @param dbName    the ovsdb database name
      * @param tableName the ovsdb table name
-     * @param uuid the key of the row
+     * @param uuid      the key of the row
      * @return row, empty if row is find
      */
     @Override
@@ -394,7 +396,7 @@ public class DefaultOvsdbClient
         port.setName(portName);
         if (portUuid == null) {
             insertConfig(OvsdbConstant.PORT, "_uuid", OvsdbConstant.BRIDGE,
-                      "ports", bridgeUuid, port.getRow());
+                         "ports", bridgeUuid, port.getRow());
         } else {
             updateConfig(OvsdbConstant.PORT, "_uuid", portUuid, port.getRow());
         }
@@ -414,7 +416,7 @@ public class DefaultOvsdbClient
         if (portUuid != null) {
             log.info("Port {} delete", portName);
             deleteConfig(OvsdbConstant.PORT, "_uuid", portUuid,
-                      OvsdbConstant.BRIDGE, "ports");
+                         OvsdbConstant.BRIDGE, "ports");
         }
     }
 
@@ -455,8 +457,8 @@ public class DefaultOvsdbClient
 
             bridge.setName(bridgeName);
             bridgeUuid = insertConfig(OvsdbConstant.BRIDGE, "_uuid",
-                                   OvsdbConstant.DATABASENAME, "bridges",
-                                   ovsUuid, bridge.getRow());
+                                      OvsdbConstant.DATABASENAME, "bridges",
+                                      ovsUuid, bridge.getRow());
 
             if (bridgeUuid != null) {
                 Port port = (Port) TableGenerator.createTable(dbSchema,
@@ -466,7 +468,7 @@ public class DefaultOvsdbClient
                     port.setName(bridgeName);
 
                     insertConfig(OvsdbConstant.PORT, "_uuid", "Bridge", "ports", bridgeUuid,
-                              port.getRow());
+                                 port.getRow());
                 }
             }
 
@@ -475,51 +477,92 @@ public class DefaultOvsdbClient
             updateConfig(OvsdbConstant.BRIDGE, "_uuid", bridgeUuid, bridge.getRow());
         }
 
-        setController(bridgeUuid);
+        setControllerAuto(bridgeUuid);
         log.info("Create bridge success");
     }
 
     /**
-     * Sets the Controller.
+     * Sets the bridge's controller automatically.
+     * <p/>
+     * The connection is a TCP connection to the local ONOS instance's IP
+     * and the default OpenFlow port.
      *
      * @param bridgeUuid bridge uuid
      */
-    private void setController(String bridgeUuid) {
-        String controllerUuid = null;
-        String iPAddress = IpAddress.valueOf(((InetSocketAddress) channel
-                                                     .localAddress())
-                                                     .getAddress()
-                                                     .getHostAddress())
-                .toString();
+    private void setControllerAuto(String bridgeUuid) {
+        IpAddress ipAddress = IpAddress.valueOf(((InetSocketAddress) channel.localAddress()).getAddress());
+        ControllerInfo controllerInfo = new ControllerInfo(ipAddress, OvsdbConstant.OFPORT, "tcp");
+        log.debug("Automatically setting controller for bridge {} to {}",
+                  bridgeUuid, controllerInfo.target());
+        setControllersWithUUID(UUID.uuid(bridgeUuid), ImmutableList.of(controllerInfo));
+    }
 
-        String target = "tcp:" + iPAddress + ":" + OvsdbConstant.OFPORT;
-        log.debug("controller IP {}: port {}", iPAddress, OvsdbConstant.OFPORT);
+    @Override
+    public void setControllersWithUUID(UUID bridgeUuid, List<ControllerInfo> controllers) {
 
         DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME);
-        Controller controller = (Controller) TableGenerator
-                .createTable(dbSchema, OvsdbTable.CONTROLLER);
-
-        if (controller != null) {
-            controller.setTarget(target);
-            controllerUuid = getControllerUuid(OvsdbConstant.CONTROLLER, target);
-            if (controllerUuid == null) {
+        if (dbSchema == null) {
+            log.debug("There is no schema");
+            return;
+        }
+        List<Controller> oldControllers = getControllers(bridgeUuid);
+        if (oldControllers == null) {
+            log.warn("There are no controllers");
+            return;
+        }
 
-                insertConfig(OvsdbConstant.CONTROLLER, "_uuid",
-                          OvsdbConstant.BRIDGE, "controller", bridgeUuid,
-                          controller.getRow());
+        Set<UUID> newControllerUuids = new HashSet<>();
 
+        Set<ControllerInfo> newControllers = new HashSet<>(controllers);
+        List<Controller> removeControllers = new ArrayList<>();
+        oldControllers.forEach(controller -> {
+            ControllerInfo controllerInfo = new ControllerInfo((String) controller.getTargetColumn().data());
+            if (newControllers.contains(controllerInfo)) {
+                newControllers.remove(controllerInfo);
+                newControllerUuids.add(controller.getRow().uuid());
             } else {
+                removeControllers.add(controller);
+            }
+        });
+        OvsdbRowStore controllerRowStore = getRowStore(OvsdbConstant.DATABASENAME,
+                                                       OvsdbConstant.CONTROLLER);
+        if (controllerRowStore == null) {
+            log.debug("There is no controller table");
+            return;
+        }
 
-                Bridge bridge = (Bridge) TableGenerator
-                        .createTable(dbSchema, OvsdbTable.BRIDGE);
-                Set<UUID> controllerUuids = new HashSet<>();
-                controllerUuids.add(UUID.uuid(controllerUuid));
-                bridge.setController(controllerUuids);
-                updateConfig(OvsdbConstant.CONTROLLER, "_uuid", bridgeUuid, bridge.getRow());
+        removeControllers.forEach(c -> deleteConfig(OvsdbConstant.CONTROLLER, "_uuid", c.getRow().uuid().value(),
+                                                    OvsdbConstant.BRIDGE, "controller"));
 
-            }
+        newControllers.stream().map(c -> {
+            Controller controller = (Controller) TableGenerator
+                    .createTable(dbSchema, OvsdbTable.CONTROLLER);
+            controller.setTarget(c.target());
+            return controller;
+        }).forEach(c -> {
+            String uuid = insertConfig(OvsdbConstant.CONTROLLER, "_uuid",
+                                       OvsdbConstant.BRIDGE, "controller", bridgeUuid.value(),
+                                       c.getRow());
+            newControllerUuids.add(UUID.uuid(uuid));
+
+        });
+
+        OvsdbRowStore rowStore = getRowStore(OvsdbConstant.DATABASENAME,
+                                             OvsdbConstant.BRIDGE);
+        if (rowStore == null) {
+            log.debug("There is no bridge table");
+            return;
         }
 
+        Row bridgeRow = rowStore.getRow(bridgeUuid.value());
+        Bridge bridge = (Bridge) TableGenerator.getTable(dbSchema, bridgeRow, OvsdbTable.BRIDGE);
+        bridge.setController(OvsdbSet.ovsdbSet(newControllerUuids));
+        updateConfig(OvsdbConstant.BRIDGE, "_uuid", bridgeUuid.value(), bridge.getRow());
+    }
+
+    @Override
+    public void setControllersWithDeviceId(DeviceId deviceId, List<ControllerInfo> controllers) {
+        setControllersWithUUID(getBridgeUUID(deviceId), controllers);
     }
 
     @Override
@@ -530,7 +573,7 @@ public class DefaultOvsdbClient
             return;
         }
         deleteConfig(OvsdbConstant.BRIDGE, "_uuid", bridgeUUID,
-                  OvsdbConstant.DATABASENAME, "bridges");
+                     OvsdbConstant.DATABASENAME, "bridges");
     }
 
     @Override
@@ -554,7 +597,7 @@ public class DefaultOvsdbClient
 
         if (portUuid == null) {
             portUuid = insertConfig(OvsdbConstant.PORT, "_uuid", OvsdbConstant.BRIDGE,
-                      "ports", bridgeUuid, port.getRow());
+                                    "ports", bridgeUuid, port.getRow());
         } else {
             updateConfig(OvsdbConstant.PORT, "_uuid", portUuid, port.getRow());
         }
@@ -595,7 +638,7 @@ public class DefaultOvsdbClient
                 options.put("remote_ip", dstIp.toString());
                 tunInterface.setOptions(options);
                 updateConfig(OvsdbConstant.INTERFACE, "_uuid", interfaceUuid,
-                          tunInterface.getRow());
+                             tunInterface.getRow());
                 log.info("Tunnel added success", tunInterface);
 
             }
@@ -619,7 +662,7 @@ public class DefaultOvsdbClient
         if (portUUID != null) {
             log.info("Delete tunnel");
             deleteConfig(OvsdbConstant.PORT, "_uuid", portUUID,
-                      OvsdbConstant.BRIDGE, "ports");
+                         OvsdbConstant.BRIDGE, "ports");
         }
 
         return;
@@ -628,16 +671,15 @@ public class DefaultOvsdbClient
     /**
      * Delete transact config.
      *
-     * @param childTableName child table name
-     * @param childColumnName child column name
-     * @param childUuid child row uuid
-     * @param parentTableName parent table name
+     * @param childTableName   child table name
+     * @param childColumnName  child column name
+     * @param childUuid        child row uuid
+     * @param parentTableName  parent table name
      * @param parentColumnName parent column
-     *
      */
     private void deleteConfig(String childTableName, String childColumnName,
-                           String childUuid, String parentTableName,
-                           String parentColumnName) {
+                              String childUuid, String parentTableName,
+                              String parentColumnName) {
         DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME);
         TableSchema childTableSchema = dbSchema.getTableSchema(childTableName);
 
@@ -672,14 +714,13 @@ public class DefaultOvsdbClient
     /**
      * Update transact config.
      *
-     * @param tableName table name
+     * @param tableName  table name
      * @param columnName column name
-     * @param uuid uuid
-     * @param row the config data
-     *
+     * @param uuid       uuid
+     * @param row        the config data
      */
     private void updateConfig(String tableName, String columnName, String uuid,
-                           Row row) {
+                              Row row) {
         DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME);
         TableSchema tableSchema = dbSchema.getTableSchema(tableName);
 
@@ -698,18 +739,17 @@ public class DefaultOvsdbClient
     /**
      * Insert transact config.
      *
-     * @param childTableName child table name
-     * @param childColumnName child column name
-     * @param parentTableName parent table name
+     * @param childTableName   child table name
+     * @param childColumnName  child column name
+     * @param parentTableName  parent table name
      * @param parentColumnName parent column
-     * @param parentUuid parent uuid
-     * @param row the config data
-     *
+     * @param parentUuid       parent uuid
+     * @param row              the config data
      * @return uuid, empty if no uuid is find
      */
     private String insertConfig(String childTableName, String childColumnName,
-                             String parentTableName, String parentColumnName,
-                             String parentUuid, Row row) {
+                                String parentTableName, String parentColumnName,
+                                String parentUuid, Row row) {
         DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME);
         TableSchema tableSchema = dbSchema.getTableSchema(childTableName);
 
@@ -741,7 +781,7 @@ public class DefaultOvsdbClient
         if (childTableName.equalsIgnoreCase(OvsdbConstant.PORT)) {
             log.info("Handle port insert");
             Insert intfInsert = handlePortInsertTable(OvsdbConstant.INTERFACE,
-                                                    row);
+                                                      row);
 
             if (intfInsert != null) {
                 operations.add(intfInsert);
@@ -772,8 +812,7 @@ public class DefaultOvsdbClient
      * Handles port insert.
      *
      * @param tableName ovsdb table interface
-     * @param portRow row of port
-     *
+     * @param portRow   row of port
      * @return insert, empty if null
      */
     private Insert handlePortInsertTable(String tableName, Row portRow) {
@@ -801,8 +840,7 @@ public class DefaultOvsdbClient
      * Gets tunnel name.
      *
      * @param tunnelType
-     * @param dstIp the remote ip address
-     *
+     * @param dstIp      the remote ip address
      * @return tunnel name
      */
     private String getTunnelName(String tunnelType, IpAddress dstIp) {
@@ -821,7 +859,7 @@ public class DefaultOvsdbClient
             Function<JsonNode, DatabaseSchema> rowFunction = new Function<JsonNode, DatabaseSchema>() {
                 @Override
                 public DatabaseSchema apply(JsonNode input) {
-                    log.info("Get ovsdb database schema", dbName);
+                    log.info("Get ovsdb database schema {}", dbName);
                     DatabaseSchema dbSchema = FromJsonUtil
                             .jsonNodeToDbSchema(dbName, input);
                     if (dbSchema == null) {
@@ -877,21 +915,17 @@ public class DefaultOvsdbClient
         }
         DatabaseSchema dbSchema = schema.get(dbName);
         if (dbSchema != null) {
-            Function<List<JsonNode>, List<OperationResult>> rowFunction =
-                    new Function<List<JsonNode>, List<OperationResult>>() {
-                @Override
-                public List<OperationResult> apply(List<JsonNode> input) {
-                    log.info("Get ovsdb operation result");
-                    List<OperationResult> result = FromJsonUtil
-                            .jsonNodeToOperationResult(input, operations);
-
-                    if (result == null) {
-                        log.debug("The operation result is null");
-                        return null;
-                    }
-                    return result;
+            Function<List<JsonNode>, List<OperationResult>> rowFunction = (input -> {
+                log.info("Get ovsdb operation result");
+                List<OperationResult> result = FromJsonUtil
+                        .jsonNodeToOperationResult(input, operations);
+
+                if (result == null) {
+                    log.debug("The operation result is null");
+                    return null;
                 }
-            };
+                return result;
+            });
             return Futures.transform(transact(dbSchema, operations),
                                      rowFunction);
         }
@@ -972,7 +1006,7 @@ public class DefaultOvsdbClient
 
     }
 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @SuppressWarnings({"rawtypes", "unchecked"})
     @Override
     public void processResult(JsonNode response) {
         log.debug("Handle result");
@@ -1041,6 +1075,105 @@ public class DefaultOvsdbClient
         return ovsdbBridges;
     }
 
+    @Override
+    public Set<ControllerInfo> getControllers(DeviceId openflowDeviceId) {
+        UUID bridgeUuid = getBridgeUUID(openflowDeviceId);
+        if (bridgeUuid == null) {
+            log.warn("bad bridge Uuid");
+            return null;
+        }
+        List<Controller> controllers = getControllers(bridgeUuid);
+        if (controllers == null) {
+            log.warn("bad list of controllers");
+            return null;
+        }
+        return controllers.stream().
+                map(controller -> new ControllerInfo(
+                        (String) controller.getTargetColumn()
+                                .data())).collect(Collectors.toSet());
+    }
+
+    private List<Controller> getControllers(UUID bridgeUuid) {
+        DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME);
+        if (dbSchema == null) {
+            return null;
+        }
+        OvsdbRowStore rowStore = getRowStore(OvsdbConstant.DATABASENAME,
+                                             OvsdbConstant.BRIDGE);
+        if (rowStore == null) {
+            log.debug("There is no bridge table");
+            return null;
+        }
+
+        Row bridgeRow = rowStore.getRow(bridgeUuid.value());
+        Bridge bridge = (Bridge) TableGenerator.
+                getTable(dbSchema, bridgeRow, OvsdbTable.BRIDGE);
+
+        //FIXME remove log
+        log.warn("type of controller column", bridge.getControllerColumn()
+                .data().getClass());
+        Set<UUID> controllerUuids = (Set<UUID>) ((OvsdbSet) bridge
+                .getControllerColumn().data()).set();
+//        Set<String> controllerUuidStrings = (Set<String>) bridge.getControllerColumn().data();
+
+        OvsdbRowStore controllerRowStore = getRowStore(OvsdbConstant.DATABASENAME,
+                                                       OvsdbConstant.CONTROLLER);
+        if (controllerRowStore == null) {
+            log.debug("There is no controller table");
+            return null;
+        }
+
+        List<Controller> ovsdbControllers = new ArrayList<>();
+        ConcurrentMap<String, Row> controllerTableRows = controllerRowStore.getRowStore();
+        controllerTableRows.forEach((key, row) -> {
+            if (!controllerUuids.contains(UUID.uuid(key))) {
+                return;
+            }
+            Controller controller = (Controller) TableGenerator
+                    .getTable(dbSchema, row, OvsdbTable.CONTROLLER);
+            ovsdbControllers.add(controller);
+        });
+        return ovsdbControllers;
+    }
+
+
+    private UUID getBridgeUUID(DeviceId openflowDeviceId) {
+        DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME);
+        if (dbSchema == null) {
+            return null;
+        }
+        OvsdbRowStore rowStore = getRowStore(OvsdbConstant.DATABASENAME,
+                                             OvsdbConstant.BRIDGE);
+        if (rowStore == null) {
+            log.debug("There is no bridge table");
+            return null;
+        }
+
+        ConcurrentMap<String, Row> bridgeTableRows = rowStore.getRowStore();
+        final AtomicReference<UUID> uuid = new AtomicReference<>();
+        for (Map.Entry<String, Row> entry : bridgeTableRows.entrySet()) {
+            Bridge b = (Bridge) TableGenerator.getTable(dbSchema,
+                                                        entry.getValue(),
+                                                        OvsdbTable.BRIDGE);
+            if (matchesDpid(b, openflowDeviceId)) {
+                uuid.set(UUID.uuid(entry.getKey()));
+                break;
+            }
+        }
+        if (uuid.get() == null) {
+            log.debug("There is no bridge for {}", openflowDeviceId);
+        }
+        return uuid.get();
+
+    }
+
+    private static boolean matchesDpid(Bridge b, DeviceId deviceId) {
+        String ofDpid = deviceId.toString().replace("of:", "");
+        Set ofDeviceIds = ((OvsdbSet) b.getDatapathIdColumn().data()).set();
+        //TODO Set<String>
+        return ofDeviceIds.contains(ofDpid);
+    }
+
     @Override
     public Set<OvsdbPort> getPorts() {
         Set<OvsdbPort> ovsdbPorts = new HashSet<OvsdbPort>();
@@ -1185,4 +1318,10 @@ public class DefaultOvsdbClient
         }
         return ifaceid;
     }
+
+    @Override
+    public void disconnect() {
+        channel.disconnect();
+        this.agent.removeConnectedNode(nodeId);
+    }
 }
diff --git a/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbClientServiceAdapter.java b/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbClientServiceAdapter.java
new file mode 100644 (file)
index 0000000..71fee4f
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdb.controller.driver;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.ovsdb.controller.OvsdbBridge;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.onosproject.ovsdb.controller.OvsdbPort;
+import org.onosproject.ovsdb.controller.OvsdbTunnel;
+import org.onosproject.ovsdb.rfc.message.OperationResult;
+import org.onosproject.ovsdb.rfc.message.TableUpdates;
+import org.onosproject.ovsdb.rfc.notation.Row;
+import org.onosproject.ovsdb.rfc.notation.UUID;
+import org.onosproject.ovsdb.rfc.operations.Operation;
+import org.onosproject.ovsdb.rfc.schema.DatabaseSchema;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test Adapter for OvsdbClientService.
+ */
+public class OvsdbClientServiceAdapter implements OvsdbClientService {
+
+    @Override
+    public OvsdbNodeId nodeId() {
+        return null;
+    }
+
+    @Override
+    public void createTunnel(IpAddress srcIp, IpAddress dstIp) {
+
+    }
+
+    @Override
+    public void dropTunnel(IpAddress srcIp, IpAddress dstIp) {
+
+    }
+
+    @Override
+    public Set<OvsdbTunnel> getTunnels() {
+        return null;
+    }
+
+    @Override
+    public void createBridge(String bridgeName) {
+
+    }
+
+    @Override
+    public void dropBridge(String bridgeName) {
+
+    }
+
+    @Override
+    public Set<OvsdbBridge> getBridges() {
+        return null;
+    }
+
+    @Override
+    public Set<ControllerInfo> getControllers(DeviceId openflowDeviceId) {
+        return null;
+    }
+
+    @Override
+    public void setControllersWithUUID(UUID bridgeUuid, List<ControllerInfo> controllers) {
+
+    }
+
+    @Override
+    public void setControllersWithDeviceId(DeviceId deviceId, List<ControllerInfo> controllers) {
+
+    }
+
+    @Override
+    public void createPort(String bridgeName, String portName) {
+
+    }
+
+    @Override
+    public void dropPort(String bridgeName, String portName) {
+
+    }
+
+    @Override
+    public Set<OvsdbPort> getPorts() {
+        return null;
+    }
+
+    @Override
+    public boolean isConnected() {
+        return false;
+    }
+
+    @Override
+    public String getBridgeUuid(String bridgeName) {
+        return null;
+    }
+
+    @Override
+    public String getPortUuid(String portName, String bridgeUuid) {
+        return null;
+    }
+
+    @Override
+    public String getInterfaceUuid(String portUuid, String portName) {
+        return null;
+    }
+
+    @Override
+    public String getControllerUuid(String controllerName, String controllerTarget) {
+        return null;
+    }
+
+    @Override
+    public String getOvsUuid(String dbName) {
+        return null;
+    }
+
+    @Override
+    public ListenableFuture<DatabaseSchema> getOvsdbSchema(String dbName) {
+        return null;
+    }
+
+    @Override
+    public ListenableFuture<TableUpdates> monitorTables(String dbName, String id) {
+        return null;
+    }
+
+    @Override
+    public ListenableFuture<List<OperationResult>> transactConfig(String dbName, List<Operation> operations) {
+        return null;
+    }
+
+    @Override
+    public DatabaseSchema getDatabaseSchema(String dbName) {
+        return null;
+    }
+
+    @Override
+    public Row getRow(String dbName, String tableName, String uuid) {
+        return null;
+    }
+
+    @Override
+    public void removeRow(String dbName, String tableName, String uuid) {
+
+    }
+
+    @Override
+    public void updateOvsdbStore(String dbName, String tableName, String uuid, Row row) {
+
+    }
+
+    @Override
+    public Set<OvsdbPort> getLocalPorts(Iterable<String> ifaceids) {
+        return null;
+    }
+
+    @Override
+    public void disconnect() {
+
+    }
+
+    @Override
+    public ListenableFuture<JsonNode> getSchema(List<String> dbnames) {
+        return null;
+    }
+
+    @Override
+    public ListenableFuture<List<String>> echo() {
+        return null;
+    }
+
+    @Override
+    public ListenableFuture<JsonNode> monitor(DatabaseSchema dbSchema, String monitorId) {
+        return null;
+    }
+
+    @Override
+    public ListenableFuture<List<String>> listDbs() {
+        return null;
+    }
+
+    @Override
+    public ListenableFuture<List<JsonNode>> transact(DatabaseSchema dbSchema, List<Operation> operations) {
+        return null;
+    }
+}
diff --git a/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbControllerAdapter.java b/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbControllerAdapter.java
new file mode 100644 (file)
index 0000000..902113a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ovsdb.controller.driver;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbEventListener;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.onosproject.ovsdb.controller.OvsdbNodeListener;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Test Adapter for OvsdbController.
+ */
+public class OvsdbControllerAdapter implements OvsdbController {
+    protected ConcurrentHashMap<OvsdbNodeId, OvsdbClientServiceAdapter> ovsdbClients =
+            new ConcurrentHashMap<OvsdbNodeId, OvsdbClientServiceAdapter>();
+
+    @Override
+    public void addNodeListener(OvsdbNodeListener listener) {
+
+    }
+
+    @Override
+    public void removeNodeListener(OvsdbNodeListener listener) {
+
+    }
+
+    @Override
+    public void addOvsdbEventListener(OvsdbEventListener listener) {
+
+    }
+
+    @Override
+    public void removeOvsdbEventListener(OvsdbEventListener listener) {
+
+    }
+
+    @Override
+    public List<OvsdbNodeId> getNodeIds() {
+        long port = 6653;
+        return new ArrayList<OvsdbNodeId>(Arrays.asList(
+                new OvsdbNodeId(IpAddress.valueOf("127.0.0.1"), port)));
+    }
+
+    @Override
+    public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) {
+        return ovsdbClients.get(nodeId);
+    }
+
+    @Override
+    public void connect(IpAddress ip, TpPort port) {
+
+    }
+}
index 0758232..2e84a16 100644 (file)
  */
 package org.onosproject.ovsdb.controller.impl;
 
+import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.buffer.PooledByteBufAllocator;
 import io.netty.channel.Channel;
+import io.netty.channel.ChannelDuplexHandler;
 import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.EventLoop;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.ServerChannel;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
 import io.netty.handler.codec.string.StringEncoder;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.handler.timeout.IdleStateHandler;
 import io.netty.util.CharsetUtil;
 
 import java.net.InetSocketAddress;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
 import org.onosproject.ovsdb.controller.OvsdbConstant;
 import org.onosproject.ovsdb.controller.OvsdbNodeId;
 import org.onosproject.ovsdb.controller.driver.DefaultOvsdbClient;
@@ -63,6 +76,9 @@ public class Controller {
     private EventLoopGroup workerGroup;
     private Class<? extends ServerChannel> serverChannelClass;
 
+    private static final int MAX_RETRY = 5;
+    private static final int IDLE_TIMEOUT_SEC = 10;
+
     /**
      * Initialization.
      */
@@ -198,4 +214,86 @@ public class Controller {
         workerGroup.shutdownGracefully();
         bossGroup.shutdownGracefully();
     }
+
+    /**
+     * Connect to the ovsdb server with given ip address and port number.
+     *
+     * @param ip ip address
+     * @param port port number
+     */
+    public void connect(IpAddress ip, TpPort port) {
+        ChannelFutureListener listener = new ConnectionListener(this, ip, port);
+        connectRetry(ip, port, listener);
+    }
+
+    private void connectRetry(IpAddress ip, TpPort port, ChannelFutureListener listener) {
+        try {
+            Bootstrap b = new Bootstrap();
+            b.group(workerGroup)
+                    .channel(NioSocketChannel.class)
+                    .option(ChannelOption.TCP_NODELAY, true)
+                    .handler(new ChannelInitializer<SocketChannel>() {
+
+                        @Override
+                        protected void initChannel(SocketChannel channel) throws Exception {
+                            ChannelPipeline p = channel.pipeline();
+                            p.addLast(new MessageDecoder(),
+                                      new StringEncoder(CharsetUtil.UTF_8),
+                                      new IdleStateHandler(IDLE_TIMEOUT_SEC, 0, 0),
+                                      new ConnectionHandler());
+                        }
+                    });
+            b.remoteAddress(ip.toString(), port.toInt());
+            b.connect().addListener(listener);
+        } catch (Exception e) {
+            log.warn("Connection to the ovsdb server {}:{} failed", ip.toString(), port.toString());
+        }
+    }
+
+    private class ConnectionListener implements ChannelFutureListener {
+        private Controller controller;
+        private IpAddress ip;
+        private TpPort port;
+        private AtomicInteger count = new AtomicInteger();
+
+        public ConnectionListener(Controller controller,
+                                  IpAddress ip,
+                                  TpPort port) {
+            this.controller = controller;
+            this.ip = ip;
+            this.port = port;
+        }
+
+        @Override
+        public void operationComplete(ChannelFuture channelFuture) throws Exception {
+            if (!channelFuture.isSuccess()) {
+                channelFuture.channel().close();
+
+                if (count.incrementAndGet() < MAX_RETRY) {
+                    final EventLoop loop = channelFuture.channel().eventLoop();
+
+                    loop.schedule(() -> {
+                        controller.connectRetry(this.ip, this.port, this);
+                    }, 1L, TimeUnit.SECONDS);
+                } else {
+                    log.info("Connection to the ovsdb {}:{} failed",
+                             this.ip.toString(), this.port.toString());
+                }
+            } else {
+                handleNewNodeConnection(channelFuture.channel());
+            }
+        }
+    }
+
+    private class ConnectionHandler extends ChannelDuplexHandler {
+
+        @Override
+        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+            IdleStateEvent e = (IdleStateEvent) evt;
+
+            if (e.state() == IdleState.READER_IDLE) {
+                ctx.close();
+            }
+        }
+    }
 }
index 9b48296..c2cbbf8 100644 (file)
  */
 package org.onosproject.ovsdb.controller.impl;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.math.BigInteger;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.ExecutionException;
-
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableList;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
 import org.onosproject.ovsdb.controller.DefaultEventSubject;
 import org.onosproject.ovsdb.controller.EventSubject;
 import org.onosproject.ovsdb.controller.OvsdbClientService;
@@ -67,7 +58,17 @@ import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.fasterxml.jackson.databind.JsonNode;
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutionException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * The implementation of OvsdbController.
@@ -133,8 +134,7 @@ public class OvsdbControllerImpl implements OvsdbController {
 
     @Override
     public List<OvsdbNodeId> getNodeIds() {
-        // TODO Auto-generated method stub
-        return null;
+        return ImmutableList.copyOf(ovsdbClients.keySet());
     }
 
     @Override
@@ -142,6 +142,11 @@ public class OvsdbControllerImpl implements OvsdbController {
         return ovsdbClients.get(nodeId);
     }
 
+    @Override
+    public void connect(IpAddress ip, TpPort port) {
+        controller.connect(ip, port);
+    }
+
     /**
      * Implementation of an Ovsdb Agent which is responsible for keeping track
      * of connected node and the state in which they are.
@@ -204,8 +209,8 @@ public class OvsdbControllerImpl implements OvsdbController {
      * Processes table updates.
      *
      * @param clientService OvsdbClientService instance
-     * @param updates TableUpdates instance
-     * @param dbName ovsdb database name
+     * @param updates       TableUpdates instance
+     * @param dbName        ovsdb database name
      */
     private void processTableUpdates(OvsdbClientService clientService,
                                      TableUpdates updates, String dbName)
@@ -236,8 +241,8 @@ public class OvsdbControllerImpl implements OvsdbController {
                         Row row = clientService.getRow(OvsdbConstant.DATABASENAME, tableName, uuid.value());
                         dispatchInterfaceEvent(clientService,
                                                row,
-                                          OvsdbEvent.Type.PORT_REMOVED,
-                                          dbSchema);
+                                               OvsdbEvent.Type.PORT_REMOVED,
+                                               dbSchema);
                     }
                     clientService.removeRow(dbName, tableName, uuid.value());
                 }
@@ -249,10 +254,10 @@ public class OvsdbControllerImpl implements OvsdbController {
      * Dispatches event to the north.
      *
      * @param clientService OvsdbClientService instance
-     * @param newRow a new row
-     * @param oldRow an old row
-     * @param eventType type of event
-     * @param dbSchema ovsdb database schema
+     * @param newRow        a new row
+     * @param oldRow        an old row
+     * @param eventType     type of event
+     * @param dbSchema      ovsdb database schema
      */
     private void dispatchInterfaceEvent(OvsdbClientService clientService,
                                         Row row,
@@ -277,13 +282,13 @@ public class OvsdbControllerImpl implements OvsdbController {
         }
 
         EventSubject eventSubject = new DefaultEventSubject(MacAddress.valueOf(
-                                                                               macAndIfaceId[0]),
+                macAndIfaceId[0]),
                                                             new HashSet<IpAddress>(),
                                                             new OvsdbPortName(intf
-                                                                    .getName()),
+                                                                                      .getName()),
                                                             new OvsdbPortNumber(localPort),
                                                             new OvsdbDatapathId(Long
-                                                                    .toString(dpid)),
+                                                                                        .toString(dpid)),
                                                             new OvsdbPortType(portType),
                                                             new OvsdbIfaceId(macAndIfaceId[1]));
         for (OvsdbEventListener listener : ovsdbEventListener) {
@@ -309,7 +314,7 @@ public class OvsdbControllerImpl implements OvsdbController {
 
         String attachedMac = externalIds.get(OvsdbConstant.EXTERNAL_ID_VM_MAC);
         if (attachedMac == null) {
-            log.warn("The attachedMac is null");
+            log.debug("The attachedMac is null"); //FIXME why always null?
             return null;
         }
         String ifaceid = externalIds
@@ -318,7 +323,7 @@ public class OvsdbControllerImpl implements OvsdbController {
             log.warn("The ifaceid is null");
             return null;
         }
-        return new String[] {attachedMac, ifaceid};
+        return new String[]{attachedMac, ifaceid};
     }
 
     /**
@@ -343,7 +348,7 @@ public class OvsdbControllerImpl implements OvsdbController {
      * Gets datapathid from table bridge.
      *
      * @param clientService OvsdbClientService instance
-     * @param dbSchema ovsdb database schema
+     * @param dbSchema      ovsdb database schema
      * @return datapathid the bridge datapathid
      */
     private long getDataPathid(OvsdbClientService clientService,
index 37942c2..1956a1e 100644 (file)
@@ -89,7 +89,7 @@ public final class OvsdbJsonRpcHandler extends ChannelInboundHandlerAdapter {
      */
     private void processOvsdbMessage(JsonNode jsonNode) {
 
-        log.info("Handle ovsdb message");
+        log.debug("Handle ovsdb message");
 
         if (jsonNode.has("result")) {
 
index 3326922..0060960 100644 (file)
  */
 package org.onosproject.ovsdb.rfc.notation;
 
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.Maps;
 
 import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
 
-import com.google.common.collect.Maps;
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Row is the basic element of the OpenVswitch's table.
  */
 public final class Row {
     private String tableName;
+    private UUID uuid;
     private Map<String, Column> columns;
 
     /**
@@ -40,9 +41,11 @@ public final class Row {
 
     /**
      * Row constructor.
+     *
      * @param tableName table name
      */
-    public Row(String tableName) {
+    @Deprecated
+    private Row(String tableName) {
         checkNotNull(tableName, "tableName cannot be null");
         this.tableName = tableName;
         this.columns = Maps.newHashMap();
@@ -50,18 +53,22 @@ public final class Row {
 
     /**
      * Row constructor.
+     *
      * @param tableName table name
-     * @param columns Map of Column entity
+     * @param columns   Map of Column entity
      */
-    public Row(String tableName, Map<String, Column> columns) {
+    public Row(String tableName, UUID uuid, Map<String, Column> columns) {
         checkNotNull(tableName, "table name cannot be null");
+        checkNotNull(uuid, "uuid cannot be null");
         checkNotNull(columns, "columns cannot be null");
         this.tableName = tableName;
+        this.uuid = uuid;
         this.columns = columns;
     }
 
     /**
      * Returns tableName.
+     *
      * @return tableName
      */
     public String tableName() {
@@ -70,14 +77,34 @@ public final class Row {
 
     /**
      * Set tableName value.
+     *
      * @param tableName table name
      */
     public void setTableName(String tableName) {
         this.tableName = tableName;
     }
 
+    /**
+     * Returns uuid.
+     *
+     * @return uuid
+     */
+    public UUID uuid() {
+        return uuid;
+    }
+
+    /**
+     * Sets uuid value.
+     *
+     * @param uuid new uuid
+     */
+    public void setUuid(UUID uuid) {
+        this.uuid = uuid;
+    }
+
     /**
      * Returns Column by ColumnSchema.
+     *
      * @param columnName column name
      * @return Column
      */
@@ -87,6 +114,7 @@ public final class Row {
 
     /**
      * Returns Collection of Column.
+     *
      * @return Collection of Column
      */
     public Collection<Column> getColumns() {
@@ -95,8 +123,9 @@ public final class Row {
 
     /**
      * add Column.
+     *
      * @param columnName column name
-     * @param data Column entity
+     * @param data       Column entity
      */
     public void addColumn(String columnName, Column data) {
         this.columns.put(columnName, data);
index bd589f0..0b5ffef 100644 (file)
  */
 package org.onosproject.ovsdb.rfc.table;
 
-import java.util.Map;
-import java.util.Set;
-
 import org.onosproject.ovsdb.rfc.notation.Column;
+import org.onosproject.ovsdb.rfc.notation.OvsdbSet;
 import org.onosproject.ovsdb.rfc.notation.Row;
 import org.onosproject.ovsdb.rfc.notation.UUID;
 import org.onosproject.ovsdb.rfc.schema.DatabaseSchema;
 import org.onosproject.ovsdb.rfc.tableservice.AbstractOvsdbTableService;
 import org.onosproject.ovsdb.rfc.tableservice.ColumnDescription;
 
+import java.util.Map;
+import java.util.Set;
+
 /**
  * This class provides operations of Bridge Table.
  */
@@ -351,7 +352,7 @@ public class Bridge extends AbstractOvsdbTableService {
      * of attributes.
      * @param controller the column data which column name is "controller"
      */
-    public void setController(Set<UUID> controller) {
+    public void setController(OvsdbSet controller) {
         ColumnDescription columndesc = new ColumnDescription(
                                                              BridgeColumn.CONTROLLER
                                                                      .columnName(),
index c1ae7c7..f5bd860 100644 (file)
@@ -37,6 +37,7 @@ public final class TableGenerator {
      * @param tableName table name
      * @return Object table entity
      */
+    //FIXME change the name, it creates a row object, such as a controller.
     public static Object createTable(DatabaseSchema dbSchema,
                                      OvsdbTable tableName) {
         Row row = new Row();
index 9744fb4..1dcf48f 100644 (file)
  */
 package org.onosproject.ovsdb.rfc.utils;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import org.onosproject.ovsdb.rfc.exception.AbnormalJsonNodeException;
 import org.onosproject.ovsdb.rfc.exception.UnsupportedException;
 import org.onosproject.ovsdb.rfc.jsonrpc.Callback;
@@ -41,11 +40,11 @@ import org.onosproject.ovsdb.rfc.schema.type.ColumnTypeFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
 /**
  * JsonNode utility class. convert JsonNode into Object.
@@ -247,7 +246,7 @@ public final class FromJsonUtil {
         validateJsonNode(rowsNode, "rows");
         ArrayList<Row> rows = Lists.newArrayList();
         for (JsonNode rowNode : rowsNode.get("rows")) {
-            rows.add(createRow(tableSchema, rowNode));
+            rows.add(createRow(tableSchema, null, rowNode)); //FIXME null will throw exception
         }
         return rows;
     }
@@ -285,8 +284,8 @@ public final class FromJsonUtil {
             UUID uuid = UUID.uuid(uuidStr);
             JsonNode newR = oldNewRow.getValue().get("new");
             JsonNode oldR = oldNewRow.getValue().get("old");
-            Row newRow = newR != null ? createRow(tableSchema, newR) : null;
-            Row oldRow = oldR != null ? createRow(tableSchema, oldR) : null;
+            Row newRow = newR != null ? createRow(tableSchema, uuid, newR) : null;
+            Row oldRow = oldR != null ? createRow(tableSchema, uuid, oldR) : null;
             RowUpdate rowUpdate = new RowUpdate(uuid, oldRow, newRow);
             rows.put(uuid, rowUpdate);
         }
@@ -299,7 +298,7 @@ public final class FromJsonUtil {
      * @param rowNode JsonNode
      * @return Row
      */
-    private static Row createRow(TableSchema tableSchema, JsonNode rowNode) {
+    private static Row createRow(TableSchema tableSchema, UUID uuid, JsonNode rowNode) {
         if (tableSchema == null) {
             return null;
         }
@@ -314,7 +313,7 @@ public final class FromJsonUtil {
                 columns.put(columnName, new Column(columnName, obj));
             }
         }
-        return new Row(tableSchema.name(), columns);
+        return new Row(tableSchema.name(), uuid, columns);
     }
 
 }
index 847211e..0ea5141 100644 (file)
@@ -110,8 +110,8 @@ public class PcepErrorVer1 implements PcepError {
     /**
      * Parse RP List from the channel buffer.
      *
-     * @throws PcepParseException if mandatory fields are missing
      * @param cb of type channel buffer
+     * @throws PcepParseException if mandatory fields are missing
      */
     public void parseRPList(ChannelBuffer cb) throws PcepParseException {
         byte yObjClass;
index 1bce8cd..93ef277 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-  ~ Copyright 2014 Open Networking Laboratory
+  ~ Copyright 2015 Open Networking Laboratory
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@
 
         <module>tools/package/archetypes</module>
         <module>tools/package/branding</module>
+        <module>bgp</module>
     </modules>
 
     <url>http://onosproject.org/</url>
@@ -77,6 +78,7 @@
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <onos-build-conf.version>1.0</onos-build-conf.version>
         <netty4.version>4.0.23.Final</netty4.version>
         <copycat.version>0.5.0.onos</copycat.version>
         <openflowj.version>0.9.0.onos</openflowj.version>
                 <classifier>tests</classifier>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-bgpio</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>commons-pool</groupId>
                 <artifactId>commons-pool</artifactId>
                 <version>1.6</version>
             </dependency>
+            <dependency>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-bgp-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-app-bgp-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>io.netty</groupId>
                 <artifactId>netty-common</artifactId>
                 <plugin>
                     <groupId>org.codehaus.mojo</groupId>
                     <artifactId>findbugs-maven-plugin</artifactId>
-                    <version>3.0.0</version>
+                    <version>3.0.1</version>
                     <dependencies>
                         <dependency>
                             <groupId>org.onosproject</groupId>
                             <artifactId>onos-build-conf</artifactId>
-                            <version>1.0</version>
+                            <version>${onos-build-conf.version}</version>
                         </dependency>
                     </dependencies>
                     <configuration>
                     <dependency>
                         <groupId>org.onosproject</groupId>
                         <artifactId>onos-build-conf</artifactId>
-                        <version>1.0</version>
+                        <version>${onos-build-conf.version}</version>
                     </dependency>
                     <!-- For Java 8 lambda support-->
                     <dependency>
index 9a82363..93f6bf8 100644 (file)
@@ -335,15 +335,15 @@ public class HostLocationProvider extends AbstractProvider implements HostProvid
                                                  arp.getSenderProtocolAddress());
                 updateLocationIP(hid, srcMac, vlan, hloc, ip);
 
-                // IPv4: update location only
+            // IPv4: update location only
             } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
                 updateLocation(hid, srcMac, vlan, hloc);
 
-                //
-                // NeighborAdvertisement and NeighborSolicitation: possible
-                // new hosts, update both location and IP.
-                //
-                // IPv6: update location only
+            //
+            // NeighborAdvertisement and NeighborSolicitation: possible
+            // new hosts, update both location and IP.
+            //
+            // IPv6: update location only
             } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
                 IPv6 ipv6 = (IPv6) eth.getPayload();
                 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6,
index d010f17..6cbb623 100644 (file)
@@ -23,9 +23,17 @@ import org.onlab.osgi.ComponentContextAdapter;
 import org.onlab.packet.ARP;
 import org.onlab.packet.ChassisId;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborAdvertisement;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onlab.packet.ndp.RouterAdvertisement;
+import org.onlab.packet.ndp.RouterSolicitation;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -66,6 +74,7 @@ import java.util.Hashtable;
 import java.util.Set;
 
 import static org.easymock.EasyMock.*;
+import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.*;
 import static org.onlab.packet.VlanId.vlanId;
 import static org.onosproject.net.Device.Type.SWITCH;
@@ -75,27 +84,42 @@ import static org.onosproject.net.PortNumber.portNumber;
 import static org.onosproject.net.device.DeviceEvent.Type.*;
 
 public class HostLocationProviderTest {
-
     private static final Integer INPORT = 10;
     private static final String DEV1 = "of:1";
     private static final String DEV2 = "of:2";
     private static final String DEV3 = "of:3";
+    private static final String DEV4 = "of:4";
+    private static final String DEV5 = "of:5";
+    private static final String DEV6 = "of:6";
 
     private static final VlanId VLAN = vlanId();
+
+    // IPv4 Host
     private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
     private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff");
     private static final byte[] IP = new byte[]{10, 0, 0, 1};
-
     private static final IpAddress IP_ADDRESS =
             IpAddress.valueOf(IpAddress.Version.INET, IP);
     private static final HostLocation LOCATION =
             new HostLocation(deviceId(DEV1), portNumber(INPORT), 0L);
-
     private static final DefaultHost HOST =
             new DefaultHost(ProviderId.NONE, hostId(MAC), MAC,
                             vlanId(VlanId.UNTAGGED), LOCATION,
                             ImmutableSet.of(IP_ADDRESS));
 
+    // IPv6 Host
+    private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02");
+    private static final MacAddress BCMAC2 = MacAddress.valueOf("33:33:00:00:00:01");
+    private static final byte[] IP2 = Ip6Address.valueOf("1000::1").toOctets();
+    private static final IpAddress IP_ADDRESS2 =
+            IpAddress.valueOf(IpAddress.Version.INET6, IP2);
+    private static final HostLocation LOCATION2 =
+            new HostLocation(deviceId(DEV4), portNumber(INPORT), 0L);
+    private static final DefaultHost HOST2 =
+            new DefaultHost(ProviderId.NONE, hostId(MAC2), MAC2,
+                            vlanId(VlanId.UNTAGGED), LOCATION2,
+                            ImmutableSet.of(IP_ADDRESS2));
+
     private static final ComponentContextAdapter CTX_FOR_REMOVE =
             new ComponentContextAdapter() {
                 @Override
@@ -157,51 +181,189 @@ public class HostLocationProviderTest {
     @Test
     public void events() {
         // new host
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        assertNotNull("new host expected", providerService.added);
+        assertNull("host motion unexpected", providerService.moved);
+
+        // the host moved to new switch
+        testProcessor.process(new TestArpPacketContext(DEV2));
+        assertNotNull("host motion expected", providerService.moved);
+
+        // the host was misheard on a spine
+        testProcessor.process(new TestArpPacketContext(DEV3));
+        assertNull("host misheard on spine switch", providerService.spine);
+
+        providerService.clear();
+
+        // new host
+        testProcessor.process(new TestNAPacketContext(DEV4));
         assertNotNull("new host expected", providerService.added);
         assertNull("host motion unexpected", providerService.moved);
 
         // the host moved to new switch
-        testProcessor.process(new TestPacketContext(DEV2));
+        testProcessor.process(new TestNAPacketContext(DEV5));
         assertNotNull("host motion expected", providerService.moved);
 
         // the host was misheard on a spine
-        testProcessor.process(new TestPacketContext(DEV3));
+        testProcessor.process(new TestNAPacketContext(DEV6));
         assertNull("host misheard on spine switch", providerService.spine);
     }
 
     @Test
     public void removeHostByDeviceRemove() {
         provider.modified(CTX_FOR_REMOVE);
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        testProcessor.process(new TestNAPacketContext(DEV4));
+
         Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
                                           "m", "h", "s", "n", new ChassisId(0L));
         deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device));
         assertEquals("incorrect remove count", 1, providerService.removeCount);
+
+        device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
+                                          "m", "h", "s", "n", new ChassisId(0L));
+        deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device));
+        assertEquals("incorrect remove count", 2, providerService.removeCount);
     }
 
     @Test
     public void removeHostByDeviceOffline() {
         provider.modified(CTX_FOR_REMOVE);
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV4));
+
         Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
                                           "m", "h", "s", "n", new ChassisId(0L));
         deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device));
         assertEquals("incorrect remove count", 1, providerService.removeCount);
+
+        device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
+                                          "m", "h", "s", "n", new ChassisId(0L));
+        deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device));
+        assertEquals("incorrect remove count", 2, providerService.removeCount);
     }
 
     @Test
     public void removeHostByDevicePortDown() {
         provider.modified(CTX_FOR_REMOVE);
-        testProcessor.process(new TestPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        testProcessor.process(new TestArpPacketContext(DEV4));
+
         Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
                                           "m", "h", "s", "n", new ChassisId(0L));
         deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device,
-                                                     new DefaultPort(device, portNumber(INPORT),
-                                                                     false)));
+                new DefaultPort(device, portNumber(INPORT), false)));
         assertEquals("incorrect remove count", 1, providerService.removeCount);
+
+        device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH,
+                                          "m", "h", "s", "n", new ChassisId(0L));
+        deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device,
+                new DefaultPort(device, portNumber(INPORT), false)));
+        assertEquals("incorrect remove count", 2, providerService.removeCount);
     }
 
+    /**
+     * When receiving ARP, updates location and IP.
+     */
+    @Test
+    public void testReceiveArp() {
+        testProcessor.process(new TestArpPacketContext(DEV1));
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION));
+        assertThat(descr.hwAddress(), is(MAC));
+        assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving IPv4, updates location only.
+     */
+    @Test
+    public void testReceiveIpv4() {
+        testProcessor.process(new TestIpv4PacketContext(DEV1));
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION));
+        assertThat(descr.hwAddress(), is(MAC));
+        assertThat(descr.ipAddress().size(), is(0));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving NeighborAdvertisement, updates location and IP.
+     */
+    @Test
+    public void testReceiveNA() {
+        testProcessor.process(new TestNAPacketContext(DEV4));
+        assertNotNull(providerService.added);
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION2));
+        assertThat(descr.hwAddress(), is(MAC2));
+        assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving NeighborSolicitation, updates location and IP.
+     */
+    @Test
+    public void testReceiveNS() {
+        testProcessor.process(new TestNSPacketContext(DEV4));
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION2));
+        assertThat(descr.hwAddress(), is(MAC2));
+        assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2));
+        assertThat(descr.vlan(), is(VLAN));
+    }
+
+    /**
+     * When receiving RouterAdvertisement, ignores it.
+     */
+    @Test
+    public void testReceivesRA() {
+        testProcessor.process(new TestRAPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving RouterSolicitation, ignores it.
+     */
+    @Test
+    public void testReceiveRS() {
+        testProcessor.process(new TestRSPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving Duplicate Address Detection (DAD), ignores it.
+     */
+    @Test
+    public void testReceiveDAD() {
+        testProcessor.process(new TestDADPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving IPv6 multicast packet, ignores it.
+     */
+    @Test
+    public void testReceiveIpv6Multicast() {
+        testProcessor.process(new TestIpv6McastPacketContext(DEV4));
+        assertNull(providerService.added);
+    }
+
+    /**
+     * When receiving IPv6 unicast packet, updates location only.
+     */
+    @Test
+    public void testReceiveIpv6Unicast() {
+        testProcessor.process(new TestIpv6PacketContext(DEV4));
+        assertNotNull(providerService.added);
+        HostDescription descr = providerService.added;
+        assertThat(descr.location(), is(LOCATION2));
+        assertThat(descr.hwAddress(), is(MAC2));
+        assertThat(descr.ipAddress().size(), is(0));
+        assertThat(descr.vlan(), is(VLAN));
+    }
 
     @After
     public void tearDown() {
@@ -233,24 +395,30 @@ public class HostLocationProviderTest {
             extends AbstractProviderService<HostProvider>
             implements HostProviderService {
 
-        DeviceId added = null;
-        DeviceId moved = null;
-        DeviceId spine = null;
+        HostDescription added = null;
+        HostDescription moved = null;
+        HostDescription spine = null;
         public int removeCount;
 
+        public void clear() {
+            added = null;
+            moved = null;
+            spine = null;
+            removeCount = 0;
+        }
+
         protected TestHostProviderService(HostProvider provider) {
             super(provider);
         }
 
         @Override
         public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
-            DeviceId descr = hostDescription.location().deviceId();
             if (added == null) {
-                added = descr;
-            } else if ((moved == null) && !descr.equals(added)) {
-                moved = descr;
+                added = hostDescription;
+            } else if ((moved == null) && !hostDescription.equals(added)) {
+                moved = hostDescription;
             } else {
-                spine = descr;
+                spine = hostDescription;
             }
         }
 
@@ -259,6 +427,10 @@ public class HostLocationProviderTest {
             removeCount++;
         }
 
+        @Override
+        public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
+        }
+
     }
 
     private class TestPacketService extends PacketServiceAdapter {
@@ -268,24 +440,26 @@ public class HostLocationProviderTest {
         }
     }
 
-
     private class TestTopologyService extends TopologyServiceAdapter {
         @Override
         public boolean isInfrastructure(Topology topology,
                                         ConnectPoint connectPoint) {
             //simulate DPID3 as an infrastructure switch
-            if ((connectPoint.deviceId()).equals(deviceId(DEV3))) {
+            if ((connectPoint.deviceId()).equals(deviceId(DEV3)) ||
+                    connectPoint.deviceId().equals(deviceId(DEV6))) {
                 return true;
             }
             return false;
         }
     }
 
-    private class TestPacketContext implements PacketContext {
-
+    /**
+     * Generates ARP packet.
+     */
+    private class TestArpPacketContext implements PacketContext {
         private final String deviceId;
 
-        public TestPacketContext(String deviceId) {
+        public TestArpPacketContext(String deviceId) {
             this.deviceId = deviceId;
         }
 
@@ -340,6 +514,490 @@ public class HostLocationProviderTest {
         }
     }
 
+    /**
+     * Generates IPv6 Unicast packet.
+     */
+    private class TestIpv4PacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestIpv4PacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            IPv4 ipv4 = new IPv4();
+            ipv4.setDestinationAddress("10.0.0.1");
+            ipv4.setSourceAddress(IP_ADDRESS.toString());
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV4)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC)
+                    .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01"))
+                    .setPayload(ipv4);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates NeighborAdvertisement packet.
+     */
+    private class TestNAPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestNAPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            NeighborAdvertisement na = new NeighborAdvertisement();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(na);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(BCMAC2)
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates NeighborSolicitation packet.
+     */
+    private class TestNSPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestNSPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            NeighborSolicitation ns = new NeighborSolicitation();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1:ff00:0000").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(BCMAC2)
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates Duplicate Address Detection packet.
+     */
+    private class TestDADPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestDADPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            NeighborSolicitation ns = new NeighborSolicitation();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets());
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(BCMAC2)
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates Router Solicitation packet.
+     */
+    private class TestRSPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestRSPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            RouterSolicitation ns = new RouterSolicitation();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::2").toOctets());
+            ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets());
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:02"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates Router Advertisement packet.
+     */
+    private class TestRAPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestRAPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            RouterAdvertisement ns = new RouterAdvertisement();
+            ICMP6 icmp6 = new ICMP6();
+            icmp6.setPayload(ns);
+            IPv6 ipv6 = new IPv6();
+            ipv6.setPayload(icmp6);
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates IPv6 Multicast packet.
+     */
+    private class TestIpv6McastPacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestIpv6McastPacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            IPv6 ipv6 = new IPv6();
+            ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2.toBytes())
+                    .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
+    /**
+     * Generates IPv6 Unicast packet.
+     */
+    private class TestIpv6PacketContext implements PacketContext {
+        private final String deviceId;
+
+        public TestIpv6PacketContext(String deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public long time() {
+            return 0;
+        }
+
+        @Override
+        public InboundPacket inPacket() {
+            IPv6 ipv6 = new IPv6();
+            ipv6.setDestinationAddress(Ip6Address.valueOf("1000::1").toOctets());
+            ipv6.setSourceAddress(IP2);
+            Ethernet eth = new Ethernet();
+            eth.setEtherType(Ethernet.TYPE_IPV6)
+                    .setVlanID(VLAN.toShort())
+                    .setSourceMACAddress(MAC2)
+                    .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01"))
+                    .setPayload(ipv6);
+            ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+                                                         portNumber(INPORT));
+            return new DefaultInboundPacket(receivedFrom, eth,
+                                            ByteBuffer.wrap(eth.serialize()));
+        }
+
+        @Override
+        public OutboundPacket outPacket() {
+            return null;
+        }
+
+        @Override
+        public TrafficTreatment.Builder treatmentBuilder() {
+            return null;
+        }
+
+        @Override
+        public void send() {
+
+        }
+
+        @Override
+        public boolean block() {
+            return false;
+        }
+
+        @Override
+        public boolean isHandled() {
+            return false;
+        }
+    }
+
     private class TestDeviceService extends DeviceServiceAdapter {
         private DeviceListener listener;
 
@@ -357,12 +1015,26 @@ public class HostLocationProviderTest {
     private class TestHostService extends HostServiceAdapter {
         @Override
         public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
-            return ImmutableSet.of(HOST);
+            ConnectPoint cp1 = new ConnectPoint(deviceId(DEV1), portNumber(INPORT));
+            ConnectPoint cp2 = new ConnectPoint(deviceId(DEV4), portNumber(INPORT));
+            if (connectPoint.equals(cp1)) {
+                return ImmutableSet.of(HOST);
+            } else if (connectPoint.equals(cp2)) {
+                return ImmutableSet.of(HOST2);
+            } else {
+                return ImmutableSet.of();
+            }
         }
 
         @Override
         public Set<Host> getConnectedHosts(DeviceId deviceId) {
-            return ImmutableSet.of(HOST);
+            if (deviceId.equals(deviceId(DEV1))) {
+                return ImmutableSet.of(HOST);
+            } else if (deviceId.equals(deviceId(DEV4))) {
+                return ImmutableSet.of(HOST2);
+            } else {
+                return ImmutableSet.of();
+            }
         }
 
     }
index 386d838..a840f85 100644 (file)
@@ -67,6 +67,8 @@ import java.util.concurrent.ScheduledExecutorService;
 import static com.google.common.base.Strings.isNullOrEmpty;
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onlab.packet.Ethernet.TYPE_BSN;
+import static org.onlab.packet.Ethernet.TYPE_LLDP;
 import static org.onlab.util.Tools.get;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.net.Link.Type.DIRECT;
@@ -326,10 +328,10 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
      */
     private void requestIntercepts() {
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-        selector.matchEthType(Ethernet.TYPE_LLDP);
+        selector.matchEthType(TYPE_LLDP);
         packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
 
-        selector.matchEthType(Ethernet.TYPE_BSN);
+        selector.matchEthType(TYPE_BSN);
         if (useBDDP) {
             packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
         } else {
@@ -342,9 +344,9 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
      */
     private void withdrawIntercepts() {
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-        selector.matchEthType(Ethernet.TYPE_LLDP);
+        selector.matchEthType(TYPE_LLDP);
         packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
-        selector.matchEthType(Ethernet.TYPE_BSN);
+        selector.matchEthType(TYPE_BSN);
         packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
     }
 
@@ -394,7 +396,7 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
                     synchronized (discoverers) {
                         ld = discoverers.get(deviceId);
                         if (ld == null) {
-                            if (rules.isSuppressed(device)) {
+                            if (rules != null && rules.isSuppressed(device)) {
                                 log.debug("LinkDiscovery from {} disabled by configuration", device.id());
                                 return;
                             }
@@ -474,9 +476,15 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
     private class InternalPacketProcessor implements PacketProcessor {
         @Override
         public void process(PacketContext context) {
-            if (context == null) {
+            if (context == null || context.isHandled()) {
                 return;
             }
+
+            Ethernet eth = context.inPacket().parsed();
+            if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
+                return;
+            }
+
             LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId());
             if (ld == null) {
                 return;
index cb19dc5..4fa961f 100644 (file)
  */
 package org.onosproject.provider.of.device.impl;
 
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.Port.Type.COPPER;
+import static org.onosproject.net.Port.Type.FIBER;
+import static org.onosproject.openflow.controller.Dpid.dpid;
+import static org.onosproject.openflow.controller.Dpid.uri;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -28,15 +42,17 @@ import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.ChassisId;
 import org.onlab.util.Frequency;
-import org.onosproject.cfg.ComponentConfigService;
 import org.onlab.util.Spectrum;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.GridType;
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduCltPort;
 import org.onosproject.net.OduSignalType;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
@@ -49,6 +65,7 @@ import org.onosproject.net.device.DeviceProvider;
 import org.onosproject.net.device.DeviceProviderRegistry;
 import org.onosproject.net.device.DeviceProviderService;
 import org.onosproject.net.device.OchPortDescription;
+import org.onosproject.net.device.OduCltPortDescription;
 import org.onosproject.net.device.OmsPortDescription;
 import org.onosproject.net.device.PortDescription;
 import org.onosproject.net.device.PortStatistics;
@@ -64,13 +81,19 @@ import org.onosproject.openflow.controller.PortDescPropertyType;
 import org.onosproject.openflow.controller.RoleState;
 import org.osgi.service.component.ComponentContext;
 import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFExpPort;
+import org.projectfloodlight.openflow.protocol.OFExpPortDescPropOpticalTransport;
+import org.projectfloodlight.openflow.protocol.OFExpPortOpticalTransportLayerEntry;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFObject;
 import org.projectfloodlight.openflow.protocol.OFPortConfig;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
 import org.projectfloodlight.openflow.protocol.OFPortFeatures;
 import org.projectfloodlight.openflow.protocol.OFPortOptical;
+import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportLayerClass;
+import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportSignalType;
 import org.projectfloodlight.openflow.protocol.OFPortReason;
 import org.projectfloodlight.openflow.protocol.OFPortState;
 import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
@@ -83,23 +106,10 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.PortSpeed;
 import org.slf4j.Logger;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static org.onlab.util.Tools.get;
-import static org.onosproject.net.DeviceId.deviceId;
-import static org.onosproject.net.Port.Type.COPPER;
-import static org.onosproject.net.Port.Type.FIBER;
-import static org.onosproject.openflow.controller.Dpid.dpid;
-import static org.onosproject.openflow.controller.Dpid.uri;
-import static org.slf4j.LoggerFactory.getLogger;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 
 /**
  * Provider which uses an OpenFlow controller to detect network
@@ -109,7 +119,11 @@ import static org.slf4j.LoggerFactory.getLogger;
 public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider {
 
     private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class);
+
     private static final long MBPS = 1_000 * 1_000;
+    private static final Frequency FREQ100 = Frequency.ofGHz(100);
+    private static final Frequency FREQ193_1 = Frequency.ofTHz(193.1);
+    private static final Frequency FREQ4_4 = Frequency.ofTHz(4.4);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceProviderRegistry providerRegistry;
@@ -145,27 +159,16 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
         providerService = providerRegistry.register(this);
         controller.addListener(listener);
         controller.addEventListener(listener);
-        for (OpenFlowSwitch sw : controller.getSwitches()) {
-            try {
-                listener.switchAdded(new Dpid(sw.getId()));
-            } catch (Exception e) {
-                LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage());
-                LOG.debug("Error details:", e);
-                // disconnect to trigger switch-add later
-                sw.disconnectSwitch();
-            }
-            PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency);
-            psc.start();
-            collectors.put(new Dpid(sw.getId()), psc);
-        }
+        connectInitialDevices();
         LOG.info("Started");
     }
 
     @Deactivate
     public void deactivate(ComponentContext context) {
         cfgService.unregisterProperties(getClass(), false);
-        providerRegistry.unregister(this);
         controller.removeListener(listener);
+        disconnectDevices();
+        providerRegistry.unregister(this);
         collectors.values().forEach(PortStatsCollector::stop);
         providerService = null;
         LOG.info("Stopped");
@@ -191,13 +194,31 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
         LOG.info("Settings: portStatsPollFrequency={}", portStatsPollFrequency);
     }
 
+    private void connectInitialDevices() {
+        for (OpenFlowSwitch sw : controller.getSwitches()) {
+            try {
+                listener.switchAdded(new Dpid(sw.getId()));
+            } catch (Exception e) {
+                LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage());
+                LOG.debug("Error details:", e);
+                // disconnect to trigger switch-add later
+                sw.disconnectSwitch();
+            }
+            PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency);
+            psc.start();
+            collectors.put(new Dpid(sw.getId()), psc);
+        }
+    }
+
+    private void disconnectDevices() {
+        // Only disconnect the devices for which we are currently master.
+        controller.getMasterSwitches().forEach(sw -> listener.switchRemoved(new Dpid(sw.getId())));
+    }
+
     @Override
     public boolean isReachable(DeviceId deviceId) {
         OpenFlowSwitch sw = controller.getSwitch(dpid(deviceId.uri()));
-        if (sw == null || !sw.isConnected()) {
-            return false;
-        }
-        return true;
+        return sw != null && sw.isConnected();
     }
 
     @Override
@@ -302,8 +323,9 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
             ChassisId cId = new ChassisId(dpid.value());
 
             SparseAnnotations annotations = DefaultAnnotations.builder()
-                    .set("protocol", sw.factory().getVersion().toString())
-                    .set("channelId", sw.channelId())
+                    .set(AnnotationKeys.PROTOCOL, sw.factory().getVersion().toString())
+                    .set(AnnotationKeys.CHANNEL_ID, sw.channelId())
+                    .set(AnnotationKeys.MANAGEMENT_ADDRESS, sw.channelId().split(":")[0])
                     .build();
 
             DeviceDescription description =
@@ -386,18 +408,27 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
          */
         private List<PortDescription> buildPortDescriptions(OpenFlowSwitch sw) {
             final List<PortDescription> portDescs = new ArrayList<>(sw.getPorts().size());
-            sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
+            if (!(Device.Type.ROADM.equals(sw.deviceType()))) {
+                  sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
+            }
 
             OpenFlowOpticalSwitch opsw;
             switch (sw.deviceType()) {
                 case ROADM:
                     opsw = (OpenFlowOpticalSwitch) sw;
+                    List<OFPortDesc> ports = opsw.getPorts();
+                    LOG.debug("SW ID {} , ETH- ODU CLT Ports {}", opsw.getId(), ports);
+                    // ODU client ports are reported as ETH
+                    ports.forEach(port -> portDescs.add(buildOduCltPortDescription(port)));
+
                     opsw.getPortTypes().forEach(type -> {
-                        opsw.getPortsOf(type).forEach(
-                                op -> {
-                                    portDescs.add(buildPortDescription(type, (OFPortOptical) op));
-                                }
-                        );
+                    List<? extends OFObject> portsOf = opsw.getPortsOf(type);
+                    LOG.debug("Ports Of{}", portsOf);
+                    portsOf.forEach(
+                        op -> {
+                            portDescs.add(buildPortDescription(type, (OFObject) op));
+                        }
+                     );
                     });
                     break;
                 case FIBER_SWITCH:
@@ -417,6 +448,105 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
             return portDescs;
         }
 
+        private PortDescription buildOduCltPortDescription(OFPortDesc port) {
+            PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+            boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) &&
+                              !port.getConfig().contains(OFPortConfig.PORT_DOWN);
+            Long portSpeed = portSpeed(port);
+            OduCltPort.SignalType sigType = null;
+
+            switch (portSpeed.toString()) {
+                case "1":
+                    sigType = OduCltPort.SignalType.CLT_1GBE;
+                    break;
+                case "10":
+                    sigType = OduCltPort.SignalType.CLT_10GBE;
+                    break;
+                case "40":
+                    sigType = OduCltPort.SignalType.CLT_40GBE;
+                    break;
+                case "100":
+                    sigType = OduCltPort.SignalType.CLT_100GBE;
+                    break;
+                default:
+                    throw new RuntimeException("Un recognize OduClt speed: " + portSpeed.toString());
+            }
+
+            SparseAnnotations annotations = buildOduCltAnnotation(port);
+            return new OduCltPortDescription(portNo, enabled, sigType, annotations);
+        }
+
+        private SparseAnnotations buildOduCltAnnotation(OFPortDesc port) {
+            SparseAnnotations annotations = null;
+            String portName = Strings.emptyToNull(port.getName());
+            if (portName != null) {
+                 annotations = DefaultAnnotations.builder()
+                        .set(AnnotationKeys.PORT_NAME, portName)
+                        .set(AnnotationKeys.STATIC_PORT, Boolean.TRUE.toString()).build();
+            }
+            return annotations;
+        }
+
+        private PortDescription buildPortDescription(PortDescPropertyType ptype, OFObject port) {
+            if (port instanceof  OFPortOptical) {
+               return buildPortDescription(ptype, (OFPortOptical) port);
+            }
+            return buildPortDescription(ptype, (OFExpPort) port);
+        }
+
+        /**
+         * Build a portDescription from a given a port description describing some
+         * Optical port.
+         *
+         * @param ptype description property type.
+         * @param port the port to build from.
+         * @return portDescription for the port.
+         */
+        private PortDescription buildPortDescription(PortDescPropertyType ptype, OFExpPort port) {
+            PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+            boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
+                    && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
+            SparseAnnotations annotations = makePortNameAnnotation(port.getName());
+
+            OFExpPortDescPropOpticalTransport firstProp = port.getProperties().get(0);
+            OFPortOpticalTransportSignalType sigType = firstProp.getPortSignalType();
+
+            DefaultPortDescription portDes = null;
+            switch (sigType) {
+            case OMSN:
+                portDes =  new OmsPortDescription(portNo, enabled, FREQ193_1, FREQ193_1.add(FREQ4_4),
+                       FREQ100, annotations);
+                break;
+            case OCH:
+                OFExpPortOpticalTransportLayerEntry entry = firstProp.getFeatures().get(0).getValue().get(0);
+                OFPortOpticalTransportLayerClass layerClass =  entry.getLayerClass();
+                if (!OFPortOpticalTransportLayerClass.ODU.equals(layerClass)) {
+                    LOG.error("Unsupported layer Class {} ", layerClass);
+                    return null;
+                }
+
+                // convert to ONOS OduSignalType
+                OduSignalType oduSignalType = OpenFlowDeviceValueMapper.
+                        lookupOduSignalType((byte) entry.getSignalType());
+                //OchSignal is needed for OchPortDescription constructor,
+                //yet not relevant for tunable OCH port, creating with default parameters
+                OchSignal signalId = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 1, 1);
+
+                portDes = new OchPortDescription(portNo, enabled,
+                        oduSignalType, true, signalId, annotations);
+
+                break;
+            case OTU2:
+            case OTU4:
+                  LOG.error("Signal tpye OTU2/4 not supported yet ", port.toString());
+                  break;
+            default:
+                break;
+            }
+
+            return portDes;
+        }
+
         /**
          * Creates an annotation for the port name if one is available.
          *
@@ -565,5 +695,4 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
             }
         }
     }
-
 }
diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java
new file mode 100644 (file)
index 0000000..7bdf06f
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.provider.of.device.impl;
+
+import org.onosproject.net.OduSignalType;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.EnumHashBiMap;
+
+/**
+ * Collection of helper methods to convert protocol agnostic models to values used in OpenFlow spec.
+ */
+final class OpenFlowDeviceValueMapper {
+
+    // prohibit instantiation
+    private OpenFlowDeviceValueMapper() {}
+
+    private static final BiMap<OduSignalType, Byte> ODU_SIGNAL_TYPES = EnumHashBiMap.create(OduSignalType.class);
+    static {
+        // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values
+        ODU_SIGNAL_TYPES.put(OduSignalType.ODU1, (byte) 1);         // OFPODUT_ODU1 of enum ofp_odu_signal_type
+        ODU_SIGNAL_TYPES.put(OduSignalType.ODU2, (byte) 2);         // OFPODUT_ODU2 of enum ofp_odu_signal_type
+        ODU_SIGNAL_TYPES.put(OduSignalType.ODU3, (byte) 3);         // OFPODUT_ODU3 of enum ofp_odu_signal_type
+        ODU_SIGNAL_TYPES.put(OduSignalType.ODU4, (byte) 4);         // OFPODUT_ODU4 of enum ofp_odu_signal_type
+        ODU_SIGNAL_TYPES.put(OduSignalType.ODU0, (byte) 10);        // OFPODUT_ODU0 of enum ofp_odu_signal_type
+        ODU_SIGNAL_TYPES.put(OduSignalType.ODU2e, (byte) 11);       // OFPODUT_ODU2E of enum ofp_odu_signal_type
+    }
+
+    /**
+     * Looks up the specified input value to the corresponding value with the specified map.
+     *
+     * @param map bidirectional mapping
+     * @param input input value
+     * @param cls class of output value
+     * @param <I> type of input value
+     * @param <O> type of output value
+     * @return the corresponding value stored in the specified map
+     */
+    private static <I, O> O lookup(BiMap<I, O> map, I input, Class<O> cls) {
+        if (!map.containsKey(input)) {
+            throw new RuntimeException(
+                    String.format("No mapping found for %s when converting to %s", input, cls.getName()));
+        }
+
+        return map.get(input);
+    }
+
+    /**
+     * Looks up the the corresponding {@link OduSignalType} instance
+     * from the specified byte value for ODU signal type defined in
+     * ONF "Optical Transport Protocol Extensions Version 1.0".
+     *
+     * @param signalType byte value as ODU (Optical channel Data Unit) signal type defined the spec
+     * @return the corresponding OchSignalType instance
+     */
+    static OduSignalType lookupOduSignalType(byte signalType) {
+        return lookup(ODU_SIGNAL_TYPES.inverse(), signalType, OduSignalType.class);
+    }
+
+}
index 7b4d792..d0838bb 100644 (file)
@@ -16,6 +16,7 @@
 package org.onosproject.provider.of.device.impl;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 
@@ -236,7 +237,7 @@ public class OpenFlowDeviceProviderTest {
 
         @Override
         public Iterable<OpenFlowSwitch> getMasterSwitches() {
-            return null;
+            return ImmutableSet.of();
         }
 
         @Override
index f238bdb..cf91860 100644 (file)
@@ -221,11 +221,6 @@ public class FlowEntryBuilder {
 
     private TrafficTreatment buildTreatment() {
         TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
-        // If this is a drop rule
-        if (instructions.size() == 0) {
-            builder.drop();
-            return builder.build();
-        }
         for (OFInstruction in : instructions) {
             switch (in.getType()) {
                 case GOTO_TABLE:
index c9de450..f77819d 100644 (file)
@@ -142,7 +142,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder {
         for (Instruction i : treatment.immediate()) {
             switch (i.type()) {
             case DROP:
-                log.warn("Saw drop action; assigning drop action");
+            case NOACTION:
                 return Collections.emptyList();
             case L2MODIFICATION:
                 act = buildL2Modification(i);
index 8918d33..cc26575 100644 (file)
@@ -123,6 +123,9 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
         if (treatment.writeMetadata() != null) {
             instructions.add(buildMetadata(treatment.writeMetadata()));
         }
+        if (treatment.metered() != null) {
+            instructions.add(buildMeter(treatment.metered()));
+        }
 
         long cookie = flowRule().id().value();
 
@@ -212,6 +215,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
         for (Instruction i : treatments) {
             switch (i.type()) {
                 case DROP:
+                case NOACTION:
                     return Collections.emptyList();
                 case L0MODIFICATION:
                     actions.add(buildL0Modification(i));
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
new file mode 100644 (file)
index 0000000..a81367c
--- /dev/null
@@ -0,0 +1,881 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.onosproject.provider.of.flow.impl;\r
+\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+import java.util.Set;\r
+import java.util.concurrent.TimeUnit;\r
+import java.util.concurrent.ScheduledFuture;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.ScheduledExecutorService;\r
+\r
+import com.google.common.base.Objects;\r
+import com.google.common.collect.ImmutableSet;\r
+import com.google.common.collect.Maps;\r
+import com.google.common.collect.Sets;\r
+\r
+import org.onosproject.net.flow.DefaultTypedFlowEntry;\r
+import org.onosproject.net.flow.FlowEntry;\r
+import org.onosproject.net.flow.FlowId;\r
+import org.onosproject.net.flow.FlowRule;\r
+import org.onosproject.net.flow.StoredFlowEntry;\r
+import org.onosproject.net.flow.TypedStoredFlowEntry;\r
+import org.onosproject.net.flow.instructions.Instruction;\r
+import org.onosproject.net.flow.instructions.Instructions;\r
+import org.onosproject.openflow.controller.OpenFlowSwitch;\r
+import org.onosproject.openflow.controller.RoleState;\r
+import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;\r
+import org.projectfloodlight.openflow.protocol.match.Match;\r
+import org.projectfloodlight.openflow.types.OFPort;\r
+import org.projectfloodlight.openflow.types.TableId;\r
+import org.slf4j.Logger;\r
+\r
+import static com.google.common.base.Preconditions.checkNotNull;\r
+import static org.onlab.util.Tools.groupedThreads;\r
+import static org.onosproject.net.flow.TypedStoredFlowEntry.*;\r
+import static org.slf4j.LoggerFactory.getLogger;\r
+\r
+/**\r
+ * Efficiently and adaptively collects flow statistics for the specified switch.\r
+ */\r
+public class NewAdaptiveFlowStatsCollector {\r
+\r
+    private final Logger log = getLogger(getClass());\r
+\r
+    private final OpenFlowSwitch sw;\r
+\r
+    private ScheduledExecutorService adaptiveFlowStatsScheduler =\r
+            Executors.newScheduledThreadPool(4, groupedThreads("onos/flow", "device-stats-collector-%d"));\r
+    private ScheduledFuture<?> calAndShortFlowsThread;\r
+    private ScheduledFuture<?> midFlowsThread;\r
+    private ScheduledFuture<?> longFlowsThread;\r
+\r
+    // Task that calculates all flowEntries' FlowLiveType and collects stats IMMEDIATE flows every calAndPollInterval\r
+    private CalAndShortFlowsTask calAndShortFlowsTask;\r
+    // Task that collects stats MID flows every 2*calAndPollInterval\r
+    private MidFlowsTask midFlowsTask;\r
+    // Task that collects stats LONG flows every 3*calAndPollInterval\r
+    private LongFlowsTask longFlowsTask;\r
+\r
+    private static final int CAL_AND_POLL_TIMES = 1; // must be always 0\r
+    private static final int MID_POLL_TIMES = 2;     // variable greater or equal than 1\r
+    private static final int LONG_POLL_TIMES = 3;    // variable greater or equal than MID_POLL_TIMES\r
+    //TODO: make ENTIRE_POLL_TIMES configurable with enable or disable\r
+    // must be variable greater or equal than common multiple of MID_POLL_TIMES and LONG_POLL_TIMES\r
+    private static final int ENTIRE_POLL_TIMES = 6;\r
+\r
+    private static final int DEFAULT_CAL_AND_POLL_FREQUENCY = 5;\r
+    private static final int MIN_CAL_AND_POLL_FREQUENCY = 2;\r
+    private static final int MAX_CAL_AND_POLL_FREQUENCY = 60;\r
+\r
+    private int calAndPollInterval; // CAL_AND_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;\r
+    private int midPollInterval; // MID_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;\r
+    private int longPollInterval; // LONG_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;\r
+    // only used for checking condition at each task if it collects entire flows from a given switch or not\r
+    private int entirePollInterval; // ENTIRE_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;\r
+\r
+    // Number of call count of each Task,\r
+    // for undoing collection except only entire flows collecting task in CalAndShortFlowsTask\r
+    private int callCountCalAndShortFlowsTask = 0; // increased CAL_AND_POLL_TIMES whenever Task is called\r
+    private int callCountMidFlowsTask = 0;   // increased MID_POLL_TIMES whenever Task is called\r
+    private int callCountLongFlowsTask = 0;  // increased LONG_POLL_TIMES whenever Task is called\r
+\r
+    private InternalDeviceFlowTable deviceFlowTable = new InternalDeviceFlowTable();\r
+\r
+    private boolean isFirstTimeStart = true;\r
+\r
+    public static final long NO_FLOW_MISSING_XID = (-1);\r
+    private long flowMissingXid = NO_FLOW_MISSING_XID;\r
+\r
+    /**\r
+     * Creates a new adaptive collector for the given switch and default cal_and_poll frequency.\r
+     *\r
+     * @param sw           switch to pull\r
+     * @param pollInterval cal and immediate poll frequency in seconds\r
+     */\r
+    NewAdaptiveFlowStatsCollector(OpenFlowSwitch sw, int pollInterval) {\r
+        this.sw = sw;\r
+\r
+        initMemberVars(pollInterval);\r
+    }\r
+\r
+    // check calAndPollInterval validity and set all pollInterval values and finally initialize each task call count\r
+    private void initMemberVars(int pollInterval) {\r
+        if (pollInterval < MIN_CAL_AND_POLL_FREQUENCY) {\r
+            this.calAndPollInterval = MIN_CAL_AND_POLL_FREQUENCY;\r
+        } else if (pollInterval >= MAX_CAL_AND_POLL_FREQUENCY) {\r
+            this.calAndPollInterval = MAX_CAL_AND_POLL_FREQUENCY;\r
+        } else {\r
+            this.calAndPollInterval = pollInterval;\r
+        }\r
+\r
+        calAndPollInterval = CAL_AND_POLL_TIMES * calAndPollInterval;\r
+        midPollInterval = MID_POLL_TIMES * calAndPollInterval;\r
+        longPollInterval = LONG_POLL_TIMES * calAndPollInterval;\r
+        entirePollInterval = ENTIRE_POLL_TIMES * calAndPollInterval;\r
+\r
+        callCountCalAndShortFlowsTask = 0;\r
+        callCountMidFlowsTask = 0;\r
+        callCountLongFlowsTask = 0;\r
+\r
+        flowMissingXid = NO_FLOW_MISSING_XID;\r
+    }\r
+\r
+    /**\r
+     * Adjusts adaptive poll frequency.\r
+     *\r
+     * @param pollInterval poll frequency in seconds\r
+     */\r
+    synchronized void adjustCalAndPollInterval(int pollInterval) {\r
+        initMemberVars(pollInterval);\r
+\r
+        if (calAndShortFlowsThread != null) {\r
+            calAndShortFlowsThread.cancel(false);\r
+        }\r
+        if (midFlowsThread != null) {\r
+            midFlowsThread.cancel(false);\r
+        }\r
+        if (longFlowsThread != null) {\r
+            longFlowsThread.cancel(false);\r
+        }\r
+\r
+        calAndShortFlowsTask = new CalAndShortFlowsTask();\r
+        calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(\r
+                calAndShortFlowsTask,\r
+                0,\r
+                calAndPollInterval,\r
+                TimeUnit.SECONDS);\r
+\r
+        midFlowsTask = new MidFlowsTask();\r
+        midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(\r
+                midFlowsTask,\r
+                0,\r
+                midPollInterval,\r
+                TimeUnit.SECONDS);\r
+\r
+        longFlowsTask = new LongFlowsTask();\r
+        longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(\r
+                longFlowsTask,\r
+                0,\r
+                longPollInterval,\r
+                TimeUnit.SECONDS);\r
+\r
+        log.debug("calAndPollInterval=" + calAndPollInterval + "is adjusted");\r
+    }\r
+\r
+    private class CalAndShortFlowsTask implements Runnable {\r
+        @Override\r
+        public void run() {\r
+            if (sw.getRole() == RoleState.MASTER) {\r
+                log.trace("CalAndShortFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());\r
+\r
+                if (isFirstTimeStart) {\r
+                    // isFirstTimeStart, get entire flow stats from a given switch sw\r
+                    log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats at first time start for {}",\r
+                            sw.getStringId());\r
+                    ofFlowStatsRequestAllSend();\r
+\r
+                    callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;\r
+                    isFirstTimeStart = false;\r
+                } else  if (callCountCalAndShortFlowsTask == ENTIRE_POLL_TIMES) {\r
+                    // entire_poll_times, get entire flow stats from a given switch sw\r
+                    log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats for {}", sw.getStringId());\r
+                    ofFlowStatsRequestAllSend();\r
+\r
+                    callCountCalAndShortFlowsTask = CAL_AND_POLL_TIMES;\r
+                    //TODO: check flows deleted in switch, but exist in controller flow table, then remove them\r
+                    //\r
+                } else {\r
+                    calAndShortFlowsTaskInternal();\r
+                    callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    // send openflow flow stats request message with getting all flow entries to a given switch sw\r
+    private void ofFlowStatsRequestAllSend() {\r
+        OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()\r
+                .setMatch(sw.factory().matchWildcardAll())\r
+                .setTableId(TableId.ALL)\r
+                .setOutPort(OFPort.NO_MASK)\r
+                .build();\r
+\r
+        synchronized (this) {\r
+            // set the request xid to check the reply in OpenFlowRuleProvider\r
+            // After processing the reply of this request message,\r
+            // this must be set to NO_FLOW_MISSING_XID(-1) by provider\r
+            setFlowMissingXid(request.getXid());\r
+            log.debug("ofFlowStatsRequestAllSend,Request={},for {}", request.toString(), sw.getStringId());\r
+\r
+            sw.sendMsg(request);\r
+        }\r
+    }\r
+\r
+    // send openflow flow stats request message with getting the specific flow entry(fe) to a given switch sw\r
+    private void ofFlowStatsRequestFlowSend(FlowEntry fe) {\r
+        // set find match\r
+        Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty()).buildMatch();\r
+        // set find tableId\r
+        TableId tableId = TableId.of(fe.tableId());\r
+        // set output port\r
+        Instruction ins = fe.treatment().allInstructions().stream()\r
+                .filter(i -> (i.type() == Instruction.Type.OUTPUT))\r
+                .findFirst()\r
+                .orElse(null);\r
+        OFPort ofPort = OFPort.NO_MASK;\r
+        if (ins != null) {\r
+            Instructions.OutputInstruction out = (Instructions.OutputInstruction) ins;\r
+            ofPort = OFPort.of((int) ((out.port().toLong())));\r
+        }\r
+\r
+        OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()\r
+                .setMatch(match)\r
+                .setTableId(tableId)\r
+                .setOutPort(ofPort)\r
+                .build();\r
+\r
+        synchronized (this) {\r
+            if (getFlowMissingXid() != NO_FLOW_MISSING_XID) {\r
+                log.debug("ofFlowStatsRequestFlowSend: previous FlowStatsRequestAll does not be processed yet,"\r
+                                + " set no flow missing xid anyway, for {}",\r
+                        sw.getStringId());\r
+                setFlowMissingXid(NO_FLOW_MISSING_XID);\r
+            }\r
+\r
+            sw.sendMsg(request);\r
+        }\r
+    }\r
+\r
+    private void calAndShortFlowsTaskInternal() {\r
+        deviceFlowTable.checkAndMoveLiveFlowAll();\r
+\r
+        deviceFlowTable.getShortFlows().forEach(fe -> {\r
+            ofFlowStatsRequestFlowSend(fe);\r
+        });\r
+    }\r
+\r
+    private class MidFlowsTask implements Runnable {\r
+        @Override\r
+        public void run() {\r
+            if (sw.getRole() == RoleState.MASTER) {\r
+                log.trace("MidFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());\r
+\r
+                // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw\r
+                if (callCountMidFlowsTask == ENTIRE_POLL_TIMES) {\r
+                    callCountMidFlowsTask = MID_POLL_TIMES;\r
+                } else {\r
+                    midFlowsTaskInternal();\r
+                    callCountMidFlowsTask += MID_POLL_TIMES;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    private void midFlowsTaskInternal() {\r
+        deviceFlowTable.getMidFlows().forEach(fe -> {\r
+            ofFlowStatsRequestFlowSend(fe);\r
+        });\r
+    }\r
+\r
+    private class LongFlowsTask implements Runnable {\r
+        @Override\r
+        public void run() {\r
+            if (sw.getRole() == RoleState.MASTER) {\r
+                log.trace("LongFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());\r
+\r
+                // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw\r
+                if (callCountLongFlowsTask == ENTIRE_POLL_TIMES) {\r
+                    callCountLongFlowsTask = LONG_POLL_TIMES;\r
+                } else {\r
+                    longFlowsTaskInternal();\r
+                    callCountLongFlowsTask += LONG_POLL_TIMES;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    private void longFlowsTaskInternal() {\r
+        deviceFlowTable.getLongFlows().forEach(fe -> {\r
+            ofFlowStatsRequestFlowSend(fe);\r
+        });\r
+    }\r
+\r
+    /**\r
+     * start adaptive flow statistic collection.\r
+     *\r
+     */\r
+    public synchronized void start() {\r
+        log.debug("Starting AdaptiveStats collection thread for {}", sw.getStringId());\r
+        callCountCalAndShortFlowsTask = 0;\r
+        callCountMidFlowsTask = 0;\r
+        callCountLongFlowsTask = 0;\r
+\r
+        isFirstTimeStart = true;\r
+\r
+        // Initially start polling quickly. Then drop down to configured value\r
+        calAndShortFlowsTask = new CalAndShortFlowsTask();\r
+        calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(\r
+                calAndShortFlowsTask,\r
+                1,\r
+                calAndPollInterval,\r
+                TimeUnit.SECONDS);\r
+\r
+        midFlowsTask = new MidFlowsTask();\r
+        midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(\r
+                midFlowsTask,\r
+                1,\r
+                midPollInterval,\r
+                TimeUnit.SECONDS);\r
+\r
+        longFlowsTask = new LongFlowsTask();\r
+        longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(\r
+                longFlowsTask,\r
+                1,\r
+                longPollInterval,\r
+                TimeUnit.SECONDS);\r
+\r
+        log.info("Started");\r
+    }\r
+\r
+    /**\r
+     * stop adaptive flow statistic collection.\r
+     *\r
+     */\r
+    public synchronized void stop() {\r
+        log.debug("Stopping AdaptiveStats collection thread for {}", sw.getStringId());\r
+        if (calAndShortFlowsThread != null) {\r
+            calAndShortFlowsThread.cancel(true);\r
+        }\r
+        if (midFlowsThread != null) {\r
+            midFlowsThread.cancel(true);\r
+        }\r
+        if (longFlowsThread != null) {\r
+            longFlowsThread.cancel(true);\r
+        }\r
+\r
+        adaptiveFlowStatsScheduler.shutdownNow();\r
+\r
+        isFirstTimeStart = false;\r
+\r
+        log.info("Stopped");\r
+    }\r
+\r
+    /**\r
+     * add typed flow entry from flow rule into the internal flow table.\r
+     *\r
+     * @param flowRules the flow rules\r
+     *\r
+     */\r
+    public synchronized void addWithFlowRule(FlowRule... flowRules) {\r
+        for (FlowRule fr : flowRules) {\r
+            // First remove old entry unconditionally, if exist\r
+            deviceFlowTable.remove(fr);\r
+\r
+            // add new flow entry, we suppose IMMEDIATE_FLOW\r
+            TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fr,\r
+                    FlowLiveType.IMMEDIATE_FLOW);\r
+            deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * add or update typed flow entry from flow entry into the internal flow table.\r
+     *\r
+     * @param flowEntries the flow entries\r
+     *\r
+     */\r
+    public synchronized void addOrUpdateFlows(FlowEntry... flowEntries) {\r
+       for (FlowEntry fe : flowEntries) {\r
+           // check if this new rule is an update to an existing entry\r
+           TypedStoredFlowEntry stored = deviceFlowTable.getFlowEntry(fe);\r
+\r
+           if (stored != null) {\r
+               // duplicated flow entry is collected!, just skip\r
+               if (fe.bytes() == stored.bytes() && fe.packets() == stored.packets()\r
+                       && fe.life() == stored.life()) {\r
+                   log.debug("addOrUpdateFlows:, FlowId=" + Long.toHexString(fe.id().value())\r
+                                   + ",is DUPLICATED stats collection, just skip."\r
+                                   + " AdaptiveStats collection thread for {}",\r
+                           sw.getStringId());\r
+\r
+                   stored.setLastSeen();\r
+                   continue;\r
+               } else if (fe.life() < stored.life()) {\r
+                   // Invalid updates the stats values, i.e., bytes, packets, durations ...\r
+                   log.debug("addOrUpdateFlows():" +\r
+                               " Invalid Flow Update! The new life is SMALLER than the previous one, jus skip." +\r
+                               " new flowId=" + Long.toHexString(fe.id().value()) +\r
+                               ", old flowId=" + Long.toHexString(stored.id().value()) +\r
+                               ", new bytes=" + fe.bytes() + ", old bytes=" + stored.bytes() +\r
+                               ", new life=" + fe.life() + ", old life=" + stored.life() +\r
+                               ", new lastSeen=" + fe.lastSeen() + ", old lastSeen=" + stored.lastSeen());\r
+                   // go next\r
+                   stored.setLastSeen();\r
+                   continue;\r
+               }\r
+\r
+               // update now\r
+               stored.setLife(fe.life());\r
+               stored.setPackets(fe.packets());\r
+               stored.setBytes(fe.bytes());\r
+               stored.setLastSeen();\r
+               if (stored.state() == FlowEntry.FlowEntryState.PENDING_ADD) {\r
+                   // flow is really RULE_ADDED\r
+                   stored.setState(FlowEntry.FlowEntryState.ADDED);\r
+               }\r
+               // flow is RULE_UPDATED, skip adding and just updating flow live table\r
+               //deviceFlowTable.calAndSetFlowLiveType(stored);\r
+               continue;\r
+           }\r
+\r
+           // add new flow entry, we suppose IMMEDIATE_FLOW\r
+           TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fe,\r
+                    FlowLiveType.IMMEDIATE_FLOW);\r
+           deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * remove typed flow entry from the internal flow table.\r
+     *\r
+     * @param flowRules the flow entries\r
+     *\r
+     */\r
+    public synchronized void removeFlows(FlowRule...  flowRules) {\r
+        for (FlowRule rule : flowRules) {\r
+            deviceFlowTable.remove(rule);\r
+        }\r
+    }\r
+\r
+    // same as removeFlows() function\r
+    /**\r
+     * remove typed flow entry from the internal flow table.\r
+     *\r
+     * @param flowRules the flow entries\r
+     *\r
+     */\r
+    public void flowRemoved(FlowRule... flowRules) {\r
+        removeFlows(flowRules);\r
+    }\r
+\r
+    // same as addOrUpdateFlows() function\r
+    /**\r
+     * add or update typed flow entry from flow entry into the internal flow table.\r
+     *\r
+     * @param flowEntries the flow entry list\r
+     *\r
+     */\r
+    public void pushFlowMetrics(List<FlowEntry> flowEntries) {\r
+        flowEntries.forEach(fe -> {\r
+            addOrUpdateFlows(fe);\r
+        });\r
+    }\r
+\r
+    /**\r
+     * returns flowMissingXid that indicates the execution of flowMissing process or not(NO_FLOW_MISSING_XID(-1)).\r
+     *\r
+     */\r
+    public long getFlowMissingXid() {\r
+        return flowMissingXid;\r
+    }\r
+\r
+    /**\r
+     * set flowMissingXid, namely OFFlowStatsRequest match any ALL message Id.\r
+     *\r
+     * @param flowMissingXid the OFFlowStatsRequest message Id\r
+     *\r
+     */\r
+    public void setFlowMissingXid(long flowMissingXid) {\r
+        this.flowMissingXid = flowMissingXid;\r
+    }\r
+\r
+    private class InternalDeviceFlowTable {\r
+\r
+        private final Map<FlowId, Set<TypedStoredFlowEntry>>\r
+                flowEntries = Maps.newConcurrentMap();\r
+\r
+        private final Set<StoredFlowEntry> shortFlows = new HashSet<>();\r
+        private final Set<StoredFlowEntry> midFlows = new HashSet<>();\r
+        private final Set<StoredFlowEntry> longFlows = new HashSet<>();\r
+\r
+        // Assumed latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply\r
+        private final long latencyFlowStatsRequestAndReplyMillis = 500;\r
+\r
+\r
+        // Statistics for table operation\r
+        private long addCount = 0, addWithSetFlowLiveTypeCount = 0;\r
+        private long removeCount = 0;\r
+\r
+        /**\r
+         * Resets all count values with zero.\r
+         *\r
+         */\r
+        public void resetAllCount() {\r
+            addCount = 0;\r
+            addWithSetFlowLiveTypeCount = 0;\r
+            removeCount = 0;\r
+        }\r
+\r
+        // get set of flow entries for the given flowId\r
+        private Set<TypedStoredFlowEntry> getFlowEntriesInternal(FlowId flowId) {\r
+            return flowEntries.computeIfAbsent(flowId, id -> Sets.newCopyOnWriteArraySet());\r
+        }\r
+\r
+        // get flow entry for the given flow rule\r
+        private TypedStoredFlowEntry getFlowEntryInternal(FlowRule rule) {\r
+            Set<TypedStoredFlowEntry> flowEntries = getFlowEntriesInternal(rule.id());\r
+            return flowEntries.stream()\r
+                    .filter(entry -> Objects.equal(entry, rule))\r
+                    .findAny()\r
+                    .orElse(null);\r
+        }\r
+\r
+        // get the flow entries for all flows in flow table\r
+        private Set<TypedStoredFlowEntry> getFlowEntriesInternal() {\r
+            Set<TypedStoredFlowEntry> result = Sets.newHashSet();\r
+\r
+            flowEntries.values().forEach(result::addAll);\r
+            return result;\r
+        }\r
+\r
+        /**\r
+         * Gets the number of flow entry in flow table.\r
+         *\r
+         * @return the number of flow entry.\r
+         *\r
+         */\r
+        public long getFlowCount() {\r
+            return flowEntries.values().stream().mapToLong(Set::size).sum();\r
+        }\r
+\r
+        /**\r
+         * Gets the number of flow entry in flow table.\r
+         *\r
+         * @param rule the flow rule\r
+         * @return the typed flow entry.\r
+         *\r
+         */\r
+        public TypedStoredFlowEntry getFlowEntry(FlowRule rule) {\r
+            checkNotNull(rule);\r
+\r
+            return getFlowEntryInternal(rule);\r
+        }\r
+\r
+        /**\r
+         * Gets the all typed flow entries in flow table.\r
+         *\r
+         * @return the set of typed flow entry.\r
+         *\r
+         */\r
+        public Set<TypedStoredFlowEntry> getFlowEntries() {\r
+            return getFlowEntriesInternal();\r
+        }\r
+\r
+        /**\r
+         * Gets the short typed flow entries in flow table.\r
+         *\r
+         * @return the set of typed flow entry.\r
+         *\r
+         */\r
+        public Set<StoredFlowEntry> getShortFlows() {\r
+            return ImmutableSet.copyOf(shortFlows); //Sets.newHashSet(shortFlows);\r
+        }\r
+\r
+        /**\r
+         * Gets the mid typed flow entries in flow table.\r
+         *\r
+         * @return the set of typed flow entry.\r
+         *\r
+         */\r
+        public Set<StoredFlowEntry> getMidFlows() {\r
+            return ImmutableSet.copyOf(midFlows); //Sets.newHashSet(midFlows);\r
+        }\r
+\r
+        /**\r
+         * Gets the long typed flow entries in flow table.\r
+         *\r
+         * @return the set of typed flow entry.\r
+         *\r
+         */\r
+        public Set<StoredFlowEntry> getLongFlows() {\r
+            return ImmutableSet.copyOf(longFlows); //Sets.newHashSet(longFlows);\r
+        }\r
+\r
+        /**\r
+         * Add typed flow entry into table only.\r
+         *\r
+         * @param rule the flow rule\r
+         *\r
+         */\r
+        public synchronized void add(TypedStoredFlowEntry rule) {\r
+            checkNotNull(rule);\r
+\r
+            //rule have to be new DefaultTypedFlowEntry\r
+            boolean result = getFlowEntriesInternal(rule.id()).add(rule);\r
+\r
+            if (result) {\r
+                addCount++;\r
+            }\r
+        }\r
+\r
+        /**\r
+         * Calculates and set the flow live type at the first time,\r
+         * and then add it into a corresponding typed flow table.\r
+         *\r
+         * @param rule the flow rule\r
+         *\r
+         */\r
+        public void calAndSetFlowLiveType(TypedStoredFlowEntry rule) {\r
+            checkNotNull(rule);\r
+\r
+            calAndSetFlowLiveTypeInternal(rule);\r
+        }\r
+\r
+        /**\r
+         * Add the typed flow entry into table, and calculates and set the flow live type,\r
+         * and then add it into a corresponding typed flow table.\r
+         *\r
+         * @param rule the flow rule\r
+         *\r
+         */\r
+       public synchronized void addWithCalAndSetFlowLiveType(TypedStoredFlowEntry rule) {\r
+            checkNotNull(rule);\r
+\r
+            //rule have to be new DefaultTypedFlowEntry\r
+            boolean result = getFlowEntriesInternal(rule.id()).add(rule);\r
+            if (result) {\r
+                calAndSetFlowLiveTypeInternal(rule);\r
+                addWithSetFlowLiveTypeCount++;\r
+            } else {\r
+                log.debug("addWithCalAndSetFlowLiveType, FlowId=" + Long.toHexString(rule.id().value())\r
+                                + " ADD Failed, cause it may already exists in table !!!,"\r
+                                + " AdaptiveStats collection thread for {}",\r
+                        sw.getStringId());\r
+            }\r
+        }\r
+\r
+        // In real, calculates and set the flow live type at the first time,\r
+        // and then add it into a corresponding typed flow table\r
+        private void calAndSetFlowLiveTypeInternal(TypedStoredFlowEntry rule) {\r
+            long life = rule.life();\r
+            FlowLiveType prevFlowLiveType = rule.flowLiveType();\r
+\r
+            if (life >= longPollInterval) {\r
+                rule.setFlowLiveType(FlowLiveType.LONG_FLOW);\r
+                longFlows.add(rule);\r
+            } else if (life >= midPollInterval) {\r
+                rule.setFlowLiveType(FlowLiveType.MID_FLOW);\r
+                midFlows.add(rule);\r
+            } else if (life >= calAndPollInterval) {\r
+                rule.setFlowLiveType(FlowLiveType.SHORT_FLOW);\r
+                shortFlows.add(rule);\r
+            } else if (life >= 0) {\r
+                rule.setFlowLiveType(FlowLiveType.IMMEDIATE_FLOW);\r
+            } else { // life < 0\r
+                rule.setFlowLiveType(FlowLiveType.UNKNOWN_FLOW);\r
+            }\r
+\r
+            if (rule.flowLiveType() != prevFlowLiveType) {\r
+                switch (prevFlowLiveType) {\r
+                    // delete it from previous flow table\r
+                    case SHORT_FLOW:\r
+                        shortFlows.remove(rule);\r
+                        break;\r
+                    case MID_FLOW:\r
+                        midFlows.remove(rule);\r
+                        break;\r
+                    case LONG_FLOW:\r
+                        longFlows.remove(rule);\r
+                        break;\r
+                    default:\r
+                        break;\r
+                }\r
+            }\r
+        }\r
+\r
+\r
+        // check the flow live type based on current time, then set and add it into corresponding table\r
+        private boolean checkAndMoveLiveFlowInternal(TypedStoredFlowEntry fe, long cTime) {\r
+            long curTime = (cTime > 0 ? cTime : System.currentTimeMillis());\r
+            // For latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply\r
+            long fromLastSeen = ((curTime - fe.lastSeen() + latencyFlowStatsRequestAndReplyMillis) / 1000);\r
+            // fe.life() unit is SECOND!\r
+            long liveTime = fe.life() + fromLastSeen;\r
+\r
+\r
+            switch (fe.flowLiveType()) {\r
+                case IMMEDIATE_FLOW:\r
+                    if (liveTime >= longPollInterval) {\r
+                        fe.setFlowLiveType(FlowLiveType.LONG_FLOW);\r
+                         longFlows.add(fe);\r
+                    } else if (liveTime >= midPollInterval) {\r
+                        fe.setFlowLiveType(FlowLiveType.MID_FLOW);\r
+                        midFlows.add(fe);\r
+                    } else if (liveTime >= calAndPollInterval) {\r
+                        fe.setFlowLiveType(FlowLiveType.SHORT_FLOW);\r
+                        shortFlows.add(fe);\r
+                    }\r
+                    break;\r
+                case SHORT_FLOW:\r
+                    if (liveTime >= longPollInterval) {\r
+                        fe.setFlowLiveType(FlowLiveType.LONG_FLOW);\r
+                        shortFlows.remove(fe);\r
+                        longFlows.add(fe);\r
+                    } else if (liveTime >= midPollInterval) {\r
+                        fe.setFlowLiveType(FlowLiveType.MID_FLOW);\r
+                        shortFlows.remove(fe);\r
+                        midFlows.add(fe);\r
+                    }\r
+                    break;\r
+                case MID_FLOW:\r
+                    if (liveTime >= longPollInterval) {\r
+                        fe.setFlowLiveType(FlowLiveType.LONG_FLOW);\r
+                        midFlows.remove(fe);\r
+                        longFlows.add(fe);\r
+                    }\r
+                    break;\r
+                case LONG_FLOW:\r
+                    if (fromLastSeen > entirePollInterval) {\r
+                        log.trace("checkAndMoveLiveFlowInternal, flow is already removed at switch.");\r
+                        return false;\r
+                    }\r
+                    break;\r
+                case UNKNOWN_FLOW: // Unknown flow is an internal error flow type, just fall through\r
+                default :\r
+                    // Error Unknown Live Type\r
+                    log.error("checkAndMoveLiveFlowInternal, Unknown Live Type error!"\r
+                            + "AdaptiveStats collection thread for {}",\r
+                            sw.getStringId());\r
+                    return false;\r
+            }\r
+\r
+            log.debug("checkAndMoveLiveFlowInternal, FlowId=" + Long.toHexString(fe.id().value())\r
+                            + ", state=" + fe.state()\r
+                            + ", After liveType=" + fe.flowLiveType()\r
+                            + ", liveTime=" + liveTime\r
+                            + ", life=" + fe.life()\r
+                            + ", bytes=" + fe.bytes()\r
+                            + ", packets=" + fe.packets()\r
+                            + ", fromLastSeen=" + fromLastSeen\r
+                            + ", priority=" + fe.priority()\r
+                            + ", selector=" + fe.selector().criteria()\r
+                            + ", treatment=" + fe.treatment()\r
+                            + " AdaptiveStats collection thread for {}",\r
+                    sw.getStringId());\r
+\r
+            return true;\r
+        }\r
+\r
+        /**\r
+         * Check and move live type for all type flow entries in table at every calAndPollInterval time.\r
+         *\r
+         */\r
+        public void checkAndMoveLiveFlowAll() {\r
+            Set<TypedStoredFlowEntry> typedFlowEntries = getFlowEntriesInternal();\r
+\r
+            long calCurTime = System.currentTimeMillis();\r
+            typedFlowEntries.forEach(fe -> {\r
+                if (!checkAndMoveLiveFlowInternal(fe, calCurTime)) {\r
+                    remove(fe);\r
+                }\r
+            });\r
+\r
+            // print table counts for debug\r
+            if (log.isDebugEnabled()) {\r
+                synchronized (this) {\r
+                    long totalFlowCount = getFlowCount();\r
+                    long shortFlowCount = shortFlows.size();\r
+                    long midFlowCount = midFlows.size();\r
+                    long longFlowCount = longFlows.size();\r
+                    long immediateFlowCount = totalFlowCount - shortFlowCount - midFlowCount - longFlowCount;\r
+                    long calTotalCount = addCount + addWithSetFlowLiveTypeCount - removeCount;\r
+\r
+                    log.debug("--------------------------------------------------------------------------- for {}",\r
+                            sw.getStringId());\r
+                    log.debug("checkAndMoveLiveFlowAll, Total Flow_Count=" + totalFlowCount\r
+                            + ", add - remove_Count=" + calTotalCount\r
+                            + ", IMMEDIATE_FLOW_Count=" + immediateFlowCount\r
+                            + ", SHORT_FLOW_Count=" + shortFlowCount\r
+                            + ", MID_FLOW_Count=" + midFlowCount\r
+                            + ", LONG_FLOW_Count=" + longFlowCount\r
+                            + ", add_Count=" + addCount\r
+                            + ", addWithSetFlowLiveType_Count=" + addWithSetFlowLiveTypeCount\r
+                            + ", remove_Count=" + removeCount\r
+                            + " AdaptiveStats collection thread for {}", sw.getStringId());\r
+                    log.debug("--------------------------------------------------------------------------- for {}",\r
+                            sw.getStringId());\r
+                    if (totalFlowCount != calTotalCount) {\r
+                        log.error("checkAndMoveLiveFlowAll, Real total flow count and "\r
+                                + "calculated total flow count do NOT match, something is wrong internally "\r
+                                + "or check counter value bound is over!");\r
+                    }\r
+                    if (immediateFlowCount < 0) {\r
+                        log.error("checkAndMoveLiveFlowAll, IMMEDIATE_FLOW count is negative, "\r
+                                + "something is wrong internally "\r
+                                + "or check counter value bound is over!");\r
+                    }\r
+                }\r
+            }\r
+            log.trace("checkAndMoveLiveFlowAll, AdaptiveStats for {}", sw.getStringId());\r
+        }\r
+\r
+        /**\r
+         * Remove the typed flow entry from table.\r
+         *\r
+         * @param rule the flow rule\r
+         *\r
+         */\r
+        public synchronized void remove(FlowRule rule) {\r
+            checkNotNull(rule);\r
+\r
+            TypedStoredFlowEntry removeStore = getFlowEntryInternal(rule);\r
+            if (removeStore != null) {\r
+                removeLiveFlowsInternal((TypedStoredFlowEntry) removeStore);\r
+                boolean result = getFlowEntriesInternal(rule.id()).remove(removeStore);\r
+\r
+                if (result) {\r
+                    removeCount++;\r
+                }\r
+            }\r
+       }\r
+\r
+        // Remove the typed flow entry from corresponding table\r
+        private void removeLiveFlowsInternal(TypedStoredFlowEntry fe) {\r
+            switch (fe.flowLiveType()) {\r
+                case IMMEDIATE_FLOW:\r
+                    // do nothing\r
+                    break;\r
+                case SHORT_FLOW:\r
+                    shortFlows.remove(fe);\r
+                    break;\r
+                case MID_FLOW:\r
+                    midFlows.remove(fe);\r
+                    break;\r
+                case LONG_FLOW:\r
+                    longFlows.remove(fe);\r
+                    break;\r
+                default: // error in Flow Live Type\r
+                    log.error("removeLiveFlowsInternal, Unknown Live Type error!");\r
+                    break;\r
+            }\r
+        }\r
+    }\r
+}\r
index de079e0..6374ca5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import com.google.common.cache.RemovalCause;
 import com.google.common.cache.RemovalNotification;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -32,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultTableStatisticsEntry;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleBatchEntry;
@@ -40,6 +42,7 @@ import org.onosproject.net.flow.FlowRuleExtPayLoad;
 import org.onosproject.net.flow.FlowRuleProvider;
 import org.onosproject.net.flow.FlowRuleProviderRegistry;
 import org.onosproject.net.flow.FlowRuleProviderService;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.statistic.DefaultLoad;
@@ -58,6 +61,8 @@ import org.projectfloodlight.openflow.protocol.OFErrorType;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
@@ -70,12 +75,14 @@ import java.util.Collections;
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Timer;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onlab.util.Tools.get;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -99,11 +106,16 @@ public class OpenFlowRuleProvider extends AbstractProvider
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService cfgService;
 
-    private static final int DEFAULT_POLL_FREQUENCY = 10;
+    private static final int DEFAULT_POLL_FREQUENCY = 5;
     @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
             label = "Frequency (in seconds) for polling flow statistics")
     private int flowPollFrequency = DEFAULT_POLL_FREQUENCY;
 
+    private static final boolean DEFAULT_ADAPTIVE_FLOW_SAMPLING = true;
+    @Property(name = "adaptiveFlowSampling", boolValue = DEFAULT_ADAPTIVE_FLOW_SAMPLING,
+            label = "Adaptive Flow Sampling is on or off")
+    private boolean adaptiveFlowSampling = DEFAULT_ADAPTIVE_FLOW_SAMPLING;
+
     private FlowRuleProviderService providerService;
 
     private final InternalFlowProvider listener = new InternalFlowProvider();
@@ -111,7 +123,12 @@ public class OpenFlowRuleProvider extends AbstractProvider
     private Cache<Long, InternalCacheEntry> pendingBatches;
 
     private final Timer timer = new Timer("onos-openflow-collector");
+    private final Map<Dpid, FlowStatsCollector> simpleCollectors = Maps.newHashMap();
+
+    // NewAdaptiveFlowStatsCollector Set
+    private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newHashMap();
     private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
+    private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newHashMap();
 
     /**
      * Creates an OpenFlow host provider.
@@ -128,9 +145,11 @@ public class OpenFlowRuleProvider extends AbstractProvider
         controller.addEventListener(listener);
 
         pendingBatches = createBatchCache();
+
         createCollectors();
 
-        log.info("Started");
+        log.info("Started with flowPollFrequency = {}, adaptiveFlowSampling = {}",
+                flowPollFrequency, adaptiveFlowSampling);
     }
 
     @Deactivate
@@ -161,6 +180,20 @@ public class OpenFlowRuleProvider extends AbstractProvider
         }
 
         log.info("Settings: flowPollFrequency={}", flowPollFrequency);
+
+        boolean newAdaptiveFlowSampling;
+        String s = get(properties, "adaptiveFlowSampling");
+        newAdaptiveFlowSampling = isNullOrEmpty(s) ? adaptiveFlowSampling : Boolean.parseBoolean(s.trim());
+
+        if (newAdaptiveFlowSampling != adaptiveFlowSampling) {
+            // stop previous collector
+            stopCollectors();
+            adaptiveFlowSampling = newAdaptiveFlowSampling;
+            // create new collectors
+            createCollectors();
+        }
+
+        log.info("Settings: adaptiveFlowSampling={}", adaptiveFlowSampling);
     }
 
     private Cache<Long, InternalCacheEntry> createBatchCache() {
@@ -179,19 +212,43 @@ public class OpenFlowRuleProvider extends AbstractProvider
     }
 
     private void createCollector(OpenFlowSwitch sw) {
-        FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency);
-        fsc.start();
-        collectors.put(new Dpid(sw.getId()), fsc);
+        if (adaptiveFlowSampling) {
+            // NewAdaptiveFlowStatsCollector Constructor
+            NewAdaptiveFlowStatsCollector fsc = new NewAdaptiveFlowStatsCollector(sw, flowPollFrequency);
+            fsc.start();
+            afsCollectors.put(new Dpid(sw.getId()), fsc);
+        } else {
+            FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency);
+            fsc.start();
+            simpleCollectors.put(new Dpid(sw.getId()), fsc);
+        }
+        TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency);
+        tsc.start();
+        tableStatsCollectors.put(new Dpid(sw.getId()), tsc);
     }
 
     private void stopCollectors() {
-        collectors.values().forEach(FlowStatsCollector::stop);
-        collectors.clear();
+        if (adaptiveFlowSampling) {
+            // NewAdaptiveFlowStatsCollector Destructor
+            afsCollectors.values().forEach(NewAdaptiveFlowStatsCollector::stop);
+            afsCollectors.clear();
+        } else {
+            simpleCollectors.values().forEach(FlowStatsCollector::stop);
+            simpleCollectors.clear();
+        }
+        tableStatsCollectors.values().forEach(TableStatisticsCollector::stop);
+        tableStatsCollectors.clear();
     }
 
     private void adjustRate() {
         DefaultLoad.setPollInterval(flowPollFrequency);
-        collectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
+        if (adaptiveFlowSampling) {
+            // NewAdaptiveFlowStatsCollector calAndPollInterval
+            afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency));
+        } else {
+            simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
+        }
+        tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency));
     }
 
     @Override
@@ -202,8 +259,9 @@ public class OpenFlowRuleProvider extends AbstractProvider
     }
 
     private void applyRule(FlowRule flowRule) {
-        OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
-                                                                   .uri()));
+        Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
+
         FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
         if (hasPayload(flowRuleExtPayLoad)) {
             OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
@@ -212,6 +270,14 @@ public class OpenFlowRuleProvider extends AbstractProvider
         }
         sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
                                           Optional.empty()).buildFlowAdd());
+
+        if (adaptiveFlowSampling) {
+            // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+            NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
+            if (collector != null) {
+                collector.addWithFlowRule(flowRule);
+            }
+        }
     }
 
     @Override
@@ -222,8 +288,9 @@ public class OpenFlowRuleProvider extends AbstractProvider
     }
 
     private void removeRule(FlowRule flowRule) {
-        OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
-                                                                   .uri()));
+        Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
+
         FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
         if (hasPayload(flowRuleExtPayLoad)) {
             OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
@@ -232,6 +299,14 @@ public class OpenFlowRuleProvider extends AbstractProvider
         }
         sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
                                           Optional.empty()).buildFlowDel());
+
+        if (adaptiveFlowSampling) {
+            // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+            NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
+            if (collector != null) {
+                collector.removeFlows(flowRule);
+            }
+        }
     }
 
     @Override
@@ -242,11 +317,12 @@ public class OpenFlowRuleProvider extends AbstractProvider
 
     @Override
     public void executeBatch(FlowRuleBatchOperation batch) {
+        checkNotNull(batch);
 
         pendingBatches.put(batch.id(), new InternalCacheEntry(batch));
 
-        OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId()
-                                                                   .uri()));
+        Dpid dpid = Dpid.dpid(batch.deviceId().uri());
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
         OFFlowMod mod;
         for (FlowRuleBatchEntry fbe : batch.getOperations()) {
             // flow is the third party privacy flow
@@ -257,21 +333,35 @@ public class OpenFlowRuleProvider extends AbstractProvider
                 sw.sendMsg(msg);
                 continue;
             }
-            FlowModBuilder builder = FlowModBuilder.builder(fbe.target(), sw
-                    .factory(), Optional.of(batch.id()));
+            FlowModBuilder builder =
+                    FlowModBuilder.builder(fbe.target(), sw.factory(), Optional.of(batch.id()));
+            NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
             switch (fbe.operator()) {
                 case ADD:
                     mod = builder.buildFlowAdd();
+                    if (adaptiveFlowSampling && collector != null) {
+                        // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+                        collector.addWithFlowRule(fbe.target());
+                    }
                     break;
                 case REMOVE:
                     mod = builder.buildFlowDel();
+                    if (adaptiveFlowSampling && collector != null) {
+                        // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+                        collector.removeFlows(fbe.target());
+                    }
                     break;
                 case MODIFY:
                     mod = builder.buildFlowMod();
+                    if (adaptiveFlowSampling && collector != null) {
+                        // Add or Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+                        // afsCollectors.get(dpid).addWithFlowRule(fbe.target()); //check if add is good or not
+                        collector.addOrUpdateFlows((FlowEntry) fbe.target());
+                    }
                     break;
                 default:
                     log.error("Unsupported batch operation {}; skipping flowmod {}",
-                              fbe.operator(), fbe);
+                            fbe.operator(), fbe);
                     continue;
             }
             sw.sendMsg(mod);
@@ -292,14 +382,28 @@ public class OpenFlowRuleProvider extends AbstractProvider
 
         @Override
         public void switchAdded(Dpid dpid) {
+
+            OpenFlowSwitch sw = controller.getSwitch(dpid);
+
             createCollector(controller.getSwitch(dpid));
         }
 
         @Override
         public void switchRemoved(Dpid dpid) {
-            FlowStatsCollector collector = collectors.remove(dpid);
-            if (collector != null) {
-                collector.stop();
+            if (adaptiveFlowSampling) {
+                NewAdaptiveFlowStatsCollector collector = afsCollectors.remove(dpid);
+                if (collector != null) {
+                    collector.stop();
+                }
+            } else {
+                FlowStatsCollector collector = simpleCollectors.remove(dpid);
+                if (collector != null) {
+                    collector.stop();
+                }
+            }
+            TableStatisticsCollector tsc = tableStatsCollectors.remove(dpid);
+            if (tsc != null) {
+                tsc.stop();
             }
         }
 
@@ -321,10 +425,20 @@ public class OpenFlowRuleProvider extends AbstractProvider
 
                     FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
                     providerService.flowRemoved(fr);
+
+                    if (adaptiveFlowSampling) {
+                        // Removed TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+                        NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
+                        if (collector != null) {
+                            collector.flowRemoved(fr);
+                        }
+                    }
                     break;
                 case STATS_REPLY:
                     if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
                         pushFlowMetrics(dpid, (OFFlowStatsReply) msg);
+                    } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) {
+                        pushTableStatistics(dpid, (OFTableStatsReply) msg);
                     }
                     break;
                 case BARRIER_REPLY:
@@ -370,11 +484,10 @@ public class OpenFlowRuleProvider extends AbstractProvider
                                               + " tell us which one.");
                         }
                     }
-                    break;
+
                 default:
                     log.debug("Unhandled message type: {}", msg.getType());
             }
-
         }
 
         @Override
@@ -386,13 +499,68 @@ public class OpenFlowRuleProvider extends AbstractProvider
         private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) {
 
             DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
-            OpenFlowSwitch sw = controller.getSwitch(dpid);
 
             List<FlowEntry> flowEntries = replies.getEntries().stream()
                     .map(entry -> new FlowEntryBuilder(dpid, entry).build())
                     .collect(Collectors.toList());
 
-            providerService.pushFlowMetrics(did, flowEntries);
+            if (adaptiveFlowSampling)  {
+                NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid);
+
+                synchronized (afsc) {
+                    if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
+                        log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, "
+                                        + "OFFlowStatsReply Xid={}, for {}",
+                                afsc.getFlowMissingXid(), replies.getXid(), dpid);
+                    }
+
+                    // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest?
+                    if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
+                        if (afsc.getFlowMissingXid() == replies.getXid()) {
+                            // call entire flow stats update with flowMissing synchronization.
+                            // used existing pushFlowMetrics
+                            providerService.pushFlowMetrics(did, flowEntries);
+                        }
+                        // reset flowMissingXid to NO_FLOW_MISSING_XID
+                        afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID);
+
+                    } else {
+                        // call individual flow stats update
+                        providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries);
+                    }
+
+                    // Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+                    afsc.pushFlowMetrics(flowEntries);
+                }
+            } else {
+                // call existing entire flow stats update with flowMissing synchronization
+                providerService.pushFlowMetrics(did, flowEntries);
+            }
+        }
+
+        private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) {
+
+            DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
+            List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream()
+                    .map(entry -> buildTableStatistics(did, entry))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());
+            providerService.pushTableStatistics(did, tableStatsEntries);
+        }
+
+        private TableStatisticsEntry buildTableStatistics(DeviceId deviceId,
+                                                          OFTableStatsEntry ofEntry) {
+            TableStatisticsEntry entry = null;
+            if (ofEntry != null) {
+                entry = new DefaultTableStatisticsEntry(deviceId,
+                                                        ofEntry.getTableId().getValue(),
+                                                        ofEntry.getActiveCount(),
+                                                        ofEntry.getLookupCount().getValue(),
+                                                        ofEntry.getMatchedCount().getValue());
+            }
+
+            return entry;
+
         }
     }
 
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java
new file mode 100644 (file)
index 0000000..922a470
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.provider.of.flow.impl;
+
+import org.onlab.util.SharedExecutors;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFTableStatsRequest;
+import org.slf4j.Logger;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Collects Table statistics for the specified switch.
+ */
+class TableStatisticsCollector {
+
+    private final Logger log = getLogger(getClass());
+
+    public static final int SECONDS = 1000;
+
+    private final OpenFlowSwitch sw;
+    private Timer timer;
+    private TimerTask task;
+
+    private int pollInterval;
+
+    /**
+     * Creates a new table statistics collector for the given switch and poll frequency.
+     *
+     * @param timer        timer to use for scheduling
+     * @param sw           switch to pull
+     * @param pollInterval poll frequency in seconds
+     */
+    TableStatisticsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) {
+        this.timer = timer;
+        this.sw = sw;
+        this.pollInterval = pollInterval;
+    }
+
+    /**
+     * Adjusts poll frequency.
+     *
+     * @param pollInterval poll frequency in seconds
+     */
+    synchronized void adjustPollInterval(int pollInterval) {
+        this.pollInterval = pollInterval;
+        task.cancel();
+        task = new InternalTimerTask();
+        timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000);
+    }
+
+    private class InternalTimerTask extends TimerTask {
+        @Override
+        public void run() {
+            if (sw.getRole() == RoleState.MASTER) {
+                log.trace("Collecting stats for {}", sw.getStringId());
+                OFTableStatsRequest request = sw.factory().buildTableStatsRequest()
+                        .build();
+                sw.sendMsg(request);
+            }
+        }
+    }
+
+    public synchronized void start() {
+        // Initially start polling quickly. Then drop down to configured value
+        log.debug("Starting Table Stats collection thread for {}", sw.getStringId());
+        task = new InternalTimerTask();
+        SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS,
+                                                       pollInterval * SECONDS);
+    }
+
+    public synchronized void stop() {
+        log.debug("Stopping Table Stats collection thread for {}", sw.getStringId());
+        task.cancel();
+        task = null;
+    }
+
+}
index 5e4c567..7663a64 100644 (file)
@@ -27,6 +27,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.device.DeviceDescription;
@@ -193,6 +194,10 @@ public class OvsdbDeviceProviderTest {
             return null;
         }
 
+        @Override
+        public void connect(IpAddress ip, TpPort port) {
+
+        }
     }
 
 }
index ad720c8..01e07dd 100644 (file)
@@ -24,7 +24,9 @@ import java.util.Set;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.HostId;
 import org.onosproject.net.host.HostDescription;
@@ -159,6 +161,11 @@ public class OvsdbHostProviderTest {
             removeCount++;
         }
 
+        @Override
+        public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
+
+        }
+
     }
 
     private class OvsdbControllerTest implements OvsdbController {
@@ -195,5 +202,10 @@ public class OvsdbHostProviderTest {
         public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) {
             return null;
         }
+
+        @Override
+        public void connect(IpAddress ip, TpPort port) {
+
+        }
     }
 }
index e4f8e9d..29e4dea 100644 (file)
@@ -27,7 +27,7 @@
 
     <groupId>org.onosproject</groupId>
     <artifactId>onos-build-conf</artifactId>
-    <version>1.0</version>
+    <version>1.1</version>
     <description>Various ONOS build settings</description>
 
     <properties>
index 2908c89..1bb3b0b 100644 (file)
             <!-- ONOS alows declarations inside of switch case blocks -->
             <property name="allowInSwitchCase" value="true"/>
         </module>
-        <module name="EmptyBlock"/>
+        <module name="EmptyBlock">
+            <!-- allow empty block, as long as there's some comment -->
+            <property name="option" value="text"/>
+        </module>
         <module name="LeftCurly"/>
         <module name="NeedBraces"/>
         <module name="RightCurly"/>
index fd61ad8..ee9666a 100644 (file)
@@ -45,11 +45,11 @@ RUN mkdir onos && \
 
 
 # Ports
-# 6633 - OpenFlow
+# 6653 - OpenFlow
 # 8181 - GUI
 # 8101 - ONOS CLI
 # 9876 - ONOS CLUSTER COMMUNICATION
-EXPOSE 6633 8181 8101 9876
+EXPOSE 6653 8181 8101 9876
 
 # Get ready to run command
 WORKDIR /root/onos
index 3279445..5566e60 100755 (executable)
@@ -28,6 +28,10 @@ function build_stage_dir() {
     [ -f $KARAF_TAR ] && tar zxf $KARAF_TAR && rm -rf $ONOS_STAGE/$KARAF_DIST/demos
     mkdir bin
 
+    # Patch the log-file size in place to increase it to 10 MB
+    perl -pi.old -e "s/maxFileSize=1MB/maxFileSize=10MB/g" \
+        $ONOS_STAGE/$KARAF_DIST/etc/org.ops4j.pax.logging.cfg
+
     # Stage the ONOS admin scripts and patch in Karaf service wrapper extras
     cp -r $ONOS_ROOT/tools/package/bin .
     cp -r $ONOS_ROOT/tools/package/init $ONOS_STAGE/init
index 5e161cc..f39c2ce 100644 (file)
@@ -148,41 +148,41 @@ function nuke {
     spy "$@" | cut -c7-11 | xargs kill
 }
 
-# Edit a cell file by providing a cell name. Opens the cell file in $EDITOR.
-function vicell() {
-  local apply=false
-  local create=false
-  local cdf=""
-  local cpath="${ONOS_ROOT}/tools/test/cells/"
-
-  if [ -z "$1" ] || [ "$1" = "-h" ] ; then
-    printf "usage: vicell [file] [options]\n\noptions:\n"
-    printf "\t-a: apply the cell after editing\n"
-    printf "\t-e: [editor] set EDITOR to [editor] (default *vi*)\n"
-    printf "\t-c: create cell file if none exist\n\n"
-    return 1
-  fi
-
-  while [ $# -gt 0 ]; do
-    case "$1" in
-      -a) apply=true ;;
-      -e) EDITOR=$2; shift ;;
-      -c) create=true ;;
-      *) cdf="$1" ;;
-    esac
-    shift
-  done
-
-  if [ ! -e "${cpath}${cdf}" ] && [ "$create" = "false" ]; then
-       printf "${cdf} : no such cell\n" && return 1
-  fi
-
-  if [ -z "${EDITOR}" ] || [ -x "$(which ${EDITOR})" ]; then
-    unset EDITOR && vi ${cpath}${cdf}
-  else
-    $EDITOR ${cpath}${cdf}
-  fi
-  ($apply) && cell ${cdf}
+# Edit a cell file by providing a cell name; opens the cell file in $EDITOR.
+function vicell {
+    local apply=false
+    local create=false
+    local cdf=""
+    local cpath="${ONOS_ROOT}/tools/test/cells/"
+
+    if [ -z "$1" ] || [ "$1" = "-h" ] ; then
+        printf "usage: vicell [file] [options]\n\noptions:\n"
+        printf "\t-a: apply the cell after editing\n"
+        printf "\t-e: [editor] set EDITOR to [editor] (default *vi*)\n"
+        printf "\t-c: create cell file if none exist\n\n"
+        return 1
+    fi
+
+    while [ $# -gt 0 ]; do
+        case "$1" in
+            -a) apply=true ;;
+            -e) EDITOR=$2; shift ;;
+            -c) create=true ;;
+            *) cdf="$1" ;;
+        esac
+        shift
+    done
+
+    if [ ! -e "${cpath}${cdf}" ] && [ "$create" = "false" ]; then
+        printf "${cdf} : no such cell\n" && return 1
+    fi
+
+    if [ -z "${EDITOR}" ] || [ -x "$(which ${EDITOR})" ]; then
+        unset EDITOR && vi ${cpath}${cdf}
+    else
+        $EDITOR ${cpath}${cdf}
+    fi
+    ($apply) && cell ${cdf}
 }
 
 # autocomplete for certain utilities
index 65b00b6..454bcd6 100755 (executable)
@@ -11,7 +11,7 @@ type=${1:-bundle}
 [ $type = app ] && archetype=bundle || archetype=$type
 
 if [ "$1" = "-?" -o "$1" = "-h" -o "$1" = "--help" ]; then
-    echo "usage: $(basename $0) {app|bundle|ui|cli|api} groupId artifactId version package mvn-options"
+    echo "usage: $(basename $0) {app|bundle|ui|uitab|uitopo|cli|api} groupId artifactId version package mvn-options"
     echo "        All arguments are optional"
     exit 1
 fi
index 6c18c92..5b9dcb8 100644 (file)
@@ -1,4 +1,4 @@
-Copyright $today.year Open Networking Laboratory
+Copyright 2014-$today.year Open Networking Laboratory
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
index 525a2f6..12a2322 100644 (file)
@@ -38,6 +38,8 @@
         <module>bundle</module>
         <module>cli</module>
         <module>ui</module>
+        <module>uitab</module>
+        <module>uitopo</module>
     </modules>
 
     <build>
index 8dd3a8e..9bebe9d 100644 (file)
@@ -26,6 +26,6 @@
   <artifactId>onos-ui-archetype</artifactId>
   <packaging>maven-archetype</packaging>
 
-  <description>ONOS UI overlay archetype</description>
+  <description>ONOS UI Custom-View overlay archetype</description>
 
 </project>
index 2e1f091..d67c181 100644 (file)
@@ -22,7 +22,7 @@
     <version>${version}</version>
     <packaging>bundle</packaging>
 
-    <description>ONOS OSGi UI bundle archetype</description>
+    <description>ONOS OSGi UI Custom-View bundle archetype</description>
     <url>http://onosproject.org</url>
 
     <properties>
index f40bcb5..e44b34d 100644 (file)
@@ -34,11 +34,14 @@ import org.slf4j.LoggerFactory;
 import java.util.List;
 
 /**
- * Skeletal ONOS UI application component.
+ * Skeletal ONOS UI Custom-View application component.
  */
 @Component(immediate = true)
 public class AppUiComponent {
 
+    private static final String VIEW_ID = "sampleCustom";
+    private static final String VIEW_TEXT = "Sample Custom";
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -46,7 +49,7 @@ public class AppUiComponent {
 
     // List of application views
     private final List<UiView> uiViews = ImmutableList.of(
-            new UiView(UiView.Category.OTHER, "sample", "Sample")
+            new UiView(UiView.Category.OTHER, VIEW_ID, VIEW_TEXT)
     );
 
     // Factory for UI message handlers
index d9d68b5..d648632 100644 (file)
@@ -22,168 +22,56 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
-import org.onosproject.ui.table.TableModel;
-import org.onosproject.ui.table.TableRequestHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.Override;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 
 /**
- * Skeletal ONOS UI message handler.
- * <p>
- * This example specifically supporting a "table" view.
+ * Skeletal ONOS UI Custom-View message handler.
  */
 public class AppUiMessageHandler extends UiMessageHandler {
 
-    private static final String SAMPLE_DATA_REQ = "sampleDataRequest";
-    private static final String SAMPLE_DATA_RESP = "sampleDataResponse";
-    private static final String SAMPLES = "samples";
+    private static final String SAMPLE_CUSTOM_DATA_REQ = "sampleCustomDataRequest";
+    private static final String SAMPLE_CUSTOM_DATA_RESP = "sampleCustomDataResponse";
 
-    private static final String SAMPLE_DETAIL_REQ = "sampleDetailsRequest";
-    private static final String SAMPLE_DETAIL_RESP = "sampleDetailsResponse";
-    private static final String DETAILS = "details";
-
-    private static final String ID = "id";
-    private static final String LABEL = "label";
-    private static final String CODE = "code";
-    private static final String COMMENT = "comment";
-    private static final String RESULT = "result";
-
-    private static final String[] COLUMN_IDS = { ID, LABEL, CODE };
+    private static final String NUMBER = "number";
+    private static final String SQUARE = "square";
+    private static final String CUBE = "cube";
+    private static final String MESSAGE = "message";
+    private static final String MSG_FORMAT = "Next incrememt is %d units";
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
+    private long someNumber = 1;
+    private long someIncrement = 1;
 
     @Override
     protected Collection<RequestHandler> createRequestHandlers() {
         return ImmutableSet.of(
-                new SampleDataRequestHandler(),
-                new SampleDetailRequestHandler()
+                new SampleCustomDataRequestHandler()
         );
     }
 
-    // handler for sample table requests
-    private final class SampleDataRequestHandler extends TableRequestHandler {
-
-        private SampleDataRequestHandler() {
-            super(SAMPLE_DATA_REQ, SAMPLE_DATA_RESP, SAMPLES);
-        }
-
-        // if necessary, override defaultColumnId() -- if it isn't "id"
+    // handler for sample data requests
+    private final class SampleCustomDataRequestHandler extends RequestHandler {
 
-        @Override
-        protected String[] getColumnIds() {
-            return COLUMN_IDS;
-        }
-
-        @Override
-        protected void populateTable(TableModel tm, ObjectNode payload) {
-            // === set custom column cell formatters/comparators if need be...
-            // tm.setFormatter(CODE, new CodeFormatter());
-            // tm.setComparator(CODE, new CodeComparator());
-
-            // === retrieve table row items from some service...
-            // SomeService ss = get(SomeService.class);
-            // List<Item> items = ss.getItems()
-
-            // fake data for demonstration purposes...
-            List<Item> items = getItems();
-            for (Item item: items) {
-                populateRow(tm.addRow(), item);
-            }
-        }
-
-        private void populateRow(TableModel.Row row, Item item) {
-            row.cell(ID, item.id())
-                    .cell(LABEL, item.label())
-                    .cell(CODE, item.code());
-        }
-    }
-
-
-    // handler for sample item details requests
-    private final class SampleDetailRequestHandler extends RequestHandler {
-
-        private SampleDetailRequestHandler() {
-            super(SAMPLE_DETAIL_REQ);
+        private SampleCustomDataRequestHandler() {
+            super(SAMPLE_CUSTOM_DATA_REQ);
         }
 
         @Override
         public void process(long sid, ObjectNode payload) {
-            String id = string(payload, ID, "(none)");
-
-            // SomeService ss = get(SomeService.class);
-            // Item item = ss.getItemDetails(id)
-
-            // fake data for demonstration purposes...
-            Item item = getItem(id);
-
-            ObjectNode rootNode = MAPPER.createObjectNode();
-            ObjectNode data = MAPPER.createObjectNode();
-            rootNode.set(DETAILS, data);
-
-            if (item == null) {
-                rootNode.put(RESULT, "Item with id '" + id + "' not found");
-                log.warn("attempted to get item detail for id '{}'", id);
-
-            } else {
-                rootNode.put(RESULT, "Found item with id '" + id + "'");
-
-                data.put(ID, item.id());
-                data.put(LABEL, item.label());
-                data.put(CODE, item.code());
-                data.put(COMMENT, "Some arbitrary comment");
-            }
-
-            sendMessage(SAMPLE_DETAIL_RESP, 0, rootNode);
-        }
-    }
-
-
-    // ===================================================================
-    // NOTE: The code below this line is to create fake data for this
-    //       sample code. Normally you would use existing services to
-    //       provide real data.
-
-    // Lookup a single item.
-    private static Item getItem(String id) {
-        // We realize this code is really inefficient, but
-        // it suffices for our purposes of demonstration...
-        for (Item item : getItems()) {
-            if (item.id().equals(id)) {
-                return item;
-            }
+            someIncrement++;
+            someNumber += someIncrement;
+            log.debug("Computing data for {}...", someNumber);
+
+            ObjectNode result = objectNode();
+            result.put(NUMBER, someNumber);
+            result.put(SQUARE, someNumber * someNumber);
+            result.put(CUBE, someNumber * someNumber * someNumber);
+            result.put(MESSAGE, String.format(MSG_FORMAT, someIncrement + 1));
+            sendMessage(SAMPLE_CUSTOM_DATA_RESP, 0, result);
         }
-        return null;
-    }
-
-    // Produce a list of items.
-    private static List<Item> getItems() {
-        List<Item> items = new ArrayList<>();
-        items.add(new Item("item-1", "foo", 42));
-        items.add(new Item("item-2", "bar", 99));
-        items.add(new Item("item-3", "baz", 65));
-        return items;
-    }
-
-    // Simple model class to provide sample data
-    private static class Item {
-        private final String id;
-        private final String label;
-        private final int code;
-
-        Item(String id, String label, int code) {
-            this.id = id;
-            this.label = label;
-            this.code = code;
-        }
-
-        String id() { return id; }
-        String label() { return label; }
-        int code() { return code; }
     }
 }
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.css b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.css
new file mode 100644 (file)
index 0000000..ffeac0a
--- /dev/null
@@ -0,0 +1,48 @@
+/* css for sample app custom view */
+
+#ov-sample-custom {
+    padding: 20px;
+}
+.light #ov-sample-custom {
+    color: navy;
+}
+.dark #ov-sample-custom {
+    color: #88f;
+}
+
+#ov-sample-custom .button-panel {
+    margin: 10px;
+    width: 200px;
+}
+
+.light #ov-sample-custom .button-panel {
+    background-color: #ccf;
+}
+.dark #ov-sample-custom .button-panel {
+    background-color: #444;
+}
+
+#ov-sample-custom .my-button {
+    cursor: pointer;
+    padding: 4px;
+    text-align: center;
+}
+
+.light #ov-sample-custom .my-button {
+    color: white;
+    background-color: #99d;
+}
+.dark #ov-sample-custom .my-button {
+    color: black;
+    background-color: #aaa;
+}
+
+#ov-sample-custom .number {
+    font-size: 140%;
+    text-align: right;
+}
+
+#ov-sample-custom .quote {
+    margin: 10px 20px;
+    font-style: italic;
+}
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.html b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.html
new file mode 100644 (file)
index 0000000..d3d79a1
--- /dev/null
@@ -0,0 +1,32 @@
+<!-- partial HTML -->
+<div id="ov-sample-custom">
+    <div class="button-panel">
+        <div class="my-button" ng-click="getData()">
+            Fetch Data
+        </div>
+    </div>
+
+    <div class="data-panel">
+        <table>
+            <tr>
+                <td> Number </td>
+                <td class="number"> {{data.number}} </td>
+            </tr>
+            <tr>
+                <td> Square </td>
+                <td class="number"> {{data.square}} </td>
+            </tr>
+            <tr>
+                <td> Cube </td>
+                <td class="number"> {{data.cube}} </td>
+            </tr>
+        </table>
+
+        <p>
+            A message from our sponsors:
+        </p>
+        <p>
+            <span class="quote"> {{data.message}} </span>
+        </p>
+    </div>
+</div>
diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.js b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.js
new file mode 100644 (file)
index 0000000..2105864
--- /dev/null
@@ -0,0 +1,69 @@
+// js for sample app custom view
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, $scope, wss, ks;
+
+    // constants
+    var dataReq = 'sampleCustomDataRequest',
+        dataResp = 'sampleCustomDataResponse';
+
+    function addKeyBindings() {
+        var map = {
+            space: [getData, 'Fetch data from server'],
+
+            _helpFormat: [
+                ['space']
+            ]
+        };
+
+        ks.keyBindings(map);
+    }
+
+    function getData() {
+        wss.sendEvent(dataReq);
+    }
+
+    function respDataCb(data) {
+        $scope.data = data;
+        $scope.$apply();
+    }
+
+
+    angular.module('ovSampleCustom', [])
+        .controller('OvSampleCustomCtrl',
+        ['$log', '$scope', 'WebSocketService', 'KeyService',
+
+        function (_$log_, _$scope_, _wss_, _ks_) {
+            $log = _$log_;
+            $scope = _$scope_;
+            wss = _wss_;
+            ks = _ks_;
+
+            var handlers = {};
+            $scope.data = {};
+
+            // data response handler
+            handlers[dataResp] = respDataCb;
+            wss.bindHandlers(handlers);
+
+            addKeyBindings();
+
+            // custom click handler
+            $scope.getData = getData;
+
+            // get data the first time...
+            getData();
+
+            // cleanup
+            $scope.$on('$destroy', function () {
+                wss.unbindHandlers(handlers);
+                ks.unbindKeys();
+                $log.log('OvSampleCustomCtrl has been destroyed');
+            });
+
+            $log.log('OvSampleCustomCtrl has been created');
+        }]);
+
+}());
diff --git a/framework/src/onos/tools/package/archetypes/uitab/pom.xml b/framework/src/onos/tools/package/archetypes/uitab/pom.xml
new file mode 100644 (file)
index 0000000..cb18f1f
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.onosproject</groupId>
+    <artifactId>onos-archetypes</artifactId>
+    <version>1.4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>onos-uitab-archetype</artifactId>
+  <packaging>maven-archetype</packaging>
+
+  <description>ONOS UI Table-View overlay archetype</description>
+
+</project>
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/META-INF/maven/archetype-metadata.xml b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644 (file)
index 0000000..a627381
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<archetype-descriptor
+        xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
+        name="onos-uitab" partial="true"
+        xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <fileSets>
+        <fileSet filtered="true" packaged="true" encoding="UTF-8">
+            <directory>src/main/java</directory>
+            <includes>
+                <include>**/*.java</include>
+            </includes>
+        </fileSet>
+        <fileSet filtered="true" packaged="false" encoding="UTF-8">
+            <directory>src/main/resources</directory>
+            <includes>
+                <include>**/*.html</include>
+                <include>**/*.js</include>
+                <include>**/*.css</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</archetype-descriptor>
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/pom.xml b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/pom.xml
new file mode 100644 (file)
index 0000000..05a62b2
--- /dev/null
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>${groupId}</groupId>
+    <artifactId>${artifactId}</artifactId>
+    <version>${version}</version>
+    <packaging>bundle</packaging>
+
+    <description>ONOS OSGi UI Table-View bundle archetype</description>
+    <url>http://onosproject.org</url>
+
+    <properties>
+        <onos.version>1.4.0-SNAPSHOT</onos.version>
+        <!-- Uncomment to generate ONOS app from this module.
+        <onos.app.name>org.foo.app</onos.app.name>
+        <onos.app.origin>Foo, Inc.</onos.app.origin>
+        -->
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${onos.version}</version>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <version>1.9.8</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.5.3</version>
+                <extensions>true</extensions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.5.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+                <version>1.20.0</version>
+                <executions>
+                    <execution>
+                        <id>generate-scr-srcdescriptor</id>
+                        <goals>
+                            <goal>scr</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <supportedProjectTypes>
+                        <supportedProjectType>bundle</supportedProjectType>
+                        <supportedProjectType>war</supportedProjectType>
+                    </supportedProjectTypes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-maven-plugin</artifactId>
+                <version>1.5</version>
+                <executions>
+                    <execution>
+                        <id>cfg</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>cfg</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>swagger</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>swagger</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>app</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>app</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableComponent.java b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableComponent.java
new file mode 100644 (file)
index 0000000..263564c
--- /dev/null
@@ -0,0 +1,80 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ * Copyright 2014,2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ${package};
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Skeletal ONOS UI Table-View application component.
+ */
+@Component(immediate = true)
+public class AppUiTableComponent {
+
+    private static final String VIEW_ID = "sampleTable";
+    private static final String VIEW_TEXT = "Sample Table";
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected UiExtensionService uiExtensionService;
+
+    // List of application views
+    private final List<UiView> uiViews = ImmutableList.of(
+            new UiView(UiView.Category.OTHER, VIEW_ID, VIEW_TEXT)
+    );
+
+    // Factory for UI message handlers
+    private final UiMessageHandlerFactory messageHandlerFactory =
+            () -> ImmutableList.of(
+                    new AppUiTableMessageHandler()
+            );
+
+    // Application UI extension
+    protected UiExtension extension =
+            new UiExtension.Builder(getClass().getClassLoader(), uiViews)
+                    .resourcePath(VIEW_ID)
+                    .messageHandlerFactory(messageHandlerFactory)
+                    .build();
+
+    @Activate
+    protected void activate() {
+        uiExtensionService.register(extension);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        uiExtensionService.unregister(extension);
+        log.info("Stopped");
+    }
+
+}
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableMessageHandler.java b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableMessageHandler.java
new file mode 100644 (file)
index 0000000..a673f1f
--- /dev/null
@@ -0,0 +1,190 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ * Copyright 2014,2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ${package};
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.TableModel;
+import org.onosproject.ui.table.TableRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.Override;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Skeletal ONOS UI Table-View message handler.
+ */
+public class AppUiTableMessageHandler extends UiMessageHandler {
+
+    private static final String SAMPLE_TABLE_DATA_REQ = "sampleTableDataRequest";
+    private static final String SAMPLE_TABLE_DATA_RESP = "sampleTableDataResponse";
+    private static final String SAMPLE_TABLES = "sampleTables";
+
+    private static final String SAMPLE_TABLE_DETAIL_REQ = "sampleTableDetailsRequest";
+    private static final String SAMPLE_TABLE_DETAIL_RESP = "sampleTableDetailsResponse";
+    private static final String DETAILS = "details";
+
+    private static final String ID = "id";
+    private static final String LABEL = "label";
+    private static final String CODE = "code";
+    private static final String COMMENT = "comment";
+    private static final String RESULT = "result";
+
+    private static final String[] COLUMN_IDS = { ID, LABEL, CODE };
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+
+    @Override
+    protected Collection<RequestHandler> createRequestHandlers() {
+        return ImmutableSet.of(
+                new SampleTableDataRequestHandler(),
+                new SampleTableDetailRequestHandler()
+        );
+    }
+
+    // handler for sample table requests
+    private final class SampleTableDataRequestHandler extends TableRequestHandler {
+
+        private SampleTableDataRequestHandler() {
+            super(SAMPLE_TABLE_DATA_REQ, SAMPLE_TABLE_DATA_RESP, SAMPLE_TABLES);
+        }
+
+        // if necessary, override defaultColumnId() -- if it isn't "id"
+
+        @Override
+        protected String[] getColumnIds() {
+            return COLUMN_IDS;
+        }
+
+        // if required, override createTableModel() to set column formatters / comparators
+
+        @Override
+        protected void populateTable(TableModel tm, ObjectNode payload) {
+            // === NOTE: the table model supplied here will have been created
+            // via  a call to createTableModel(). To assign non-default
+            // cell formatters or comparators to the table model, override
+            // createTableModel() and set them there.
+
+            // === retrieve table row items from some service...
+            // SomeService ss = get(SomeService.class);
+            // List<Item> items = ss.getItems()
+
+            // fake data for demonstration purposes...
+            List<Item> items = getItems();
+            for (Item item: items) {
+                populateRow(tm.addRow(), item);
+            }
+        }
+
+        private void populateRow(TableModel.Row row, Item item) {
+            row.cell(ID, item.id())
+                    .cell(LABEL, item.label())
+                    .cell(CODE, item.code());
+        }
+    }
+
+
+    // handler for sample item details requests
+    private final class SampleTableDetailRequestHandler extends RequestHandler {
+
+        private SampleTableDetailRequestHandler() {
+            super(SAMPLE_TABLE_DETAIL_REQ);
+        }
+
+        @Override
+        public void process(long sid, ObjectNode payload) {
+            String id = string(payload, ID, "(none)");
+
+            // SomeService ss = get(SomeService.class);
+            // Item item = ss.getItemDetails(id)
+
+            // fake data for demonstration purposes...
+            Item item = getItem(id);
+
+            ObjectNode rootNode = objectNode();
+            ObjectNode data = objectNode();
+            rootNode.set(DETAILS, data);
+
+            if (item == null) {
+                rootNode.put(RESULT, "Item with id '" + id + "' not found");
+                log.warn("attempted to get item detail for id '{}'", id);
+
+            } else {
+                rootNode.put(RESULT, "Found item with id '" + id + "'");
+
+                data.put(ID, item.id());
+                data.put(LABEL, item.label());
+                data.put(CODE, item.code());
+                data.put(COMMENT, "Some arbitrary comment");
+            }
+
+            sendMessage(SAMPLE_TABLE_DETAIL_RESP, 0, rootNode);
+        }
+    }
+
+
+    // ===================================================================
+    // NOTE: The code below this line is to create fake data for this
+    //       sample code. Normally you would use existing services to
+    //       provide real data.
+
+    // Lookup a single item.
+    private static Item getItem(String id) {
+        // We realize this code is really inefficient, but
+        // it suffices for our purposes of demonstration...
+        for (Item item : getItems()) {
+            if (item.id().equals(id)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    // Produce a list of items.
+    private static List<Item> getItems() {
+        List<Item> items = new ArrayList<>();
+        items.add(new Item("item-1", "foo", 42));
+        items.add(new Item("item-2", "bar", 99));
+        items.add(new Item("item-3", "baz", 65));
+        return items;
+    }
+
+    // Simple model class to provide sample data
+    private static class Item {
+        private final String id;
+        private final String label;
+        private final int code;
+
+        Item(String id, String label, int code) {
+            this.id = id;
+            this.label = label;
+            this.code = code;
+        }
+
+        String id() { return id; }
+        String label() { return label; }
+        int code() { return code; }
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.css b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.css
new file mode 100644 (file)
index 0000000..5eb551b
--- /dev/null
@@ -0,0 +1,35 @@
+/* css for sample table view */
+
+#ov-sample-table h2 {
+    display: inline-block;
+}
+
+/* Panel Styling */
+#ov-sample-table-item-details-panel.floatpanel {
+    position: absolute;
+    top: 115px;
+}
+
+.light #ov-sample-table-item-details-panel.floatpanel {
+    background-color: rgb(229, 234, 237);
+}
+.dark #ov-sample-table-item-details-panel.floatpanel {
+    background-color: #3A4042;
+}
+
+#ov-sample-table-item-details-panel h3 {
+    margin: 0;
+    font-size: large;
+}
+
+#ov-sample-table-item-details-panel h4 {
+    margin: 0;
+}
+
+#ov-sample-table-item-details-panel td {
+    padding: 5px;
+}
+#ov-sample-table-item-details-panel td.label {
+    font-style: italic;
+    opacity: 0.8;
+}
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.html b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.html
new file mode 100644 (file)
index 0000000..e20a94d
--- /dev/null
@@ -0,0 +1,46 @@
+<!-- partial HTML -->
+<div id="ov-sample-table">
+    <div class="tabular-header">
+        <h2>Items ({{tableData.length}} total)</h2>
+        <div class="ctrl-btns">
+            <div class="refresh" ng-class="{active: autoRefresh}"
+                 icon icon-id="refresh" icon-size="36"
+                 tooltip tt-msg="autoRefreshTip"
+                 ng-click="toggleRefresh()"></div>
+        </div>
+    </div>
+
+    <div class="summary-list" onos-table-resize>
+
+        <div class="table-header" onos-sortable-header>
+            <table>
+                <tr>
+                    <td colId="id" sortable>Item ID </td>
+                    <td colId="label" sortable>Label </td>
+                    <td colId="code" sortable>Code </td>
+                </tr>
+            </table>
+        </div>
+
+        <div class="table-body">
+            <table>
+                <tr ng-if="!tableData.length" class="no-data">
+                    <td colspan="3">
+                        No Items found
+                    </td>
+                </tr>
+
+                <tr ng-repeat="item in tableData track by $index"
+                    ng-click="selectCallback($event, item)"
+                    ng-class="{selected: item.id === selId}">
+                    <td>{{item.id}}</td>
+                    <td>{{item.label}}</td>
+                    <td>{{item.code}}</td>
+                </tr>
+            </table>
+        </div>
+
+    </div>
+
+    <ov-sample-table-item-details-panel></ov-sample-table-item-details-panel>
+</div>
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.js b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.js
new file mode 100644 (file)
index 0000000..7b92555
--- /dev/null
@@ -0,0 +1,141 @@
+// js for sample app table view
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, $scope, fs, wss;
+
+    // constants
+    var detailsReq = 'sampleTableDetailsRequest',
+        detailsResp = 'sampleTableDetailsResponse',
+        pName = 'ov-sample-table-item-details-panel',
+
+        propOrder = ['id', 'label', 'code'],
+        friendlyProps = ['Item ID', 'Item Label', 'Special Code'];
+
+
+    function addProp(tbody, index, value) {
+        var tr = tbody.append('tr');
+
+        function addCell(cls, txt) {
+            tr.append('td').attr('class', cls).html(txt);
+        }
+        addCell('label', friendlyProps[index] + ' :');
+        addCell('value', value);
+    }
+
+    function populatePanel(panel) {
+        var title = panel.append('h3'),
+            tbody = panel.append('table').append('tbody');
+
+        title.text('Item Details');
+
+        propOrder.forEach(function (prop, i) {
+            addProp(tbody, i, $scope.panelDetails[prop]);
+        });
+
+        panel.append('hr');
+        panel.append('h4').text('Comments');
+        panel.append('p').text($scope.panelDetails.comment);
+    }
+
+    function respDetailsCb(data) {
+        $scope.panelDetails = data.details;
+        $scope.$apply();
+    }
+
+    angular.module('ovSampleTable', [])
+        .controller('OvSampleTableCtrl',
+        ['$log', '$scope', 'TableBuilderService',
+            'FnService', 'WebSocketService',
+
+            function (_$log_, _$scope_, tbs, _fs_, _wss_) {
+                $log = _$log_;
+                $scope = _$scope_;
+                fs = _fs_;
+                wss = _wss_;
+
+                var handlers = {};
+                $scope.panelDetails = {};
+
+                // details response handler
+                handlers[detailsResp] = respDetailsCb;
+                wss.bindHandlers(handlers);
+
+                // custom selection callback
+                function selCb($event, row) {
+                    if ($scope.selId) {
+                        wss.sendEvent(detailsReq, { id: row.id });
+                    } else {
+                        $scope.hidePanel();
+                    }
+                    $log.debug('Got a click on:', row);
+                }
+
+                // TableBuilderService creating a table for us
+                tbs.buildTable({
+                    scope: $scope,
+                    tag: 'sampleTable',
+                    selCb: selCb
+                });
+
+                // cleanup
+                $scope.$on('$destroy', function () {
+                    wss.unbindHandlers(handlers);
+                    $log.log('OvSampleTableCtrl has been destroyed');
+                });
+
+                $log.log('OvSampleTableCtrl has been created');
+            }])
+
+        .directive('ovSampleTableItemDetailsPanel', ['PanelService', 'KeyService',
+            function (ps, ks) {
+            return {
+                restrict: 'E',
+                link: function (scope, element, attrs) {
+                    // insert details panel with PanelService
+                    // create the panel
+                    var panel = ps.createPanel(pName, {
+                        width: 200,
+                        margin: 20,
+                        hideMargin: 0
+                    });
+                    panel.hide();
+                    scope.hidePanel = function () { panel.hide(); };
+
+                    function closePanel() {
+                        if (panel.isVisible()) {
+                            $scope.selId = null;
+                            panel.hide();
+                            return true;
+                        }
+                        return false;
+                    }
+
+                    // create key bindings to handle panel
+                    ks.keyBindings({
+                        esc: [closePanel, 'Close the details panel'],
+                        _helpFormat: ['esc']
+                    });
+                    ks.gestureNotes([
+                        ['click', 'Select a row to show item details']
+                    ]);
+
+                    // update the panel's contents when the data is changed
+                    scope.$watch('panelDetails', function () {
+                        if (!fs.isEmptyObject(scope.panelDetails)) {
+                            panel.empty();
+                            populatePanel(panel);
+                            panel.show();
+                        }
+                    });
+
+                    // cleanup on destroyed scope
+                    scope.$on('$destroy', function () {
+                        ks.unbindKeys();
+                        ps.destroyPanel(pName);
+                    });
+                }
+            };
+        }]);
+}());
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/css.html b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/css.html
new file mode 100644 (file)
index 0000000..26112b0
--- /dev/null
@@ -0,0 +1 @@
+<link rel="stylesheet" href="app/view/sampleTable/sampleTable.css">
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/js.html b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/js.html
new file mode 100644 (file)
index 0000000..4bfa216
--- /dev/null
@@ -0,0 +1 @@
+<script src="app/view/sampleTable/sampleTable.js"></script>
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/archetype.properties b/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/archetype.properties
new file mode 100644 (file)
index 0000000..a1213b4
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# Copyright 2014 Open Networking Laboratory
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Thu Dec 04 09:24:50 PST 2014
+package=it.pkg
+version=0.1-SNAPSHOT
+groupId=archetype.it
+artifactId=basic
diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/goal.txt b/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/goal.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/pom.xml b/framework/src/onos/tools/package/archetypes/uitopo/pom.xml
new file mode 100644 (file)
index 0000000..6ed7c87
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.onosproject</groupId>
+    <artifactId>onos-archetypes</artifactId>
+    <version>1.4.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>onos-uitopo-archetype</artifactId>
+  <packaging>maven-archetype</packaging>
+
+  <description>ONOS UI Topology-Overlay overlay archetype</description>
+
+</project>
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/META-INF/maven/archetype-metadata.xml b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644 (file)
index 0000000..26a9082
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<archetype-descriptor
+        xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
+        name="onos-uitopo" partial="true"
+        xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <fileSets>
+        <fileSet filtered="true" packaged="true" encoding="UTF-8">
+            <directory>src/main/java</directory>
+            <includes>
+                <include>**/*.java</include>
+            </includes>
+        </fileSet>
+        <fileSet filtered="true" packaged="false" encoding="UTF-8">
+            <directory>src/main/resources</directory>
+            <includes>
+                <include>**/*.html</include>
+                <include>**/*.js</include>
+                <include>**/*.css</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</archetype-descriptor>
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/pom.xml b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/pom.xml
new file mode 100644 (file)
index 0000000..f0688ec
--- /dev/null
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>${groupId}</groupId>
+    <artifactId>${artifactId}</artifactId>
+    <version>${version}</version>
+    <packaging>bundle</packaging>
+
+    <description>ONOS OSGi UI Topology-Overlay bundle archetype</description>
+    <url>http://onosproject.org</url>
+
+    <properties>
+        <onos.version>1.4.0-SNAPSHOT</onos.version>
+        <!-- Uncomment to generate ONOS app from this module.
+        <onos.app.name>org.foo.app</onos.app.name>
+        <onos.app.origin>Foo, Inc.</onos.app.origin>
+        -->
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <version>${onos.version}</version>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <version>1.9.8</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.5.3</version>
+                <extensions>true</extensions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.5.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+                <version>1.20.0</version>
+                <executions>
+                    <execution>
+                        <id>generate-scr-srcdescriptor</id>
+                        <goals>
+                            <goal>scr</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <supportedProjectTypes>
+                        <supportedProjectType>bundle</supportedProjectType>
+                        <supportedProjectType>war</supportedProjectType>
+                    </supportedProjectTypes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-maven-plugin</artifactId>
+                <version>1.5</version>
+                <executions>
+                    <execution>
+                        <id>cfg</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>cfg</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>swagger</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>swagger</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>app</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>app</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovComponent.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovComponent.java
new file mode 100644 (file)
index 0000000..fa62a78
--- /dev/null
@@ -0,0 +1,89 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ * Copyright 2014,2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ${package};
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiTopoOverlayFactory;
+import org.onosproject.ui.UiView;
+import org.onosproject.ui.UiViewHidden;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Skeletal ONOS UI Topology-Overlay application component.
+ */
+@Component(immediate = true)
+public class AppUiTopovComponent {
+
+    private static final ClassLoader CL = AppUiTopovComponent.class.getClassLoader();
+    private static final String VIEW_ID = "sampleTopov";
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected UiExtensionService uiExtensionService;
+
+    // List of application views
+    private final List<UiView> uiViews = ImmutableList.of(
+            new UiViewHidden(VIEW_ID)
+    );
+
+    // Factory for UI message handlers
+    private final UiMessageHandlerFactory messageHandlerFactory =
+            () -> ImmutableList.of(
+                    new AppUiTopovMessageHandler()
+            );
+
+    // Factory for UI topology overlays
+    private final UiTopoOverlayFactory topoOverlayFactory =
+            () -> ImmutableList.of(
+                    new AppUiTopovOverlay()
+            );
+
+    // Application UI extension
+    protected UiExtension extension =
+            new UiExtension.Builder(CL, uiViews)
+                    .resourcePath(VIEW_ID)
+                    .messageHandlerFactory(messageHandlerFactory)
+                    .topoOverlayFactory(topoOverlayFactory)
+                    .build();
+
+    @Activate
+    protected void activate() {
+        uiExtensionService.register(extension);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        uiExtensionService.unregister(extension);
+        log.info("Stopped");
+    }
+
+}
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovMessageHandler.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovMessageHandler.java
new file mode 100644 (file)
index 0000000..fe28186
--- /dev/null
@@ -0,0 +1,319 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ * Copyright 2014,2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ${package};
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Element;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiConnection;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.topo.Highlights;
+import org.onosproject.ui.topo.TopoJson;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Skeletal ONOS UI Topology-Overlay message handler.
+ */
+public class AppUiTopovMessageHandler extends UiMessageHandler {
+
+    private static final String SAMPLE_DISPLAY_START = "sampleDisplayStart";
+    private static final String SAMPLE_DISPLAY_UPDATE = "sampleDisplayUpdate";
+    private static final String SAMPLE_DISPLAY_STOP = "sampleDisplayStop";
+
+    private static final String ID = "id";
+    private static final String MODE = "mode";
+
+    private static final long UPDATE_PERIOD_MS = 1000;
+
+    private static final Link[] EMPTY_LINK_SET = new Link[0];
+
+    private enum Mode { IDLE, MOUSE, LINK }
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private DeviceService deviceService;
+    private HostService hostService;
+    private LinkService linkService;
+
+    private final Timer timer = new Timer("sample-overlay");
+    private TimerTask demoTask = null;
+    private Mode currentMode = Mode.IDLE;
+    private Element elementOfNote;
+    private Link[] linkSet = EMPTY_LINK_SET;
+    private int linkIndex;
+
+
+    // ===============-=-=-=-=-=-======================-=-=-=-=-=-=-================================
+
+
+    @Override
+    public void init(UiConnection connection, ServiceDirectory directory) {
+        super.init(connection, directory);
+        deviceService = directory.get(DeviceService.class);
+        hostService = directory.get(HostService.class);
+        linkService = directory.get(LinkService.class);
+    }
+
+    @Override
+    protected Collection<RequestHandler> createRequestHandlers() {
+        return ImmutableSet.of(
+                new DisplayStartHandler(),
+                new DisplayUpdateHandler(),
+                new DisplayStopHandler()
+        );
+    }
+
+    // === -------------------------
+    // === Handler classes
+
+    private final class DisplayStartHandler extends RequestHandler {
+        public DisplayStartHandler() {
+            super(SAMPLE_DISPLAY_START);
+        }
+
+        @Override
+        public void process(long sid, ObjectNode payload) {
+            String mode = string(payload, MODE);
+
+            log.debug("Start Display: mode [{}]", mode);
+            clearState();
+            clearForMode();
+
+            switch (mode) {
+                case "mouse":
+                    currentMode = Mode.MOUSE;
+                    cancelTask();
+                    sendMouseData();
+                    break;
+
+                case "link":
+                    currentMode = Mode.LINK;
+                    scheduleTask();
+                    initLinkSet();
+                    sendLinkData();
+                    break;
+
+                default:
+                    currentMode = Mode.IDLE;
+                    cancelTask();
+                    break;
+            }
+        }
+    }
+
+    private final class DisplayUpdateHandler extends RequestHandler {
+        public DisplayUpdateHandler() {
+            super(SAMPLE_DISPLAY_UPDATE);
+        }
+
+        @Override
+        public void process(long sid, ObjectNode payload) {
+            String id = string(payload, ID);
+            log.debug("Update Display: id [{}]", id);
+            if (!Strings.isNullOrEmpty(id)) {
+                updateForMode(id);
+            } else {
+                clearForMode();
+            }
+        }
+    }
+
+    private final class DisplayStopHandler extends RequestHandler {
+        public DisplayStopHandler() {
+            super(SAMPLE_DISPLAY_STOP);
+        }
+
+        @Override
+        public void process(long sid, ObjectNode payload) {
+            log.debug("Stop Display");
+            cancelTask();
+            clearState();
+            clearForMode();
+        }
+    }
+
+    // === ------------
+
+    private void clearState() {
+        currentMode = Mode.IDLE;
+        elementOfNote = null;
+        linkSet = EMPTY_LINK_SET;
+    }
+
+    private void updateForMode(String id) {
+        log.debug("host service: {}", hostService);
+        log.debug("device service: {}", deviceService);
+
+        try {
+            HostId hid = HostId.hostId(id);
+            log.debug("host id {}", hid);
+            elementOfNote = hostService.getHost(hid);
+            log.debug("host element {}", elementOfNote);
+
+        } catch (Exception e) {
+            try {
+                DeviceId did = DeviceId.deviceId(id);
+                log.debug("device id {}", did);
+                elementOfNote = deviceService.getDevice(did);
+                log.debug("device element {}", elementOfNote);
+
+            } catch (Exception e2) {
+                log.debug("Unable to process ID [{}]", id);
+                elementOfNote = null;
+            }
+        }
+
+        switch (currentMode) {
+            case MOUSE:
+                sendMouseData();
+                break;
+
+            case LINK:
+                sendLinkData();
+                break;
+
+            default:
+                break;
+        }
+
+    }
+
+    private void clearForMode() {
+        sendHighlights(new Highlights());
+    }
+
+    private void sendHighlights(Highlights highlights) {
+        sendMessage(TopoJson.highlightsMessage(highlights));
+    }
+
+
+    private void sendMouseData() {
+        if (elementOfNote != null && elementOfNote instanceof Device) {
+            DeviceId devId = (DeviceId) elementOfNote.id();
+            Set<Link> links = linkService.getDeviceEgressLinks(devId);
+            sendHighlights(fromLinks(links, devId));
+        }
+        // Note: could also process Host, if available
+    }
+
+    private Highlights fromLinks(Set<Link> links, DeviceId devId) {
+        DemoLinkMap linkMap = new DemoLinkMap();
+        if (links != null) {
+            log.debug("Processing {} links", links.size());
+            links.forEach(linkMap::add);
+        } else {
+            log.debug("No egress links found for device {}", devId);
+        }
+
+        Highlights highlights = new Highlights();
+
+        for (DemoLink dlink : linkMap.biLinks()) {
+            dlink.makeImportant().setLabel("Yo!");
+            highlights.add(dlink.highlight(null));
+        }
+        return highlights;
+    }
+
+    private void initLinkSet() {
+        Set<Link> links = new HashSet<>();
+        for (Link link : linkService.getActiveLinks()) {
+            links.add(link);
+        }
+        linkSet = links.toArray(new Link[links.size()]);
+        linkIndex = 0;
+        log.debug("initialized link set to {}", linkSet.length);
+    }
+
+    private void sendLinkData() {
+        DemoLinkMap linkMap = new DemoLinkMap();
+        for (Link link : linkSet) {
+            linkMap.add(link);
+        }
+        DemoLink dl = linkMap.add(linkSet[linkIndex]);
+        dl.makeImportant().setLabel(Integer.toString(linkIndex));
+        log.debug("sending link data (index {})", linkIndex);
+
+        linkIndex += 1;
+        if (linkIndex >= linkSet.length) {
+            linkIndex = 0;
+        }
+
+        Highlights highlights = new Highlights();
+        for (DemoLink dlink : linkMap.biLinks()) {
+            highlights.add(dlink.highlight(null));
+        }
+
+        sendHighlights(highlights);
+    }
+
+    private synchronized void scheduleTask() {
+        if (demoTask == null) {
+            log.debug("Starting up demo task...");
+            demoTask = new DisplayUpdateTask();
+            timer.schedule(demoTask, UPDATE_PERIOD_MS, UPDATE_PERIOD_MS);
+        } else {
+            log.debug("(demo task already running");
+        }
+    }
+
+    private synchronized void cancelTask() {
+        if (demoTask != null) {
+            demoTask.cancel();
+            demoTask = null;
+        }
+    }
+
+
+    private class DisplayUpdateTask extends TimerTask {
+        @Override
+        public void run() {
+            try {
+                switch (currentMode) {
+                    case LINK:
+                        sendLinkData();
+                        break;
+
+                    default:
+                        break;
+                }
+            } catch (Exception e) {
+                log.warn("Unable to process demo task: {}", e.getMessage());
+                log.debug("Oops", e);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovOverlay.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovOverlay.java
new file mode 100644 (file)
index 0000000..9899982
--- /dev/null
@@ -0,0 +1,75 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ * Copyright 2014,2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ${package};
+
+import org.onosproject.ui.UiTopoOverlay;
+import org.onosproject.ui.topo.ButtonId;
+import org.onosproject.ui.topo.PropertyPanel;
+import org.onosproject.ui.topo.TopoConstants.CoreButtons;
+import org.onosproject.ui.topo.TopoConstants.Glyphs;
+
+import static org.onosproject.ui.topo.TopoConstants.Properties.*;
+
+/**
+ * Our topology overlay.
+ */
+public class AppUiTopovOverlay extends UiTopoOverlay {
+
+    // NOTE: this must match the ID defined in sampleTopov.js
+    private static final String OVERLAY_ID = "meowster-overlay";
+
+    private static final String MY_TITLE = "My App Rocks!";
+    private static final String MY_VERSION = "Beta-1.0.0042";
+    private static final String MY_DEVICE_TITLE = "I changed the title";
+
+    private static final ButtonId FOO_BUTTON = new ButtonId("foo");
+    private static final ButtonId BAR_BUTTON = new ButtonId("bar");
+
+    public AppUiTopovOverlay() {
+        super(OVERLAY_ID);
+    }
+
+
+    @Override
+    public void modifySummary(PropertyPanel pp) {
+        pp.title(MY_TITLE)
+                .typeId(Glyphs.CROWN)
+                .removeProps(
+                        TOPOLOGY_SSCS,
+                        INTENTS,
+                        TUNNELS,
+                        FLOWS,
+                        VERSION
+                )
+                .addProp(VERSION, MY_VERSION);
+    }
+
+    @Override
+    public void modifyDeviceDetails(PropertyPanel pp) {
+        pp.title(MY_DEVICE_TITLE);
+        pp.removeProps(LATITUDE, LONGITUDE);
+
+        pp.addButton(FOO_BUTTON)
+                .addButton(BAR_BUTTON);
+
+        pp.removeButtons(CoreButtons.SHOW_PORT_VIEW)
+                .removeButtons(CoreButtons.SHOW_GROUP_VIEW);
+    }
+
+}
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLink.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLink.java
new file mode 100644 (file)
index 0000000..4a97f7b
--- /dev/null
@@ -0,0 +1,57 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ * Copyright 2014,2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ${package};
+
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.ui.topo.BiLink;
+import org.onosproject.ui.topo.LinkHighlight;
+import org.onosproject.ui.topo.LinkHighlight.Flavor;
+
+/**
+ * Our demo concrete class of a bi-link. We give it state so we can decide
+ * how to create link highlights.
+ */
+public class DemoLink extends BiLink {
+
+    private boolean important = false;
+    private String label = null;
+
+    public DemoLink(LinkKey key, Link link) {
+        super(key, link);
+    }
+
+    public DemoLink makeImportant() {
+        important = true;
+        return this;
+    }
+
+    public DemoLink setLabel(String label) {
+        this.label = label;
+        return this;
+    }
+
+    @Override
+    public LinkHighlight highlight(Enum<?> anEnum) {
+        Flavor flavor = important ? Flavor.PRIMARY_HIGHLIGHT
+                : Flavor.SECONDARY_HIGHLIGHT;
+        return new LinkHighlight(this.linkId(), flavor)
+                .setLabel(label);
+    }
+}
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLinkMap.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLinkMap.java
new file mode 100644 (file)
index 0000000..cc13d99
--- /dev/null
@@ -0,0 +1,33 @@
+#set( $symbol_pound = '#' )
+#set( $symbol_dollar = '$' )
+#set( $symbol_escape = '\' )
+/*
+ * Copyright 2014,2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ${package};
+
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.ui.topo.BiLinkMap;
+
+/**
+ * Our concrete link map.
+ */
+public class DemoLinkMap extends BiLinkMap<DemoLink> {
+    @Override
+    protected DemoLink create(LinkKey linkKey, Link link) {
+        return new DemoLink(linkKey, link);
+    }
+}
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.css b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.css
new file mode 100644 (file)
index 0000000..cbf460f
--- /dev/null
@@ -0,0 +1,2 @@
+/* css for sample app topology overlay  */
+
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.html b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.html
new file mode 100644 (file)
index 0000000..b1c9acb
--- /dev/null
@@ -0,0 +1,4 @@
+<!-- partial HTML -->
+<div id="ov-sample-topov">
+    <p>This is a hidden view .. just a placeholder to house the javascript</p>
+</div>
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovDemo.js b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovDemo.js
new file mode 100644 (file)
index 0000000..0b82d81
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Sample Demo module. This contains the "business logic" for the topology
+ overlay that we are implementing.
+ */
+
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, fs, flash, wss;
+
+    // constants
+    var displayStart = 'sampleDisplayStart',
+        displayUpdate = 'sampleDisplayUpdate',
+        displayStop = 'sampleDisplayStop';
+
+    // internal state
+    var currentMode = null;
+
+
+    // === ---------------------------
+    // === Helper functions
+
+    function sendDisplayStart(mode) {
+        wss.sendEvent(displayStart, {
+            mode: mode
+        });
+    }
+
+    function sendDisplayUpdate(what) {
+        wss.sendEvent(displayUpdate, {
+            id: what ? what.id : ''
+        });
+    }
+
+    function sendDisplayStop() {
+        wss.sendEvent(displayStop);
+    }
+
+    // === ---------------------------
+    // === Main API functions
+
+    function startDisplay(mode) {
+        if (currentMode === mode) {
+            $log.debug('(in mode', mode, 'already)');
+        } else {
+            currentMode = mode;
+            sendDisplayStart(mode);
+            flash.flash('Starting display mode: ' + mode);
+        }
+    }
+
+    function updateDisplay(m) {
+        if (currentMode) {
+            sendDisplayUpdate(m);
+        }
+    }
+
+    function stopDisplay() {
+        if (currentMode) {
+            currentMode = null;
+            sendDisplayStop();
+            flash.flash('Canceling display mode');
+            return true;
+        }
+        return false;
+    }
+
+    // === ---------------------------
+    // === Module Factory Definition
+
+    angular.module('ovSampleTopov', [])
+        .factory('SampleTopovDemoService',
+        ['$log', 'FnService', 'FlashService', 'WebSocketService',
+
+        function (_$log_, _fs_, _flash_, _wss_) {
+            $log = _$log_;
+            fs = _fs_;
+            flash = _flash_;
+            wss = _wss_;
+
+            return {
+                startDisplay: startDisplay,
+                updateDisplay: updateDisplay,
+                stopDisplay: stopDisplay
+            };
+        }]);
+}());
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovOverlay.js b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovOverlay.js
new file mode 100644 (file)
index 0000000..12875e1
--- /dev/null
@@ -0,0 +1,143 @@
+// sample topology overlay - client side
+//
+// This is the glue that binds our business logic (in sampleTopovDemo.js)
+// to the overlay framework.
+
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, tov, stds;
+
+    // internal state should be kept in the service module (not here)
+
+    // our overlay definition
+    var overlay = {
+        // NOTE: this must match the ID defined in AppUiTopoOverlay
+        overlayId: 'meowster-overlay',
+        glyphId: '*star4',
+        tooltip: 'Sample Meowster Topo Overlay',
+
+        // These glyphs get installed using the overlayId as a prefix.
+        // e.g. 'star4' is installed as 'meowster-overlay-star4'
+        // They can be referenced (from this overlay) as '*star4'
+        // That is, the '*' prefix stands in for 'meowster-overlay-'
+        glyphs: {
+            star4: {
+                vb: '0 0 8 8',
+                d: 'M1,4l2,-1l1,-2l1,2l2,1l-2,1l-1,2l-1,-2z'
+            },
+            banner: {
+                vb: '0 0 6 6',
+                d: 'M1,1v4l2,-2l2,2v-4z'
+            }
+        },
+
+        activate: function () {
+            $log.debug("Sample topology overlay ACTIVATED");
+        },
+        deactivate: function () {
+            stds.stopDisplay();
+            $log.debug("Sample topology overlay DEACTIVATED");
+        },
+
+        // detail panel button definitions
+        buttons: {
+            foo: {
+                gid: 'chain',
+                tt: 'A FOO action',
+                cb: function (data) {
+                    $log.debug('FOO action invoked with data:', data);
+                }
+            },
+            bar: {
+                gid: '*banner',
+                tt: 'A BAR action',
+                cb: function (data) {
+                    $log.debug('BAR action invoked with data:', data);
+                }
+            }
+        },
+
+        // Key bindings for traffic overlay buttons
+        // NOTE: fully qual. button ID is derived from overlay-id and key-name
+        keyBindings: {
+            0: {
+                cb: function () { stds.stopDisplay(); },
+                tt: 'Cancel Display Mode',
+                gid: 'xMark'
+            },
+            V: {
+                cb: function () { stds.startDisplay('mouse'); },
+                tt: 'Start Mouse Mode',
+                gid: '*banner'
+            },
+            F: {
+                cb: function () { stds.startDisplay('link'); },
+                tt: 'Start Link Mode',
+                gid: 'chain'
+            },
+            G: {
+                cb: buttonCallback,
+                tt: 'Uses the G key',
+                gid: 'crown'
+            },
+
+            _keyOrder: [
+                '0', 'V', 'F', 'G'
+            ]
+        },
+
+        hooks: {
+            // hook for handling escape key
+            // Must return true to consume ESC, false otherwise.
+            escape: function () {
+                // Must return true to consume ESC, false otherwise.
+                return stds.stopDisplay();
+            },
+
+            // hooks for when the selection changes...
+            empty: function () {
+                selectionCallback('empty');
+            },
+            single: function (data) {
+                selectionCallback('single', data);
+            },
+            multi: function (selectOrder) {
+                selectionCallback('multi', selectOrder);
+                tov.addDetailButton('foo');
+                tov.addDetailButton('bar');
+            },
+            mouseover: function (m) {
+                // m has id, class, and type properties
+                $log.debug('mouseover:', m);
+                stds.updateDisplay(m);
+            },
+            mouseout: function () {
+                $log.debug('mouseout');
+                stds.updateDisplay();
+            }
+        }
+    };
+
+
+    function buttonCallback(x) {
+        $log.debug('Toolbar-button callback', x);
+    }
+
+    function selectionCallback(x, d) {
+        $log.debug('Selection callback', x, d);
+    }
+
+    // invoke code to register with the overlay service
+    angular.module('ovSampleTopov')
+        .run(['$log', 'TopoOverlayService', 'SampleTopovDemoService',
+
+        function (_$log_, _tov_, _stds_) {
+            $log = _$log_;
+            tov = _tov_;
+            stds = _stds_;
+            tov.register(overlay);
+        }]);
+
+}());
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/css.html b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/css.html
new file mode 100644 (file)
index 0000000..0ed6f53
--- /dev/null
@@ -0,0 +1 @@
+<link rel="stylesheet" href="app/view/sampleTopov/sampleTopov.css">
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/js.html b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/js.html
new file mode 100644 (file)
index 0000000..4fed1f0
--- /dev/null
@@ -0,0 +1,2 @@
+<script src="app/view/sampleTopov/sampleTopovDemo.js"></script>
+<script src="app/view/sampleTopov/sampleTopovOverlay.js"></script>
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/archetype.properties b/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/archetype.properties
new file mode 100644 (file)
index 0000000..a1213b4
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# Copyright 2014 Open Networking Laboratory
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Thu Dec 04 09:24:50 PST 2014
+package=it.pkg
+version=0.1-SNAPSHOT
+groupId=archetype.it
+artifactId=basic
diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/goal.txt b/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/goal.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/onos/tools/package/config/samples/component-cfg.json b/framework/src/onos/tools/package/config/samples/component-cfg.json
new file mode 100644 (file)
index 0000000..f1168e4
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "org.onosproject.proxyarp.ProxyArp": {
+    "ipv6NeighborDiscovery": true
+  }
+}
\ No newline at end of file
diff --git a/framework/src/onos/tools/package/config/samples/network-cfg.json b/framework/src/onos/tools/package/config/samples/network-cfg.json
new file mode 100644 (file)
index 0000000..c2af8b8
--- /dev/null
@@ -0,0 +1,66 @@
+{
+    "ports" : {
+       "of:0000000000000002/1" : {
+           "interfaces" : [
+               {
+                   "ips" : [ "192.168.10.101/24" ],
+                   "mac" : "08:9e:01:82:38:68",
+                   "vlan" : "100"
+               }
+           ]
+       },
+       "of:0000000000000002/20" : {
+           "interfaces" : [
+               {
+                   "ips" : [ "192.168.20.101/24" ],
+                   "mac" : "08:9e:01:82:38:68",
+                   "vlan" : "200"
+               }
+           ]
+       }
+    },
+    "devices" : {
+       "of:0000000000000002" : {
+               "segmentrouting" : {
+                "name" : "Leaf-R1",
+                "nodeSid" : 101,
+                "routerIp" : "10.0.1.254",
+                "routerMac" : "00:00:00:00:01:80",
+                "isEdgeRouter" : true,
+                "adjacencySids" : [
+                    { "sid" : 100, "port" : [2, 3] },
+                    { "sid" : 200, "port" : [4, 5] }
+                ]
+            }
+       },
+       "of:0000000000000191" : {
+               "segmentrouting" : {
+                "name" : "Spine-R1",
+                "nodeSid" : 105,
+                "routerIp" : "192.168.0.11",
+                "routerMac" : "00:00:01:00:11:80",
+                "isEdgeRouter" : false
+            }
+       }
+    },
+    "apps" : {
+       "org.onosproject.router" : {
+           "bgp" : {
+               "bgpSpeakers" : [
+                   {
+                       "connectPoint" : "of:00000000000000aa/10",
+                       "peers" : [
+                           "192.168.10.1"
+                       ]
+                   },
+                   {
+                       "connectPoint" : "of:00000000000000aa/20",
+                       "peers" : [
+                           "192.168.20.1"
+                       ]
+                   }
+               ]
+           }
+       }
+    }
+}
index 41842bd..f02b7a8 100644 (file)
@@ -44,7 +44,7 @@ function _cell-opts () {
   fi
 }
 
-complete -F _cell-opts cell
+complete -F _cell-opts cell vicell
 
 
 # Tab completion settings for onos-create-app.
index 89197db..780a90d 100755 (executable)
@@ -3,6 +3,27 @@
 # ONOS remote command-line client.
 # -----------------------------------------------------------------------------
 
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0) [-w] [node]
+
+flags:
+- -w : Waits for ONOS instance to reach run-level 100, i.e. to be fully started.
+
+options:
+- [node] : the node to attach to
+
+summary:
+ ONOS remote command-line client.
+
+ The -w flag depends on 'onos-wait-for-start'. If [node] is unspecified, \$OCI
+ is used.
+
+_EOF_
+}
+[ "$1" = "-h" ] && _usage && exit 0
+
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
 . $ONOS_ROOT/tools/test/bin/find-node.sh
index 7ae0033..331c433 100755 (executable)
@@ -3,6 +3,19 @@
 # Builds a set of projects using ONOS archetypes.
 # -----------------------------------------------------------------------------
 
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0)
+
+summary:
+ Builds a set of projects using ONOS archetypes.
+
+_EOF_
+}
+
+[ "$1" = "-h" ] && _usage && exit 0
+
 set -e
 
 export AROOT=/tmp/foo
index 67864a2..ae28891 100755 (executable)
@@ -3,6 +3,28 @@
 # Executes selected set of ONOS commands using the batch mode.
 # -----------------------------------------------------------------------------
 
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0) [node] <commands>
+
+options:
+- [node] <commands>  : node to run <commands>
+
+summary:
+ Executes selected set of ONOS commands using the batch mode.
+
+ <commands> is a comma-separated list of ONOS CLI commands.
+
+ If [node] isn't specified, the defualt target becomes \$OCI. When no commands
+ are specified, the commands 'summary','intents','flows', and 'hosts' are
+ executed against \$OCI.
+
+_EOF_
+}
+
+[ "$1" = "-h" ] && _usage && exit 0
+
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
 
index 5c3ab02..348cb83 100755 (executable)
@@ -3,6 +3,28 @@
 # Remotely configures & starts ONOS for the first time.
 # -----------------------------------------------------------------------------
 
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0) [node]
+
+options:
+- [node] : The node to configure
+
+summary:
+ Remotely configures and starts ONOS for the first time.
+
+ The procedure for configruing a node include determining base features,
+ applications to load at startup, and clustering and logical network view
+ configurations, among others.
+
+ If [node] isn't specified, the defualt target becomes \$OCI.
+
+_EOF_
+}
+
+[ "$1" = "-h" ] && _usage && exit 0
+
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
 
index 7384c2e..139944e 100755 (executable)
@@ -3,6 +3,32 @@
 # Remotely pushes bits to a remote node and installs ONOS on it.
 # -----------------------------------------------------------------------------
 
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0) [-fn] [-m] <settings> [node]
+
+flags:
+- -f            : forces uninstall of currently installed ONOS
+- -n            : do not copy over onos.conf upstart configuration file.
+- -m <settings> : pass <settings> XML file to remote maven installation
+
+options:
+- [node] : remote node to install ONOS on.
+
+summary:
+ Remotely pushes bits to a remote node and installs ONOS on it.
+
+ The [-n] flag assumes that Upstart is used. The [-f] flag depends on
+ and 'onos-config'.
+
+ If [node] is not specified the default target is \$OCI.
+
+_EOF_
+}
+
+[ "$1" = "-h" ] && _usage && exit 0
+
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
 
index 8c9276f..4d425c6 100755 (executable)
@@ -2,6 +2,24 @@
 # -----------------------------------------------------------------------------
 # Remotely pushes bits to a remote node in preparation for install.
 # -----------------------------------------------------------------------------
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0) [node]
+
+options:
+- [node] : the target node to prime for installation
+
+summary:
+ Remotely pushes bits to a remote node in preparation for install.
+
+ $(basename $0) is invoked as part of 'onos-install', and shouldn't be
+ directly invoked for the most part.
+
+_EOF_
+}
+
+[ "$1" = "-h" ] && _usage && exit 0
 
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
index cc69491..35764e6 100755 (executable)
@@ -34,7 +34,6 @@ fi
 
 case $2 in 
     start|stop|restart|status)
-
         # Select the target
         if [ "${1}" = "--cell" ]; then
             nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
index 5b3cd6f..27cc16c 100755 (executable)
@@ -6,7 +6,7 @@
 controllers=""
 
 for node in $ONOS_INSTANCES; do
-    controllers="$controllers tcp:$node:${OF_PORT:-6633}"
+    controllers="$controllers tcp:$node:${OF_PORT:-6653}"
 done
 
 ssh ${ONOS_USER:-sdn}@$OCN "
index 7a8b9a5..ff8ff53 100755 (executable)
@@ -3,6 +3,24 @@
 # Remotely stops & uninstalls ONOS on the specified node.
 # -----------------------------------------------------------------------------
 
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0) [node]
+
+options:
+- [node] : The remote instance to uninstall ONOS from.
+
+summary:
+ Remotely stops and uninstalls ONOS on the specified node.
+
+ If [node] isn't specified, \$OCI becomes the target.
+
+_EOF_
+}
+
+[ "$1" = "-h" ] && _usage && exit 0
+
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
 
@@ -16,9 +34,12 @@ ssh $remote "
       [ -z \"\$(ps -ef | grep karaf.jar | grep -v grep)\" ] && break
       sleep 1
     done
-    [ -z \"\$(ps -ef | grep karaf.jar | grep -v grep)\" ] || echo 'ONOS failed to stop.'
+    [ -z \"\$(ps -ef | grep karaf.jar | grep -v grep)\" ] || \
+        (echo 'ONOS failed to stop.'; status=1)
 
     # Remove onos directory and init file
-    sudo rm -fr $ONOS_INSTALL_DIR
-    [ -f /etc/init/onos.conf ] && sudo rm /etc/init/onos.conf
+    [ -d $ONOS_INSTALL_DIR ] && sudo rm -fr $ONOS_INSTALL_DIR
+    [ -f /etc/init/onos.conf ] && sudo rm -f /etc/init/onos.conf
+
+    exit \${status:-0};
 "
index 28e88c2..11962f9 100755 (executable)
@@ -2,6 +2,30 @@
 # -----------------------------------------------------------------------------
 # Monitors selected set of ONOS commands using the system watch command.
 # -----------------------------------------------------------------------------
+function _usage () {
+cat << _EOF_
+usage:
+ $(basename $0) [node] <commands> [watchflags]
+
+options:
+- [node] <commands> : the node to run the commands against
+- [watchflags]      : flags to be passed to the watch command.
+
+summary:
+ Monitors selected set of ONOS commands using the system watch command.
+
+ <commands> is a comma-sepatarted list of ONOS CLI commands. If no commands
+ are supplied, the commands run are 'summary', 'intents', 'flows', and
+ 'hosts' against \$OCI.
+
+ Note that [watchflags] only applies to platforms with the Linux-like watch
+ command. For other platforms, the default behavior of watch (refresh every 2
+ s) is emulated.
+
+_EOF_
+}
+
+[ "$1" = "-h" ] && _usage && exit 0
 
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
@@ -14,4 +38,10 @@ aux=/tmp/onos-watch.$$
 trap "rm -f $aux" EXIT
 
 echo "$commands" | tr ',' '\n' > $aux
-watch $3 "onos $node -b <$aux 2>/dev/null"
+
+# emulate watch if not Linux.
+if [ "$(uname)" != "Linux" ]; then
+    while clear; "onos $node -b <$aux 2>/dev/null" ; do sleep 2; done
+else
+    watch $3 "onos $node -b <$aux 2>/dev/null"
+fi
diff --git a/framework/src/onos/tools/test/cells/andrea b/framework/src/onos/tools/test/cells/andrea
new file mode 100644 (file)
index 0000000..1f9f22f
--- /dev/null
@@ -0,0 +1,11 @@
+# Andrea's ProxMox ONOS instances 1,2,3 & ONOS mininet box
+
+export ONOS_NIC="10.128.12.*"
+export OC1="10.128.12.1"
+export OC2="10.128.12.2"
+export OC3="10.128.12.3"
+export OCN="10.128.12.4"
+
+export OCT=$OC1
+export ONOS_USE_SSH=true
+export ONOS_APPS=drivers,openflow,proxyarp,ovsdb
index 3e528ee..91036e6 100644 (file)
@@ -7,4 +7,5 @@ export OC3="10.128.11.3"
 export OCN="10.128.11.4"
 
 export OCT=$OC1
-export ONOS_APPS=drivers,openflow,proxyarp
\ No newline at end of file
+export ONOS_USE_SSH=true
+export ONOS_APPS=drivers,openflow,proxyarp,mobility
\ No newline at end of file
index 8244a32..5440b55 100644 (file)
         <step name="Verify-UI" requires="^"
               exec="onos-check-views ${OCI} id=sample"/>
 
+        <step name="Create-App-UI-Table-Overlay" requires="Reinstall-App-With-UI"
+              exec="onos-create-app uitab org.test.app test-app 1.2.3 org.test.app -DinteractiveMode=false"/>
+        <step name="Build-App-With-UI-Table" requires="^"
+              exec="mvn clean install"/>
+        <step name="Reinstall-App-With-UI-Table" requires="^,~Verify-UI"
+              exec="onos-app ${OCI} reinstall! target/test-app-1.2.3.oar"/>
+        <step name="Verify-UI-Table" requires="^"
+              exec="onos-check-views ${OCI} id=sample"/>
+
+        <step name="Create-App-UI-Topo-Overlay" requires="Reinstall-App-With-UI-Table"
+              exec="onos-create-app uitopo org.test.app test-app 1.2.3 org.test.app -DinteractiveMode=false"/>
+        <step name="Build-App-With-UI-Topo" requires="^"
+              exec="mvn clean install"/>
+        <step name="Reinstall-App-With-UI-Topo" requires="^,~Verify-UI-Table"
+              exec="onos-app ${OCI} reinstall! target/test-app-1.2.3.oar"/>
+        <step name="Verify-UI-Topo" requires="^"
+              exec="onos-check-views ${OCI} id=sample"/>
+
         <step name="Uninstall-App" requires="^"
               exec="onos-app ${OCI} uninstall org.foo.app"/>
     </group>
index ae6045e..f9d4ba2 100755 (executable)
@@ -31,7 +31,7 @@ class ONOS( Controller ):
         Controller.__init__( self, name, **kwargs )
         # the following have been done for us:
         #self.ip = ip ('127.0.0.1')
-        #self.port = port (6633)
+        #self.port = port (6653)
         #self.protocol = protocol ('tcp')
         #self.checkListening()
 
index 36cdbad..b778592 100644 (file)
@@ -69,7 +69,7 @@ topos = {'optical': ( lambda: OpticalTopo() )}
 
 
 def run():
-    c = RemoteController('c','127.0.0.1',6633)
+    c = RemoteController('c','127.0.0.1',6653)
     net = Mininet( topo=OpticalTopo(),controller=None,autoSetMacs=True)
     net.addController(c)
     net.start()
index ea11b66..f316162 100644 (file)
@@ -23,7 +23,7 @@ class Solar(object):
         # We are creating the controller with local-loopback on purpose to avoid
         # having the switches connect immediately. Instead, we'll set controller
         # explicitly for each switch after configuring it as we want.
-        self.ctrls = [ RemoteController(cname, cip, 6633) for cip in cips ]
+        self.ctrls = [ RemoteController(cname, cip, 6653) for cip in cips ]
         self.net = Mininet(controller=RemoteController, switch = OVSKernelSwitch,
                            build=False)
 
index 2b999d3..b3a718d 100644 (file)
@@ -56,7 +56,7 @@
           [{switch,1,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:01"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -66,7 +66,7 @@
            {switch,2,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:02"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -76,7 +76,7 @@
            {switch,3,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:03"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -86,7 +86,7 @@
            {switch,4,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:04"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -96,7 +96,7 @@
            {switch,5,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:05"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,7,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:07"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,8,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:08"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,9,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:09"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,10,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:0A"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,6,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:06"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
index b843146..b1451be 100644 (file)
@@ -18,7 +18,7 @@
           [{switch,1,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:01"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -28,7 +28,7 @@
            {switch,3,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:03"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -37,7 +37,7 @@
            {switch,2,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:02"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -47,7 +47,7 @@
            {switch,4,
               [{backend,linc_us4_oe},
                {datapath_id,"00:00:ff:ff:ff:ff:ff:04"},
-               {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+               {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                {controllers_listener,disabled},
                {queues_status,disabled},
                {ports,
index 2b999d3..b3a718d 100644 (file)
@@ -56,7 +56,7 @@
           [{switch,1,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:01"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -66,7 +66,7 @@
            {switch,2,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:02"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -76,7 +76,7 @@
            {switch,3,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:03"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -86,7 +86,7 @@
            {switch,4,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:04"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
@@ -96,7 +96,7 @@
            {switch,5,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:05"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,7,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:07"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,8,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:08"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,9,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:09"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,10,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:0A"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
            {switch,6,
                [{backend,linc_us4_oe},
                 {datapath_id,"00:00:ff:ff:ff:ff:ff:06"},
-                {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]},
+                {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]},
                 {controllers_listener,disabled},
                 {queues_status,disabled},
                 {ports,
index 1afc494..686a9a5 100644 (file)
@@ -64,27 +64,40 @@ public final class TestUtils {
     /**
      * Gets the field, bypassing scope restriction.
      *
-     * @param subject Object where the field belongs
+     * @param subject   Object where the field belongs
      * @param fieldName name of the field to get
+     * @param <T>       subject type
+     * @param <U>       fieldO value type
      * @return value of the field.
-     * @param <T> subject type
-     * @param <U> field value type
      * @throws TestUtilsException if there are reflection errors while getting
-     * the field
+     *                            the field
      */
     public static <T, U> U getField(T subject, String fieldName)
             throws TestUtilsException {
         try {
+            NoSuchFieldException exception = null;
             @SuppressWarnings("unchecked")
-            Class<T> clazz = (Class<T>) subject.getClass();
-            Field field = clazz.getDeclaredField(fieldName);
-            field.setAccessible(true);
+            Class clazz = subject.getClass();
+            while (clazz != null) {
+                try {
+                    Field field = clazz.getDeclaredField(fieldName);
+                    field.setAccessible(true);
 
-            @SuppressWarnings("unchecked")
-            U result = (U) field.get(subject);
-            return result;
-        } catch (NoSuchFieldException | SecurityException |
-                 IllegalArgumentException | IllegalAccessException e) {
+                    @SuppressWarnings("unchecked")
+                    U result = (U) field.get(subject);
+                    return result;
+                } catch (NoSuchFieldException e) {
+                    exception = e;
+                    if (clazz == clazz.getSuperclass()) {
+                        break;
+                    }
+                    clazz = clazz.getSuperclass();
+                }
+            }
+            throw new TestUtilsException("Field not found. " + fieldName, exception);
+
+        } catch (SecurityException |
+                IllegalArgumentException | IllegalAccessException e) {
             throw new TestUtilsException("getField failed", e);
         }
     }
index b62d3b2..206a34c 100644 (file)
@@ -19,52 +19,67 @@ package org.onlab.graph;
 
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 
-import static com.google.common.collect.ImmutableSet.of;
 import static com.google.common.base.MoreObjects.toStringHelper;
 
-
+/**
+ * Pair of disjoint paths.
+ *
+ * @param <V> type of vertex
+ * @param <E> type of edge
+ */
 public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Path<V, E> {
-    public Path<V, E> path1, path2;
-    boolean usingPath1 = true;
+
+    private Path<V, E> primary, secondary;
+    boolean primaryActive = true;
 
     /**
-     * Creates a Disjoint Path Pair from two paths.
+     * Creates a disjoint path pair from two paths.
      *
-     * @param p1    first path
-     * @param p2    second path
+     * @param primary   primary path
+     * @param secondary secondary path
      */
-    public DisjointPathPair(Path<V, E> p1, Path<V, E> p2) {
-        path1 = p1;
-        path2 = p2;
+    public DisjointPathPair(Path<V, E> primary, Path<V, E> secondary) {
+        this.primary = primary;
+        this.secondary = secondary;
     }
 
     @Override
     public V src() {
-        return path1.src();
+        return primary.src();
     }
 
     @Override
     public V dst() {
-        return path1.dst();
+        return primary.dst();
+    }
+
+    /**
+     * Returns the primary path.
+     *
+     * @return primary path
+     */
+    public Path<V, E> primary() {
+        return primary;
+    }
+
+    /**
+     * Returns the secondary path.
+     *
+     * @return primary path
+     */
+    public Path<V, E> secondary() {
+        return secondary;
     }
 
     @Override
     public double cost() {
-        if (!hasBackup()) {
-            return path1.cost();
-        }
-        return path1.cost() + path2.cost();
+        return hasBackup() ? primary.cost() + secondary.cost() : primary.cost();
     }
 
     @Override
     public List<E> edges() {
-        if (usingPath1 || !hasBackup()) {
-            return path1.edges();
-        } else {
-            return path2.edges();
-        }
+        return primaryActive || !hasBackup() ? primary.edges() : secondary.edges();
     }
 
     /**
@@ -73,7 +88,7 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa
      * @return boolean representing whether it has backup
      */
     public boolean hasBackup() {
-        return path2 != null && path2.edges() != null;
+        return secondary != null && secondary.edges() != null;
     }
 
     @Override
@@ -88,13 +103,8 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa
 
     @Override
     public int hashCode() {
-        Set<Path<V, E>> paths;
-        if (!hasBackup()) {
-            paths = of(path1);
-        } else {
-            paths = of(path1, path2);
-        }
-        return Objects.hash(paths);
+        return hasBackup() ? Objects.hash(primary) + Objects.hash(secondary) :
+                Objects.hash(primary);
     }
 
     @Override
@@ -106,10 +116,10 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa
             final DisjointPathPair other = (DisjointPathPair) obj;
             return Objects.equals(this.src(), other.src()) &&
                     Objects.equals(this.dst(), other.dst()) &&
-                    (Objects.equals(this.path1, other.path1) &&
-                            Objects.equals(this.path2, other.path2)) ||
-                    (Objects.equals(this.path1, other.path2) &&
-                            Objects.equals(this.path2, other.path1));
+                    (Objects.equals(this.primary, other.primary) &&
+                            Objects.equals(this.secondary, other.secondary)) ||
+                    (Objects.equals(this.primary, other.secondary) &&
+                            Objects.equals(this.secondary, other.primary));
         }
         return false;
     }
@@ -120,9 +130,6 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa
      * @return number of paths
      */
     public int size() {
-        if (hasBackup()) {
-            return 2;
-        }
-        return 1;
+        return hasBackup() ? 2 : 1;
     }
 }
index 891a019..be4ab19 100644 (file)
@@ -241,7 +241,7 @@ public class PIMAddrGroup {
             return false;
         }
         final PIMAddrGroup other = (PIMAddrGroup) obj;
-        if (this.family != this.family) {
+        if (this.family != other.family) {
             return false;
         }
 
index 2d4a781..2152640 100644 (file)
@@ -265,7 +265,7 @@ public class PIMAddrSource {
             return false;
         }
         final PIMAddrSource other = (PIMAddrSource) obj;
-        if (this.family != this.family) {
+        if (this.family != other.family) {
             return false;
         }
 
index 0c2d676..a6ba389 100644 (file)
@@ -166,7 +166,7 @@ public class PIMAddrUnicast {
             return false;
         }
         final PIMAddrUnicast other = (PIMAddrUnicast) obj;
-        if (this.family != this.family) {
+        if (this.family != other.family) {
             return false;
         }
 
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexDump.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexDump.java
new file mode 100755 (executable)
index 0000000..cfb7939
--- /dev/null
@@ -0,0 +1,57 @@
+/*\r
+ * Copyright 2015 Open Networking Laboratory\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.onlab.util;\r
+\r
+import org.jboss.netty.buffer.ChannelBuffer;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+/**\r
+ * HexDump class an utility to dump buffer in hex format.\r
+ */\r
+public final class HexDump {\r
+    protected static final Logger log = LoggerFactory.getLogger(HexDump.class);\r
+\r
+    private HexDump() {\r
+    }\r
+\r
+    /**\r
+     * Dump the buffer content in hex format.\r
+     *\r
+     * @param buff buffer content to dump in hex format\r
+     */\r
+    public static void dump(ChannelBuffer buff) {\r
+        try {\r
+            byte[] yTemp;\r
+            yTemp = buff.array();\r
+\r
+            int iStartIndex = buff.readerIndex();\r
+            int iEndIndex = buff.writerIndex();\r
+            do {\r
+                StringBuilder sb = new StringBuilder();\r
+                for (int k = 0; (k < 16) && (iStartIndex < iEndIndex); ++k) {\r
+                    if (0 == k % 4) {\r
+                        sb.append(String.format(" ")); // blank after 4 bytes\r
+                    }\r
+                    sb.append(String.format("%02X ", yTemp[iStartIndex++]));\r
+                }\r
+                log.debug(sb.toString());\r
+            } while (iStartIndex < iEndIndex);\r
+        } catch (Exception e) {\r
+            log.error("[HexDump] Invalid buffer: " + e.toString());\r
+        }\r
+    }\r
+}\r
index abc48cc..1b78814 100644 (file)
@@ -299,12 +299,14 @@ public abstract class Tools {
      *
      * @param path file path
      * @return file contents
+     * @deprecated in Emu release
      */
+    @Deprecated
     public static List<String> slurp(File path) {
-        try {
+        try (
             BufferedReader br = new BufferedReader(
                     new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
-
+                ) {
             List<String> lines = new ArrayList<>();
             String line;
             while ((line = br.readLine()) != null) {
index 885fbe5..8bfd270 100644 (file)
 package org.onlab.graph;
 
 import org.junit.Test;
-import java.util.Set;
+
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
-import java.util.HashMap;
+import java.util.Set;
 
 import static com.google.common.collect.ImmutableSet.of;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-
-
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
 
 /**
  * Test of the Suurballe backup path algorithm.
  */
 public class SRLGGraphSearchTest extends BreadthFirstSearchTest {
+
     @Override
     protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() {
-        return new SRLGGraphSearch<TestVertex, TestEdge>(null);
+        return new SRLGGraphSearch<>(null);
     }
 
-    public void setWeights() {
-        weight = new EdgeWeight<TestVertex, TestEdge>() {
-            @Override
-            public double weight(TestEdge edge) {
-                return edge.weight();
-            }
-        };
-    }
     public void setDefaultWeights() {
         weight = null;
     }
+
     @Override
     public void defaultGraphTest() {
-
     }
 
     @Override
     public void defaultHopCountWeight() {
-
     }
 
     @Test
@@ -66,34 +59,34 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest {
         TestEdge dC = new TestEdge(D, C, 1);
         Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D),
                                                                       of(aB, bC, aD, dC));
-        Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>();
+        Map<TestEdge, Integer> riskProfile = new HashMap<>();
         riskProfile.put(aB, 0);
         riskProfile.put(bC, 0);
         riskProfile.put(aD, 1);
         riskProfile.put(dC, 1);
-        SRLGGraphSearch<TestVertex, TestEdge> search =
-                new SRLGGraphSearch<TestVertex, TestEdge>(2, riskProfile);
-        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, GraphPathSearch.ALL_PATHS).paths();
+        SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(2, riskProfile);
+        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, ALL_PATHS).paths();
         System.out.println("\n\n\n" + paths + "\n\n\n");
-        assertTrue("one disjoint path pair found", paths.size() == 1);
+        assertEquals("one disjoint path pair found", 1, paths.size());
         checkIsDisjoint(paths.iterator().next(), riskProfile);
     }
+
     public void checkIsDisjoint(Path<TestVertex, TestEdge> p, Map<TestEdge, Integer> risks) {
         assertTrue("The path is not a DisjointPathPair", (p instanceof DisjointPathPair));
         DisjointPathPair<TestVertex, TestEdge> q = (DisjointPathPair) p;
-        Set<Integer> p1Risks = new HashSet<Integer>();
-        Set<Integer> p2Risks = new HashSet<Integer>();
-        for (TestEdge e: q.edges()) {
+        Set<Integer> p1Risks = new HashSet<>();
+        for (TestEdge e : q.edges()) {
             p1Risks.add(risks.get(e));
         }
         if (!q.hasBackup()) {
             return;
         }
-        Path<TestVertex, TestEdge> pq = q.path2;
+        Path<TestVertex, TestEdge> pq = q.secondary();
         for (TestEdge e: pq.edges()) {
             assertTrue("The paths are not disjoint", !p1Risks.contains(risks.get(e)));
         }
     }
+
     @Test
     public void complexGraphTest() {
         setDefaultWeights();
@@ -105,16 +98,15 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest {
         TestEdge bE = new TestEdge(B, E, 1);
         Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D, E),
                                                                       of(aB, bC, aD, dC, cE, bE));
-        Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>();
+        Map<TestEdge, Integer> riskProfile = new HashMap<>();
         riskProfile.put(aB, 0);
         riskProfile.put(bC, 0);
         riskProfile.put(aD, 1);
         riskProfile.put(dC, 1);
         riskProfile.put(cE, 2);
         riskProfile.put(bE, 3);
-        SRLGGraphSearch<TestVertex, TestEdge> search =
-                new SRLGGraphSearch<TestVertex, TestEdge>(4, riskProfile);
-        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, GraphPathSearch.ALL_PATHS).paths();
+        SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(4, riskProfile);
+        search.search(graph, A, E, weight, ALL_PATHS).paths();
     }
 
     @Test
@@ -128,19 +120,19 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest {
         TestEdge cE = new TestEdge(C, E, 1);
         Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D, E),
                                                                       of(aB, bE, aD, dE, aC, cE));
-        Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>();
+        Map<TestEdge, Integer> riskProfile = new HashMap<>();
         riskProfile.put(aB, 0);
         riskProfile.put(bE, 1);
         riskProfile.put(aD, 2);
         riskProfile.put(dE, 3);
         riskProfile.put(aC, 4);
         riskProfile.put(cE, 5);
-        SRLGGraphSearch<TestVertex, TestEdge> search =
-                new SRLGGraphSearch<TestVertex, TestEdge>(6, riskProfile);
-        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, GraphPathSearch.ALL_PATHS).paths();
+        SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(6, riskProfile);
+        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, ALL_PATHS).paths();
         assertTrue("> one disjoint path pair found", paths.size() >= 1);
         checkIsDisjoint(paths.iterator().next(), riskProfile);
     }
+
     @Test
     public void onePath() {
         setDefaultWeights();
@@ -150,17 +142,17 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest {
         TestEdge dC = new TestEdge(D, C, 1);
         Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D),
                                                                       of(aB, bC, aD, dC));
-        Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>();
+        Map<TestEdge, Integer> riskProfile = new HashMap<>();
         riskProfile.put(aB, 0);
         riskProfile.put(bC, 0);
         riskProfile.put(aD, 1);
         riskProfile.put(dC, 0);
-        SRLGGraphSearch<TestVertex, TestEdge> search =
-                new SRLGGraphSearch<TestVertex, TestEdge>(2, riskProfile);
-        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, GraphPathSearch.ALL_PATHS).paths();
+        SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(2, riskProfile);
+        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, ALL_PATHS).paths();
         System.out.println(paths);
         assertTrue("no disjoint path pairs found", paths.size() == 0);
     }
+
     @Test
     public void noPath() {
         setDefaultWeights();
@@ -175,9 +167,8 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest {
         riskProfile.put(bC, 0);
         riskProfile.put(aD, 1);
         riskProfile.put(dC, 0);
-        SRLGGraphSearch<TestVertex, TestEdge> search =
-                new SRLGGraphSearch<>(2, riskProfile);
-        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, GraphPathSearch.ALL_PATHS).paths();
+        SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(2, riskProfile);
+        Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, ALL_PATHS).paths();
         assertTrue("no disjoint path pairs found", paths.size() == 0);
     }
 }
index 02f0deb..db7224a 100644 (file)
  */
 package org.onlab.util;
 
-import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.List;
-import java.util.Timer;
 import java.util.stream.IntStream;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.onlab.junit.TestTools.assertAfter;
-import static org.onlab.junit.TestTools.delay;
 
 /**
  * Tests the operation of the accumulator.
  */
 public class AbstractAccumulatorTest {
 
-    private final Timer timer = new Timer();
+
+    private final ManuallyAdvancingTimer timer = new ManuallyAdvancingTimer();
+
 
     @Test
     public void basics() throws Exception {
@@ -42,7 +43,6 @@ public class AbstractAccumulatorTest {
         assertEquals("incorrect idle ms", 70, accumulator.maxIdleMillis());
     }
 
-    @Ignore("FIXME: timing sensitive test failing randomly.")
     @Test
     public void eventTrigger() {
         TestAccumulator accumulator = new TestAccumulator();
@@ -52,43 +52,40 @@ public class AbstractAccumulatorTest {
         accumulator.add(new TestItem("d"));
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestItem("e"));
-        delay(20);
+        timer.advanceTimeMillis(20, 10);
         assertFalse("should have fired", accumulator.batch.isEmpty());
         assertEquals("incorrect batch", "abcde", accumulator.batch);
     }
 
-    @Ignore("FIXME: timing sensitive test failing randomly.")
     @Test
     public void timeTrigger() {
         TestAccumulator accumulator = new TestAccumulator();
         accumulator.add(new TestItem("a"));
-        delay(30);
+        timer.advanceTimeMillis(30, 1);
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestItem("b"));
-        delay(30);
+        timer.advanceTimeMillis(30, 1);
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestItem("c"));
-        delay(30);
+        timer.advanceTimeMillis(30, 1);
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestItem("d"));
-        delay(60);
+        timer.advanceTimeMillis(10, 10);
         assertFalse("should have fired", accumulator.batch.isEmpty());
         assertEquals("incorrect batch", "abcd", accumulator.batch);
     }
 
-    @Ignore("FIXME: timing sensitive test failing randomly.")
     @Test
     public void idleTrigger() {
         TestAccumulator accumulator = new TestAccumulator();
         accumulator.add(new TestItem("a"));
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestItem("b"));
-        delay(80);
+        timer.advanceTimeMillis(70, 10);
         assertFalse("should have fired", accumulator.batch.isEmpty());
         assertEquals("incorrect batch", "ab", accumulator.batch);
     }
 
-    @Ignore("FIXME: timing sensitive test failing randomly.")
     @Test
     public void readyIdleTrigger() {
         TestAccumulator accumulator = new TestAccumulator();
@@ -96,30 +93,28 @@ public class AbstractAccumulatorTest {
         accumulator.add(new TestItem("a"));
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestItem("b"));
-        delay(80);
+        timer.advanceTimeMillis(80, 1);
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.ready = true;
-        delay(80);
+        timer.advanceTimeMillis(80, 10);
         assertFalse("should have fired", accumulator.batch.isEmpty());
         assertEquals("incorrect batch", "ab", accumulator.batch);
     }
 
-    @Ignore("FIXME: timing sensitive test failing randomly.")
     @Test
     public void readyLongTrigger() {
         TestAccumulator accumulator = new TestAccumulator();
         accumulator.ready = false;
-        delay(120);
+        timer.advanceTimeMillis(120, 1);
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.add(new TestItem("a"));
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.ready = true;
-        delay(80);
+        timer.advanceTimeMillis(120, 10);
         assertFalse("should have fired", accumulator.batch.isEmpty());
         assertEquals("incorrect batch", "a", accumulator.batch);
     }
 
-    @Ignore("FIXME: timing sensitive test failing randomly.")
     @Test
     public void readyMaxTrigger() {
         TestAccumulator accumulator = new TestAccumulator();
@@ -133,16 +128,16 @@ public class AbstractAccumulatorTest {
         assertTrue("should not have fired yet", accumulator.batch.isEmpty());
         accumulator.ready = true;
         accumulator.add(new TestItem("g"));
-        delay(5);
+        timer.advanceTimeMillis(10, 10);
         assertFalse("should have fired", accumulator.batch.isEmpty());
         assertEquals("incorrect batch", "abcdefg", accumulator.batch);
     }
 
-    @Ignore("FIXME: timing sensitive test failing randomly.")
     @Test
     public void stormTest() {
         TestAccumulator accumulator = new TestAccumulator();
         IntStream.range(0, 1000).forEach(i -> accumulator.add(new TestItem("#" + i)));
+        timer.advanceTimeMillis(1);
         assertAfter(100, () -> assertEquals("wrong item count", 1000, accumulator.itemCount));
         assertEquals("wrong batch count", 200, accumulator.batchCount);
     }
@@ -180,5 +175,4 @@ public class AbstractAccumulatorTest {
             return ready;
         }
     }
-
 }
diff --git a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java
new file mode 100644 (file)
index 0000000..4116cbe
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.util;
+
+import com.google.common.collect.Lists;
+import org.onlab.junit.TestUtils;
+import org.slf4j.Logger;
+
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.TimerTask;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.junit.TestTools.delay;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Provides manually scheduled timer utility. All schedulable methods are subject to overflow (you can set a period of
+ * max long).  Additionally if a skip skips a period of time greater than one period for a periodic task that task will
+ * only be executed once for that skip and scheduled it's period after the last execution.
+ */
+public class ManuallyAdvancingTimer extends java.util.Timer {
+
+    /* States whether or not the static values from timer task have been set ensures population will only occur once.*/
+    private boolean staticsPopulated = false;
+
+    /* Virgin value from timer task */
+    private int virginState;
+
+    /* Scheduled value from timer task */
+    private int scheduledState;
+
+    /* Executed value from timer task */
+    private int executedState;
+
+    /* Cancelled value from timer task */
+    private int cancelledState;
+
+    private final Logger logger = getLogger(getClass());
+
+    /* Service for executing timer tasks */
+    private final ExecutorService executorService = Executors.newSingleThreadExecutor();
+
+    /* Internal time representation independent of system time, manually advanced */
+    private final TimerKeeper timerKeeper = new TimerKeeper();
+
+    /* Data structure for tracking tasks */
+    private final TaskQueue queue = new TaskQueue();
+
+    @Override
+    public void schedule(TimerTask task, long delay) {
+        if (!staticsPopulated) {
+            populateStatics(task);
+        }
+        if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay :
+                timerKeeper.currentTimeInMillis() - delay, 0)) {
+            logger.error("Failed to submit task");
+        }
+    }
+
+    @Override
+    public void schedule(TimerTask task, Date time) {
+        if (!staticsPopulated) {
+            populateStatics(task);
+        }
+        if (!submitTask(task, time.getTime(), 0)) {
+            logger.error("Failed to submit task");
+        }
+    }
+
+    @Override
+    public void schedule(TimerTask task, long delay, long period) {
+        if (!staticsPopulated) {
+            populateStatics(task);
+        }
+        if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay :
+                timerKeeper.currentTimeInMillis() - delay, period)) {
+            logger.error("Failed to submit task");
+        }
+    }
+
+    @Override
+    public void schedule(TimerTask task, Date firstTime, long period) {
+        if (!staticsPopulated) {
+            populateStatics(task);
+        }
+        if (!submitTask(task, firstTime.getTime(), period)) {
+            logger.error("Failed to submit task");
+        }
+    }
+
+    /*################################################WARNING################################################*/
+    /* Schedule at fixed rate methods do not work exactly as in the java timer. They are clones of the periodic
+    *scheduling methods. */
+    @Override
+    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
+        if (!staticsPopulated) {
+            populateStatics(task);
+        }
+        if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay :
+                timerKeeper.currentTimeInMillis() - delay, period)) {
+            logger.error("Failed to submit task");
+        }
+    }
+
+    @Override
+    public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {
+        if (!staticsPopulated) {
+            populateStatics(task);
+        }
+        if (!submitTask(task, firstTime.getTime(), period)) {
+            logger.error("Failed to submit task");
+        }
+    }
+
+    @Override
+    public void cancel() {
+        executorService.shutdown();
+        queue.clear();
+    }
+
+    @Override
+    public int purge() {
+        return queue.removeCancelled();
+    }
+
+    /**
+     * Returns the virtual current time in millis.
+     *
+     * @return long representing simulated current time.
+     */
+    public long currentTimeInMillis() {
+        return timerKeeper.currentTimeInMillis();
+    }
+
+    /**
+     * Returns the new simulated current time in millis after advancing the absolute value of millis to advance.
+     * Triggers event execution of all events scheduled for execution at times up to and including the returned time.
+     * Passing in the number zero has no effect.
+     *
+     * @param millisToAdvance the number of millis to advance.
+     * @return a long representing the current simulated time in millis
+     */
+    public long advanceTimeMillis(long millisToAdvance) {
+        return timerKeeper.advanceTimeMillis(millisToAdvance);
+    }
+
+    /**
+     * Advances the virtual time a certain number of millis triggers execution delays a certain amount to
+     * allow time for execution.
+     *
+     * @param virtualTimeAdvance the time to be advances in millis of simulated time.
+     * @param realTimeDelay      the time to delay in real time to allow for processing.
+     */
+    public void advanceTimeMillis(long virtualTimeAdvance, int realTimeDelay) {
+        timerKeeper.advanceTimeMillis(virtualTimeAdvance);
+        delay(realTimeDelay);
+    }
+
+    /**
+     * Sets up the task and submits it to the queue.
+     *
+     * @param task    the task to be added to the queue
+     * @param runtime the first runtime of the task
+     * @param period  the period between runs thereafter
+     * @return returns true if the task was successfully submitted, false otherwise
+     */
+    private boolean submitTask(TimerTask task, long runtime, long period) {
+        checkNotNull(task);
+        try {
+            TestUtils.setField(task, "state", scheduledState);
+            TestUtils.setField(task, "nextExecutionTime", runtime);
+            TestUtils.setField(task, "period", period);
+        } catch (TestUtils.TestUtilsException e) {
+            e.printStackTrace();
+            return false;
+        }
+        queue.insertOrdered(task);
+        return true;
+    }
+
+    /**
+     * Executes the given task (only if it is in the scheduled state) and proceeds to reschedule it or mark it as
+     * executed.  Does not remove from the queue (this must be done outside).
+     *
+     * @param task the timer task to be executed
+     */
+    private boolean executeTask(TimerTask task) {
+        checkNotNull(task);
+        int currentState;
+        try {
+            currentState = TestUtils.getField(task, "state");
+        } catch (TestUtils.TestUtilsException e) {
+            logger.error("Could not get state of task.");
+            e.printStackTrace();
+            return false;
+        }
+        //If cancelled or already executed stop here.
+        if (currentState == executedState || currentState == cancelledState) {
+            return false;
+        } else if (currentState == virginState) {
+            logger.error("Task was set for execution without being scheduled.");
+            return false;
+        } else if (currentState == scheduledState) {
+            long period;
+
+            try {
+                period = TestUtils.getField(task, "period");
+            } catch (TestUtils.TestUtilsException e) {
+                logger.error("Could not read period of task.");
+                e.printStackTrace();
+                return false;
+            }
+            //Period of zero means one time execution.
+            if (period == 0) {
+                try {
+                    TestUtils.setField(task, "state", executedState);
+                } catch (TestUtils.TestUtilsException e) {
+                    logger.error("Could not set executed state.");
+                    e.printStackTrace();
+                    return false;
+                }
+                executorService.execute(task);
+                return true;
+            } else {
+                //Calculate next execution time, using absolute value of period
+                long nextTime = (period > 0) ? (timerKeeper.currentTimeInMillis() + period) :
+                        (timerKeeper.currentTimeInMillis() - period);
+                try {
+                    TestUtils.setField(task, "nextExecutionTime", nextTime);
+                } catch (TestUtils.TestUtilsException e) {
+                    logger.error("Could not set next execution time.");
+                    e.printStackTrace();
+                    return false;
+                }
+                //Schedule next execution
+                queue.insertOrdered(task);
+                executorService.execute(task);
+                return true;
+            }
+        }
+        logger.error("State property of {} is in an illegal state and did not execute.", task);
+        return false;
+    }
+
+    /**
+     * Executes all tasks in the queue scheduled for execution up to and including the current time.
+     *
+     * @return the total number of tasks run, -1 if failure
+     */
+    private int executeEventsUpToPresent() {
+        int totalRun = 0;
+        if (queue.isEmpty()) {
+            return -1;
+        }
+        TimerTask currTask = queue.peek();
+        long currExecTime;
+        try {
+            currExecTime = TestUtils.getField(currTask, "nextExecutionTime");
+        } catch (TestUtils.TestUtilsException e) {
+            e.printStackTrace();
+            throw new RuntimeException("Could not get nextExecutionTime");
+        }
+        while (currExecTime <= timerKeeper.currentTimeInMillis()) {
+            if (executeTask(queue.pop())) {
+                totalRun++;
+            }
+            if (queue.isEmpty()) {
+                break;
+            }
+            currTask = queue.peek();
+            try {
+                currExecTime = TestUtils.getField(currTask, "nextExecutionTime");
+            } catch (TestUtils.TestUtilsException e) {
+                e.printStackTrace();
+                throw new RuntimeException("Could not get nextExecutionTime");
+            }
+        }
+        return totalRun;
+    }
+
+    /**
+     * Populates the static fields from timer task. Should only be called once.
+     */
+    private void populateStatics(TimerTask task) {
+        try {
+            virginState = TestUtils.getField(task, "VIRGIN");
+            scheduledState = TestUtils.getField(task, "SCHEDULED");
+            executedState = TestUtils.getField(task, "EXECUTED");
+            cancelledState = TestUtils.getField(task, "CANCELLED");
+            staticsPopulated = true;
+        } catch (TestUtils.TestUtilsException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * A class used to maintain the virtual time.
+     */
+    private class TimerKeeper {
+
+        private long currentTime = 0;
+
+        /**
+         * Returns the virtual current time in millis.
+         *
+         * @return long representing simulated current time.
+         */
+        long currentTimeInMillis() {
+            return currentTime;
+        }
+
+        /**
+         * Returns the new simulated current time in millis after advancing the absolute value of millis to advance.
+         * Triggers event execution of all events scheduled for execution at times up to and including the returned
+         * time. Passing in the number zero has no effect.
+         *
+         * @param millisToAdvance the number of millis to advance.
+         * @return a long representing the current simulated time in millis
+         */
+        long advanceTimeMillis(long millisToAdvance) {
+            currentTime = (millisToAdvance >= 0) ? (currentTime + millisToAdvance) : (currentTime - millisToAdvance);
+            if (millisToAdvance != 0) {
+                executeEventsUpToPresent();
+            }
+            return currentTime;
+        }
+    }
+
+    /**
+     * A queue backed by a linked list. Keeps elements sorted in ascending order of execution time.  All calls are safe
+     * even on empty queue's.
+     */
+    private class TaskQueue {
+        private final LinkedList<TimerTask> taskList = Lists.newLinkedList();
+
+        /**
+         * Adds the task to the queue in ascending order of scheduled execution. If execution time has already passed
+         * execute immediately.
+         *
+         * @param task the task to be added to the queue
+         */
+        void insertOrdered(TimerTask task) {
+            //Using O(N) insertion because random access is expensive in linked lists worst case is 2N links followed
+            // for binary insertion vs N for simple insertion.
+            checkNotNull(task);
+            if (!staticsPopulated) {
+                populateStatics(task);
+            }
+            long insertTime;
+            try {
+                insertTime = TestUtils.getField(task, "nextExecutionTime");
+                TestUtils.setField(task, "state", scheduledState);
+            } catch (TestUtils.TestUtilsException e) {
+                e.printStackTrace();
+                return;
+            }
+            //If the task was scheduled in the past or for the current time run it immediately and do not add to the
+            // queue, subsequent executions will be scheduled as normal
+            if (insertTime <= timerKeeper.currentTimeInMillis()) {
+                executeTask(task);
+                return;
+            }
+
+            Iterator<TimerTask> iter = taskList.iterator();
+            int positionCounter = 0;
+            long nextTaskTime;
+            TimerTask currentTask;
+            while (iter.hasNext()) {
+                currentTask = iter.next();
+                try {
+                    nextTaskTime = TestUtils.getField(currentTask, "nextExecutionTime");
+                } catch (TestUtils.TestUtilsException e) {
+                    e.printStackTrace();
+                    return;
+                }
+                if (insertTime < nextTaskTime) {
+                    taskList.add(positionCounter, task);
+                    return;
+                }
+                positionCounter++;
+            }
+            taskList.addLast(task);
+        }
+
+        /**
+         * Returns the first item in the queue (next scheduled for execution) without removing it, returns null if the
+         * queue is empty.
+         *
+         * @return the next TimerTask to run or null if the queue is empty
+         */
+        TimerTask peek() {
+            if (taskList.isEmpty()) {
+                return null;
+            }
+            return taskList.getFirst();
+        }
+
+        /**
+         * Returns and removes the first item in the queue or null if it is empty.
+         *
+         * @return the first element of the queue or null if the queue is empty
+         */
+        TimerTask pop() {
+            if (taskList.isEmpty()) {
+                return null;
+            }
+            return taskList.pop();
+        }
+
+        /**
+         * Performs a sort on the set of timer tasks, earliest task is first. Does nothing if queue is empty.
+         */
+        void sort() {
+            if (taskList.isEmpty()) {
+                return;
+            }
+            taskList.sort((o1, o2) -> {
+                checkNotNull(o1);
+                checkNotNull(o2);
+                long executionTimeOne;
+                long executionTimeTwo;
+                try {
+                    executionTimeOne = TestUtils.getField(o1, "nextExecutionTime");
+                    executionTimeTwo = TestUtils.getField(o2, "nextExecutionTime");
+                } catch (TestUtils.TestUtilsException e) {
+                    e.printStackTrace();
+                    throw new RuntimeException("Could not get next execution time.");
+                }
+                if (executionTimeOne == executionTimeTwo) {
+                    return 0;
+                } else if (executionTimeOne < executionTimeTwo) {
+                    return -1;
+                } else {
+                    return 1;
+                }
+            });
+        }
+
+        /**
+         * Returns whether the queue is currently empty.
+         *
+         * @return true if the queue is empty, false otherwise
+         */
+        boolean isEmpty() {
+            return taskList.isEmpty();
+        }
+
+        /**
+         * Clears the underlying list of the queue.
+         */
+        void clear() {
+            taskList.clear();
+        }
+
+        /**
+         * Removes all cancelled tasks from the queue. Has no effect on behavior.
+         *
+         * @return returns the total number of items removed, -1 if list is empty or failure occurs.
+         */
+        int removeCancelled() {
+            if (taskList.isEmpty()) {
+                return -1;
+            }
+            int removedCount = 0;
+            Iterator<TimerTask> taskIterator = taskList.iterator();
+            TimerTask currTask;
+            int currState;
+            while (taskIterator.hasNext()) {
+                currTask = taskIterator.next();
+                try {
+                    currState = TestUtils.getField(currTask, "state");
+                } catch (TestUtils.TestUtilsException e) {
+                    logger.error("Could not get task state.");
+                    e.printStackTrace();
+                    return -1;
+                }
+                if (currState == cancelledState) {
+                    removedCount++;
+                    taskIterator.remove();
+                }
+            }
+            return removedCount;
+        }
+    }
+}
diff --git a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimerTest.java b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimerTest.java
new file mode 100644 (file)
index 0000000..b8e1e85
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onlab.util;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.onlab.junit.TestTools.delay;
+
+/**
+ * Testing class for manually advancing timer.
+ */
+public class ManuallyAdvancingTimerTest {
+
+    private ManuallyAdvancingTimer timer;
+
+    /* Generates unique id's for TestTasks */
+    private AtomicInteger idGenerator;
+
+    /* Tracks TestTasks in order of creation, tasks are automatically added at creation. */
+    private ArrayList<TestTask> taskList;
+
+    /* Total number of tasks run */
+    private AtomicInteger tasksRunCount;
+
+    // FIXME if this class fails first try increasing the real time delay to account for heavy system load.
+    private static final int REAL_TIME_DELAY = 1;
+
+    /**
+     * Sets up the testing environment.
+     */
+    @Before
+    public void setup() {
+        timer = new ManuallyAdvancingTimer();
+        idGenerator = new AtomicInteger(1);
+        tasksRunCount = new AtomicInteger(0);
+        taskList = Lists.newArrayList();
+    }
+
+    /**
+     * Tests the one time schedule with delay.
+     *
+     * @throws Exception throws an exception if the test fails
+     */
+    @Test
+    public void testScheduleByDelay() throws Exception {
+        /* Test scheduling in the future as normal. */
+        timer.schedule(new TestTask(), 10);
+        timer.advanceTimeMillis(5);
+        assertFalse(taskList.get(0).hasRun());
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertTrue(taskList.get(0).hasRun());
+
+        /* Test scheduling with negative numbers */
+        timer.schedule(new TestTask(), -10);
+        timer.advanceTimeMillis(5);
+        assertFalse(taskList.get(1).hasRun());
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertTrue(taskList.get(1).hasRun());
+
+        /* Reset list, counter and timer for next test */
+        taskList.clear();
+        idGenerator.set(1);
+        tasksRunCount.set(0);
+
+        for (int i = 0; i < 50; i++) {
+            timer.schedule(new TestTask(), i);
+        }
+        /* Test that a task scheduled for present is run and not placed in the queue */
+        assertEquals("Only the first task should have run.", 1, tasksRunCount.get());
+
+        for (int i = 2; i <= 50; i++) {
+            timer.advanceTimeMillis(1, REAL_TIME_DELAY);
+            assertEquals("One task should be executed per loop", i, tasksRunCount.get());
+        }
+        /* Below tests ordered insertion, this will only be done once, it is the same for all schedule methods. */
+
+        tasksRunCount.set(0);
+
+        for (int i = 0; i < 10; i++) {
+            timer.schedule(new TestTask(), 500);
+        }
+
+        assertEquals("No new tasks should have been run  since run count reset.", 0, tasksRunCount.get());
+        timer.schedule(new TestTask(), 10);
+        assertEquals("No new tasks should have been run  since run count reset.", 0, tasksRunCount.get());
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertEquals("One new tasks should have been run  since run count reset.", 1, tasksRunCount.get());
+        timer.advanceTimeMillis(510, REAL_TIME_DELAY);
+        assertEquals("Eleven new tasks should have been run  since run count reset.", 11, tasksRunCount.get());
+    }
+
+    /**
+     * Tests scheduling for a particular date or time which may be in the past.
+     *
+     * @throws Exception throws an exception if the test fails
+     */
+    @Test
+    public void testScheduleByDate() throws Exception {
+        /* Tests basic scheduling for future times. */
+        timer.schedule(new TestTask(), new Date(10));
+        timer.advanceTimeMillis(5);
+        assertFalse(taskList.get(0).hasRun());
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertTrue(taskList.get(0).hasRun());
+
+        /* Test scheduling with past times numbers */
+        timer.schedule(new TestTask(), new Date(0));
+        delay(REAL_TIME_DELAY);
+        assertTrue(taskList.get(1).hasRun());
+
+        /* Tests cancellation on non-periodic events */
+        TestTask task = new TestTask();
+        timer.schedule(task, new Date(timer.currentTimeInMillis() + 10));
+        task.cancel();
+        timer.advanceTimeMillis(12, REAL_TIME_DELAY);
+        assertFalse(task.hasRun());
+
+    }
+
+    /**
+     * Test scheduling beginning after a delay and recurring periodically.
+     *
+     * @throws Exception throws an exception if the test fails
+     */
+    @Test
+    public void testScheduleByDelayPeriodic() throws Exception {
+        /* Test straightforward periodic execution */
+        timer.schedule(new TestTask(), 0, 10);
+        delay(REAL_TIME_DELAY);
+        assertEquals("Task should have run once when added.", 1, taskList.get(0).timesRun());
+
+        /* Tests whether things that are not added to the queue are scheduled for future executions (ones which execute
+        immediately on add). */
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertEquals("Task should have run once when added.", 2, taskList.get(0).timesRun());
+
+        /* Tests whether cancellation works on periodic events. */
+        taskList.get(0).cancel();
+
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertEquals("The task should not have run another time.", 2, taskList.get(0).timesRun());
+
+        TestTask task = new TestTask();
+        timer.schedule(task, 0, 10);
+        timer.advanceTimeMillis(100, REAL_TIME_DELAY);
+        assertEquals("Should have run immeditaley and subsequently once during the larger skip", task.timesRun(), 2);
+
+    }
+
+    /**
+     * Test scheduling beginning at a specified date and recurring periodically.
+     *
+     * @throws Exception throws an exception if the test fails
+     */
+    @Test
+    public void testScheduleByDatePeriodic() throws Exception {
+        /* Test straightforward periodic execution */
+        timer.schedule(new TestTask(), new Date(timer.currentTimeInMillis()), 10);
+        delay(REAL_TIME_DELAY);
+        assertEquals("Task should have run once when added.", 1, taskList.get(0).timesRun());
+
+        /* Tests whether things that are not added to the queue are scheduled for future executions (ones which execute
+        immediately on add). */
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertEquals("Task should have run once when added.", 2, taskList.get(0).timesRun());
+
+        /* Tests whether cancellation works on periodic events. */
+        taskList.get(0).cancel();
+
+        timer.advanceTimeMillis(10, REAL_TIME_DELAY);
+        assertEquals("The task should not have run another time.", 2, taskList.get(0).timesRun());
+
+        TestTask task = new TestTask();
+        timer.schedule(task, new Date(timer.currentTimeInMillis()), 10);
+        timer.advanceTimeMillis(100, REAL_TIME_DELAY);
+        assertEquals("Should have run immediately and subsequently once during the larger skip", task.timesRun(), 2);
+    }
+
+    /* Schedule at fixed rate runs exactly like the two scheduling methods just tested so tests are not included */
+
+    /**
+     * Timer task with added functions to make it better for testing.
+     */
+    private class TestTask extends TimerTask {
+
+        /* Remains true once the task has been run at least once */
+        private boolean hasRun;
+
+        /* Unique id per event. */
+        private int id;
+
+        /* Specifies the number of times an event has run */
+        private int timesRun;
+
+        /**
+         * Constructor initializes id, timesRun, and id fields.
+         */
+        public TestTask() {
+            id = idGenerator.getAndIncrement();
+            timesRun = 0;
+            hasRun = false;
+            taskList.add(this);
+        }
+
+        @Override
+        public void run() {
+            this.hasRun = true;
+            tasksRunCount.incrementAndGet();
+            timesRun++;
+        }
+
+        /**
+         * Returns whether this event has run.
+         *
+         * @return true if the event has run, false otherwise.
+         */
+        public boolean hasRun() {
+            return hasRun;
+        }
+
+        /**
+         * Returns the number of times this task has run.
+         *
+         * @return an int representing the number of times this task has been run
+         */
+        public int timesRun() {
+            return timesRun;
+        }
+
+        /**
+         * Returns the unique identifier of this task.
+         *
+         * @return a unique integer identifier
+         */
+        public int getId() {
+            return id;
+        }
+    }
+}
\ No newline at end of file
index 0278ccd..b8718c3 100644 (file)
@@ -22,14 +22,53 @@ import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.ComponentInstance;
 
 import java.util.Dictionary;
+import java.util.Enumeration;
 
 /**
  * Adapter implementation of OSGI component context.
  */
 public class ComponentContextAdapter implements ComponentContext {
+    private static class MockDictionary extends Dictionary {
+
+        @Override
+        public int size() {
+            return 0;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return false;
+        }
+
+        @Override
+        public Enumeration keys() {
+            return null;
+        }
+
+        @Override
+        public Enumeration elements() {
+            return null;
+        }
+
+        @Override
+        public Object get(Object key) {
+            return null;
+        }
+
+        @Override
+        public Object put(Object key, Object value) {
+            return null;
+        }
+
+        @Override
+        public Object remove(Object key) {
+            return null;
+        }
+    }
+
     @Override
     public Dictionary getProperties() {
-        return null;
+        return new MockDictionary();
     }
 
     @Override
index 325e191..0e88e34 100644 (file)
@@ -66,9 +66,9 @@ public class FlowsWebResource extends AbstractWebResource {
     public Response getFlows() {
         final Iterable<Device> devices = get(DeviceService.class).getDevices();
         for (final Device device : devices) {
-            final Iterable<FlowEntry> deviceEntries = service.getFlowEntries(device.id());
-            if (deviceEntries != null) {
-                for (final FlowEntry entry : deviceEntries) {
+            final Iterable<FlowEntry> flowEntries = service.getFlowEntries(device.id());
+            if (flowEntries != null) {
+                for (final FlowEntry entry : flowEntries) {
                     flowsNode.add(codec(FlowEntry.class).encode(entry, this));
                 }
             }
@@ -88,13 +88,13 @@ public class FlowsWebResource extends AbstractWebResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("{deviceId}")
     public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) {
-        final Iterable<FlowEntry> deviceEntries =
+        final Iterable<FlowEntry> flowEntries =
                 service.getFlowEntries(DeviceId.deviceId(deviceId));
 
-        if (!deviceEntries.iterator().hasNext()) {
+        if (!flowEntries.iterator().hasNext()) {
             throw new ItemNotFoundException(DEVICE_NOT_FOUND);
         }
-        for (final FlowEntry entry : deviceEntries) {
+        for (final FlowEntry entry : flowEntries) {
             flowsNode.add(codec(FlowEntry.class).encode(entry, this));
         }
         return ok(root).build();
@@ -113,13 +113,13 @@ public class FlowsWebResource extends AbstractWebResource {
     @Path("{deviceId}/{flowId}")
     public Response getFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
                                                @PathParam("flowId") long flowId) {
-        final Iterable<FlowEntry> deviceEntries =
+        final Iterable<FlowEntry> flowEntries =
                 service.getFlowEntries(DeviceId.deviceId(deviceId));
 
-        if (!deviceEntries.iterator().hasNext()) {
+        if (!flowEntries.iterator().hasNext()) {
             throw new ItemNotFoundException(DEVICE_NOT_FOUND);
         }
-        for (final FlowEntry entry : deviceEntries) {
+        for (final FlowEntry entry : flowEntries) {
             if (entry.id().value() == flowId) {
                 flowsNode.add(codec(FlowEntry.class).encode(entry, this));
             }
@@ -175,14 +175,14 @@ public class FlowsWebResource extends AbstractWebResource {
     @Path("{deviceId}/{flowId}")
     public void deleteFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
                                               @PathParam("flowId") long flowId) {
-        final Iterable<FlowEntry> deviceEntries =
+        final Iterable<FlowEntry> flowEntries =
                 service.getFlowEntries(DeviceId.deviceId(deviceId));
 
-        if (!deviceEntries.iterator().hasNext()) {
+        if (!flowEntries.iterator().hasNext()) {
             throw new ItemNotFoundException(DEVICE_NOT_FOUND);
         }
 
-        StreamSupport.stream(deviceEntries.spliterator(), false)
+        StreamSupport.stream(flowEntries.spliterator(), false)
                 .filter(entry -> entry.id().value() == flowId)
                 .forEach(service::removeFlowRules);
     }
index 9e2b627..808fcc1 100644 (file)
@@ -49,73 +49,77 @@ public class NetworkConfigWebResource extends AbstractWebResource {
     public Response download() {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = mapper().createObjectNode();
-        service.getSubjectClasses().forEach(sc ->
-            produceJson(service, newObject(root, service.getSubjectFactory(sc).subjectKey()), sc));
+        service.getSubjectClasses().forEach(sc -> {
+            SubjectFactory subjectFactory = service.getSubjectFactory(sc);
+            produceJson(service, newObject(root, subjectFactory.subjectClassKey()),
+                        subjectFactory, sc);
+        });
         return ok(root).build();
     }
 
     /**
      * Get all network configuration for a subject class.
      *
-     * @param subjectKey subject class key
+     * @param subjectClassKey subject class key
      * @return network configuration JSON
      */
     @GET
-    @Path("{subjectKey}")
+    @Path("{subjectClassKey}")
     @Produces(MediaType.APPLICATION_JSON)
     @SuppressWarnings("unchecked")
-    public Response download(@PathParam("subjectKey") String subjectKey) {
+    public Response download(@PathParam("subjectClassKey") String subjectClassKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = mapper().createObjectNode();
-        produceJson(service, root, service.getSubjectFactory(subjectKey).subjectClass());
+        SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey);
+        produceJson(service, root, subjectFactory, subjectFactory.subjectClass());
         return ok(root).build();
     }
 
     /**
-     * Get all network configuration for a subject.
+     * Get all network configuration for a subjectKey.
      *
-     * @param subjectKey subject class key
-     * @param subject    subject key
+     * @param subjectClassKey subjectKey class key
+     * @param subjectKey      subjectKey key
      * @return network configuration JSON
      */
     @GET
-    @Path("{subjectKey}/{subject}")
+    @Path("{subjectClassKey}/{subjectKey}")
     @Produces(MediaType.APPLICATION_JSON)
     @SuppressWarnings("unchecked")
-    public Response download(@PathParam("subjectKey") String subjectKey,
-                             @PathParam("subject") String subject) {
+    public Response download(@PathParam("subjectClassKey") String subjectClassKey,
+                             @PathParam("subjectKey") String subjectKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = mapper().createObjectNode();
-        produceSubjectJson(service, root,
-                           service.getSubjectFactory(subjectKey).createSubject(subject));
+        SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey);
+        produceSubjectJson(service, root, subjectFactory.createSubject(subjectKey));
         return ok(root).build();
     }
 
     /**
-     * Get specific network configuration for a subject.
+     * Get specific network configuration for a subjectKey.
      *
-     * @param subjectKey subject class key
-     * @param subject    subject key
-     * @param configKey  configuration class key
+     * @param subjectClassKey subjectKey class key
+     * @param subjectKey      subjectKey key
+     * @param configKey       configuration class key
      * @return network configuration JSON
      */
     @GET
-    @Path("{subjectKey}/{subject}/{configKey}")
+    @Path("{subjectClassKey}/{subjectKey}/{configKey}")
     @Produces(MediaType.APPLICATION_JSON)
     @SuppressWarnings("unchecked")
-    public Response download(@PathParam("subjectKey") String subjectKey,
-                             @PathParam("subject") String subject,
+    public Response download(@PathParam("subjectClassKey") String subjectClassKey,
+                             @PathParam("subjectKey") String subjectKey,
                              @PathParam("configKey") String configKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
-        return ok(service.getConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
-                                    service.getConfigClass(subjectKey, configKey)).node()).build();
+        return ok(service.getConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+                                    service.getConfigClass(subjectClassKey, configKey)).node()).build();
     }
 
     @SuppressWarnings("unchecked")
     private void produceJson(NetworkConfigService service, ObjectNode node,
-                             Class subjectClass) {
+                             SubjectFactory subjectFactory, Class subjectClass) {
         service.getSubjects(subjectClass).forEach(s ->
-            produceSubjectJson(service, newObject(node, s.toString()), s));
+            produceSubjectJson(service, newObject(node, subjectFactory.subjectKey(s)), s));
     }
 
     private void produceSubjectJson(NetworkConfigService service, ObjectNode node,
@@ -128,8 +132,8 @@ public class NetworkConfigWebResource extends AbstractWebResource {
      * Upload bulk network configuration.
      *
      * @param request network configuration JSON rooted at the top node
-     * @throws IOException if unable to parse the request
      * @return empty response
+     * @throws IOException if unable to parse the request
      */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
@@ -146,78 +150,78 @@ public class NetworkConfigWebResource extends AbstractWebResource {
     /**
      * Upload multiple network configurations for a subject class.
      *
-     * @param subjectKey subject class key
-     * @param request    network configuration JSON rooted at the top node
+     * @param subjectClassKey subject class key
+     * @param request         network configuration JSON rooted at the top node
      * @return empty response
      * @throws IOException if unable to parse the request
      */
     @POST
-    @Path("{subjectKey}")
+    @Path("{subjectClassKey}")
     @Consumes(MediaType.APPLICATION_JSON)
     @SuppressWarnings("unchecked")
-    public Response upload(@PathParam("subjectKey") String subjectKey,
+    public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
                            InputStream request) throws IOException {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
-        consumeJson(service, root, service.getSubjectFactory(subjectKey));
+        consumeJson(service, root, service.getSubjectFactory(subjectClassKey));
         return Response.ok().build();
     }
 
     /**
-     * Upload mutliple network configurations for a subject.
+     * Upload mutliple network configurations for a subjectKey.
      *
-     * @param subjectKey subject class key
-     * @param subject    subject key
-     * @param request    network configuration JSON rooted at the top node
+     * @param subjectClassKey subjectKey class key
+     * @param subjectKey      subjectKey key
+     * @param request         network configuration JSON rooted at the top node
      * @return empty response
      * @throws IOException if unable to parse the request
      */
     @POST
-    @Path("{subjectKey}/{subject}")
+    @Path("{subjectClassKey}/{subjectKey}")
     @Consumes(MediaType.APPLICATION_JSON)
     @SuppressWarnings("unchecked")
-    public Response upload(@PathParam("subjectKey") String subjectKey,
-                           @PathParam("subject") String subject,
+    public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
+                           @PathParam("subjectKey") String subjectKey,
                            InputStream request) throws IOException {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
         consumeSubjectJson(service, root,
-                           service.getSubjectFactory(subjectKey).createSubject(subject),
-                           subjectKey);
+                           service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+                           subjectClassKey);
         return Response.ok().build();
     }
 
     /**
-     * Upload specific network configuration for a subject.
+     * Upload specific network configuration for a subjectKey.
      *
-     * @param subjectKey subject class key
-     * @param subject    subject key
-     * @param configKey  configuration class key
-     * @param request    network configuration JSON rooted at the top node
+     * @param subjectClassKey subjectKey class key
+     * @param subjectKey      subjectKey key
+     * @param configKey       configuration class key
+     * @param request         network configuration JSON rooted at the top node
      * @return empty response
      * @throws IOException if unable to parse the request
      */
     @POST
-    @Path("{subjectKey}/{subject}/{configKey}")
+    @Path("{subjectClassKey}/{subjectKey}/{configKey}")
     @Consumes(MediaType.APPLICATION_JSON)
     @SuppressWarnings("unchecked")
-    public Response upload(@PathParam("subjectKey") String subjectKey,
-                           @PathParam("subject") String subject,
+    public Response upload(@PathParam("subjectClassKey") String subjectClassKey,
+                           @PathParam("subjectKey") String subjectKey,
                            @PathParam("configKey") String configKey,
                            InputStream request) throws IOException {
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
-        service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
-                            service.getConfigClass(subjectKey, configKey), root);
+        service.applyConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+                            service.getConfigClass(subjectClassKey, configKey), root);
         return Response.ok().build();
     }
 
     private void consumeJson(NetworkConfigService service, ObjectNode classNode,
                              SubjectFactory subjectFactory) {
         classNode.fieldNames().forEachRemaining(s ->
-                                                        consumeSubjectJson(service, (ObjectNode) classNode.path(s),
-                                                                           subjectFactory.createSubject(s),
-                                                                           subjectFactory.subjectKey()));
+            consumeSubjectJson(service, (ObjectNode) classNode.path(s),
+                               subjectFactory.createSubject(s),
+                               subjectFactory.subjectClassKey()));
     }
 
     private void consumeSubjectJson(NetworkConfigService service,
@@ -225,7 +229,7 @@ public class NetworkConfigWebResource extends AbstractWebResource {
                                     String subjectKey) {
         subjectNode.fieldNames().forEachRemaining(c ->
             service.applyConfig(subject, service.getConfigClass(subjectKey, c),
-                                (ObjectNode) subjectNode.path(c)));
+                                subjectNode.path(c)));
     }
 
 
@@ -241,64 +245,62 @@ public class NetworkConfigWebResource extends AbstractWebResource {
         service.getSubjectClasses()
                 .forEach(subjectClass -> service.getSubjects(subjectClass)
                         .forEach(subject -> service.getConfigs(subject)
-                                .forEach(config -> service
-                                        .removeConfig(subject, config.getClass()))));
+                                .forEach(config -> service.removeConfig(subject, config.getClass()))));
         return Response.ok().build();
     }
 
     /**
      * Clear all network configurations for a subject class.
      *
-     * @param subjectKey subject class key
+     * @param subjectClassKey subject class key
      * @return empty response
      */
     @DELETE
-    @Path("{subjectKey}")
+    @Path("{subjectClassKey}")
     @SuppressWarnings("unchecked")
-    public Response delete(@PathParam("subjectKey") String subjectKey) {
+    public Response delete(@PathParam("subjectClassKey") String subjectClassKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
-        service.getSubjects(service.getSubjectFactory(subjectKey).getClass())
+        service.getSubjects(service.getSubjectFactory(subjectClassKey).getClass())
                 .forEach(subject -> service.getConfigs(subject)
-                        .forEach(config -> service
-                                .removeConfig(subject, config.getClass())));
+                        .forEach(config -> service.removeConfig(subject, config.getClass())));
         return Response.ok().build();
     }
 
     /**
-     * Clear all network configurations for a subject.
+     * Clear all network configurations for a subjectKey.
      *
-     * @param subjectKey subject class key
-     * @param subject    subject key
+     * @param subjectClassKey subjectKey class key
+     * @param subjectKey      subjectKey key
      * @return empty response
      */
     @DELETE
-    @Path("{subjectKey}/{subject}")
+    @Path("{subjectClassKey}/{subjectKey}")
     @SuppressWarnings("unchecked")
-    public Response delete(@PathParam("subjectKey") String subjectKey,
-                           @PathParam("subject") String subject) {
+    public Response delete(@PathParam("subjectClassKey") String subjectClassKey,
+                           @PathParam("subjectKey") String subjectKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
-        Object s = service.getSubjectFactory(subjectKey).createSubject(subject);
+        Object s = service.getSubjectFactory(subjectClassKey).createSubject(subjectKey);
         service.getConfigs(s).forEach(c -> service.removeConfig(s, c.getClass()));
         return Response.ok().build();
     }
 
     /**
-     * Clear specific network configuration for a subject.
+     * Clear specific network configuration for a subjectKey.
      *
-     * @param subjectKey subject class key
-     * @param subject    subject key
-     * @param configKey  configuration class key
+     * @param subjectClassKey subjectKey class key
+     * @param subjectKey      subjectKey key
+     * @param configKey       configuration class key
      * @return empty response
      */
     @DELETE
-    @Path("{subjectKey}/{subject}/{configKey}")
+    @Path("{subjectClassKey}/{subjectKey}/{configKey}")
     @SuppressWarnings("unchecked")
-    public Response delete(@PathParam("subjectKey") String subjectKey,
-                           @PathParam("subject") String subject,
+    public Response delete(@PathParam("subjectClassKey") String subjectClassKey,
+                           @PathParam("subjectKey") String subjectKey,
                            @PathParam("configKey") String configKey) {
         NetworkConfigService service = get(NetworkConfigService.class);
-        service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
-                             service.getConfigClass(subjectKey, configKey));
+        service.removeConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey),
+                             service.getConfigClass(subjectClassKey, configKey));
         return Response.ok().build();
     }
 
index baa1b1e..9714690 100644 (file)
  */
 package org.onosproject.rest.resources;
 
-import java.util.Set;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.rest.AbstractWebResource;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -23,14 +27,7 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.ElementId;
-import org.onosproject.net.HostId;
-import org.onosproject.net.topology.PathService;
-
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.rest.AbstractWebResource;
+import java.util.Set;
 
 /**
  * Compute paths in the network graph.
@@ -49,6 +46,17 @@ public class PathsWebResource extends AbstractWebResource {
         return id.matches("..:..:..:..:..:../.*") ? HostId.hostId(id) : null;
     }
 
+    /**
+     * Returns either host id or device id, depending on the ID format.
+     *
+     * @param id host or device id string
+     * @return element id
+     */
+    private ElementId elementId(String id) {
+        ElementId elementId = isHostId(id);
+        return elementId != null ? elementId : DeviceId.deviceId(id);
+    }
+
     /**
      * Get all shortest paths between any two hosts or devices.
      * Returns array of all shortest paths between any two elements.
@@ -63,23 +71,27 @@ public class PathsWebResource extends AbstractWebResource {
     public Response getPath(@PathParam("src") String src,
                             @PathParam("dst") String dst) {
         PathService pathService = get(PathService.class);
-
-        ElementId srcElement = isHostId(src);
-        ElementId dstElement = isHostId(dst);
-
-        if (srcElement == null) {
-            // Doesn't look like a host, assume it is a device
-            srcElement = DeviceId.deviceId(src);
-        }
-
-        if (dstElement == null) {
-            // Doesn't look like a host, assume it is a device
-            dstElement = DeviceId.deviceId(dst);
-        }
-
-        Set<org.onosproject.net.Path> paths = pathService.getPaths(srcElement, dstElement);
-        ObjectNode root = encodeArray(org.onosproject.net.Path.class, "paths", paths);
-        return ok(root).build();
+        Set<org.onosproject.net.Path> paths =
+                pathService.getPaths(elementId(src), elementId(dst));
+        return ok(encodeArray(org.onosproject.net.Path.class, "paths", paths)).build();
     }
 
+    /**
+     * Get all shortest disjoint paths between any two hosts or devices.
+     * Returns array of all shortest disjoint paths between any two elements.
+     *
+     * @param src source identifier
+     * @param dst destination identifier
+     * @return path data
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("{src}/{dst}/disjoint")
+    public Response getDisjointPath(@PathParam("src") String src,
+                                    @PathParam("dst") String dst) {
+        PathService pathService = get(PathService.class);
+        Set<org.onosproject.net.DisjointPath> paths =
+                pathService.getDisjointPaths(elementId(src), elementId(dst));
+        return ok(encodeArray(org.onosproject.net.DisjointPath.class, "paths", paths)).build();
+    }
 }
index 2ffa229..c91cb6d 100644 (file)
@@ -21,6 +21,7 @@ import java.util.stream.StreamSupport;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
@@ -31,7 +32,12 @@ import javax.ws.rs.core.UriInfo;
 
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.net.statistic.Load;
 import org.onosproject.net.statistic.StatisticService;
@@ -92,4 +98,59 @@ public class StatisticsWebResource  extends AbstractWebResource {
         result.set("loads", loads);
         return ok(result).build();
     }
+
+    /**
+     * Get table statistics for all tables of all devices.
+     *
+     * @return JSON encoded array of table statistics
+     */
+    @GET
+    @Path("flows/tables")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getTableStatistics() {
+        final FlowRuleService service = get(FlowRuleService.class);
+        final Iterable<Device> devices = get(DeviceService.class).getDevices();
+        final ObjectNode root = mapper().createObjectNode();
+        final ArrayNode rootArrayNode = root.putArray("device-table-statistics");
+        for (final Device device : devices) {
+            final ObjectNode deviceStatsNode = mapper().createObjectNode();
+            deviceStatsNode.put("device", device.id().toString());
+            final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics");
+            final Iterable<TableStatisticsEntry> tableStatsEntries = service.getFlowTableStatistics(device.id());
+            if (tableStatsEntries != null) {
+                for (final TableStatisticsEntry entry : tableStatsEntries) {
+                    statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this));
+                }
+            }
+            rootArrayNode.add(deviceStatsNode);
+        }
+
+        return ok(root).build();
+    }
+
+    /**
+     * Get table statistics for all tables of a specified device.
+     *
+     * @param deviceId device ID
+     * @return JSON encoded array of table statistics
+     */
+    @GET
+    @Path("flows/tables/{deviceId}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getTableStatisticsByDeviceId(@PathParam("deviceId") String deviceId) {
+        final FlowRuleService service = get(FlowRuleService.class);
+        final Iterable<TableStatisticsEntry> tableStatisticsEntries =
+                service.getFlowTableStatistics(DeviceId.deviceId(deviceId));
+        final ObjectNode root = mapper().createObjectNode();
+        final ArrayNode rootArrayNode = root.putArray("table-statistics");
+
+        final ObjectNode deviceStatsNode = mapper().createObjectNode();
+        deviceStatsNode.put("device", deviceId);
+        final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics");
+        for (final TableStatisticsEntry entry : tableStatisticsEntries) {
+            statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this));
+        }
+        rootArrayNode.add(deviceStatsNode);
+        return ok(root).build();
+    }
 }
index fb83cdd..53b16a6 100644 (file)
@@ -25,12 +25,16 @@ import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.Port;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.TableModel;
 import org.onosproject.ui.table.TableRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -38,7 +42,10 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
+import static com.google.common.base.Strings.emptyToNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.apache.commons.lang.WordUtils.capitalizeFully;
+import static org.onosproject.net.DeviceId.deviceId;
 
 /**
  * Message handler for device view related messages.
@@ -53,6 +60,11 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
     private static final String DEV_DETAILS_RESP = "deviceDetailsResponse";
     private static final String DETAILS = "details";
 
+    private static final String DEV_NAME_CHANGE_REQ = "deviceNameChangeRequest";
+    private static final String DEV_NAME_CHANGE_RESP = "deviceNameChangeResponse";
+
+    private static final String ZERO_URI = "of:0000000000000000";
+
     private static final String ID = "id";
     private static final String TYPE = "type";
     private static final String AVAILABLE = "available";
@@ -72,25 +84,41 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
     private static final String ENABLED = "enabled";
     private static final String SPEED = "speed";
     private static final String NAME = "name";
+    private static final String WARN = "warn";
 
 
     private static final String[] COL_IDS = {
-            AVAILABLE, AVAILABLE_IID, TYPE_IID, ID,
-            NUM_PORTS, MASTER_ID, MFR, HW, SW,
+            AVAILABLE, AVAILABLE_IID, TYPE_IID,
+            NAME, ID, MASTER_ID, NUM_PORTS, MFR, HW, SW,
             PROTOCOL, CHASSIS_ID, SERIAL
     };
 
     private static final String ICON_ID_ONLINE = "active";
     private static final String ICON_ID_OFFLINE = "inactive";
 
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+
     @Override
     protected Collection<RequestHandler> createRequestHandlers() {
         return ImmutableSet.of(
                 new DataRequestHandler(),
+                new NameChangeHandler(),
                 new DetailRequestHandler()
         );
     }
 
+    // Get friendly name of the device from the annotations
+    private static String deviceName(Device device) {
+        String name = device.annotations().value(AnnotationKeys.NAME);
+        return isNullOrEmpty(name) ? device.id().toString() : name;
+    }
+
+    private static String deviceProtocol(Device device) {
+        String protocol = device.annotations().value(PROTOCOL);
+        return protocol != null ? protocol : "";
+    }
+
     private static String getTypeIconId(Device d) {
         return DEV_ICON_PREFIX + d.type().toString();
     }
@@ -121,16 +149,15 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
             boolean available = ds.isAvailable(id);
             String iconId = available ? ICON_ID_ONLINE : ICON_ID_OFFLINE;
 
-            String protocol = dev.annotations().value(PROTOCOL);
-
             row.cell(ID, id)
+                .cell(NAME, deviceName(dev))
                 .cell(AVAILABLE, available)
                 .cell(AVAILABLE_IID, iconId)
                 .cell(TYPE_IID, getTypeIconId(dev))
                 .cell(MFR, dev.manufacturer())
                 .cell(HW, dev.hwVersion())
                 .cell(SW, dev.swVersion())
-                .cell(PROTOCOL, protocol != null ? protocol : "")
+                .cell(PROTOCOL, deviceProtocol(dev))
                 .cell(NUM_PORTS, ds.getPorts(id).size())
                 .cell(MASTER_ID, ms.getMasterFor(id));
         }
@@ -144,15 +171,16 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
 
         @Override
         public void process(long sid, ObjectNode payload) {
-            String id = string(payload, "id", "of:0000000000000000");
+            String id = string(payload, ID, ZERO_URI);
 
-            DeviceId deviceId = DeviceId.deviceId(id);
+            DeviceId deviceId = deviceId(id);
             DeviceService service = get(DeviceService.class);
             MastershipService ms = get(MastershipService.class);
             Device device = service.getDevice(deviceId);
-            ObjectNode data = MAPPER.createObjectNode();
+            ObjectNode data = objectNode();
 
             data.put(ID, deviceId.toString());
+            data.put(NAME, deviceName(device));
             data.put(TYPE, capitalizeFully(device.type().toString()));
             data.put(TYPE_IID, getTypeIconId(device));
             data.put(MFR, device.manufacturer());
@@ -161,9 +189,9 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
             data.put(SERIAL, device.serialNumber());
             data.put(CHASSIS_ID, device.chassisId().toString());
             data.put(MASTER_ID, ms.getMasterFor(deviceId).toString());
-            data.put(PROTOCOL, device.annotations().value(PROTOCOL));
+            data.put(PROTOCOL, deviceProtocol(device));
 
-            ArrayNode ports = MAPPER.createArrayNode();
+            ArrayNode ports = arrayNode();
 
             List<Port> portList = new ArrayList<>(service.getPorts(deviceId));
             Collections.sort(portList, (p1, p2) -> {
@@ -176,13 +204,13 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
             }
             data.set(PORTS, ports);
 
-            ObjectNode rootNode = MAPPER.createObjectNode();
+            ObjectNode rootNode = objectNode();
             rootNode.set(DETAILS, data);
             sendMessage(DEV_DETAILS_RESP, 0, rootNode);
         }
 
         private ObjectNode portData(Port p, DeviceId id) {
-            ObjectNode port = MAPPER.createObjectNode();
+            ObjectNode port = objectNode();
             LinkService ls = get(LinkService.class);
             String name = p.annotations().value(AnnotationKeys.PORT_NAME);
 
@@ -206,4 +234,29 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
             return port;
         }
     }
+
+
+    // handler for changing device friendly name
+    private final class NameChangeHandler extends RequestHandler {
+        private NameChangeHandler() {
+            super(DEV_NAME_CHANGE_REQ);
+        }
+
+        @Override
+        public void process(long sid, ObjectNode payload) {
+            DeviceId deviceId = deviceId(string(payload, ID, ZERO_URI));
+            String name = emptyToNull(string(payload, NAME, null));
+            log.debug("Name change request: {} -- '{}'", deviceId, name);
+
+            NetworkConfigService service = get(NetworkConfigService.class);
+            BasicDeviceConfig cfg =
+                    service.addConfig(deviceId, BasicDeviceConfig.class);
+
+            // Name attribute missing from the payload (or empty string)
+            // means that the friendly name should be unset.
+            cfg.name(name);
+            cfg.apply();
+            sendMessage(DEV_NAME_CHANGE_RESP, 0, payload);
+        }
+    }
 }
diff --git a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java
new file mode 100644 (file)
index 0000000..5d97504
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ui.impl;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.packet.PacketProcessorEntry;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.TableModel;
+import org.onosproject.ui.table.TableRequestHandler;
+import org.onosproject.ui.table.cell.NumberFormatter;
+
+import java.util.Collection;
+
+import static org.onosproject.net.packet.PacketProcessor.ADVISOR_MAX;
+import static org.onosproject.net.packet.PacketProcessor.DIRECTOR_MAX;
+
+/**
+ * Message handler for packet processor view related messages.
+ */
+public class ProcessorViewMessageHandler extends UiMessageHandler {
+
+    private static final String PROCESSOR_DATA_REQ = "processorDataRequest";
+    private static final String PROCESSOR_DATA_RESP = "processorDataResponse";
+    private static final String PROCESSORS = "processors";
+
+    private static final String OBSERVER = "observer";
+    private static final String DIRECTOR = "director";
+    private static final String ADVISOR = "advisor";
+
+    private static final String ID = "id";
+    private static final String TYPE = "type";
+    private static final String PRIORITY = "priority";
+    private static final String PROCESSOR = "processor";
+    private static final String PACKETS = "packets";
+    private static final String AVG_MS = "avgMillis";
+
+    private static final long NANOS_IN_MS = 1_000_000;
+
+    private static final String[] COL_IDS = {
+            ID, TYPE, PRIORITY, PROCESSOR, PACKETS, AVG_MS
+    };
+
+    @Override
+    protected Collection<RequestHandler> createRequestHandlers() {
+        return ImmutableSet.of(new ProcessorDataRequest());
+    }
+
+    // handler for packet processor table requests
+    private final class ProcessorDataRequest extends TableRequestHandler {
+        private ProcessorDataRequest() {
+            super(PROCESSOR_DATA_REQ, PROCESSOR_DATA_RESP, PROCESSORS);
+        }
+
+        @Override
+        protected String[] getColumnIds() {
+            return COL_IDS;
+        }
+
+        @Override
+        protected TableModel createTableModel() {
+            TableModel tm = super.createTableModel();
+            tm.setFormatter(AVG_MS, new NumberFormatter());
+            return tm;
+        }
+
+        @Override
+        protected void populateTable(TableModel tm, ObjectNode payload) {
+            PacketService ps = get(PacketService.class);
+            ps.getProcessors().forEach(entry -> populateRow(tm.addRow(), entry));
+        }
+
+        private void populateRow(TableModel.Row row, PacketProcessorEntry entry) {
+            row.cell(ID, entry.priority())
+                    .cell(TYPE, processorType(entry.priority()))
+                    .cell(PRIORITY, processorPriority(entry.priority()))
+                    .cell(PROCESSOR, entry.processor().getClass().getName())
+                    .cell(PACKETS, entry.invocations())
+                    .cell(AVG_MS, (double) entry.averageNanos() / NANOS_IN_MS);
+        }
+
+        private String processorType(int p) {
+            return p > DIRECTOR_MAX ? OBSERVER : p > ADVISOR_MAX ? DIRECTOR : ADVISOR;
+        }
+
+        private int processorPriority(int p) {
+            return p > DIRECTOR_MAX ? (p - DIRECTOR_MAX - 1) :
+                    p > ADVISOR_MAX ? (p - ADVISOR_MAX - 1) : (p - 1);
+        }
+
+    }
+}
index 2bd0bb6..86cf038 100644 (file)
@@ -77,6 +77,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
                 new UiView(PLATFORM, "app", "Applications", "nav_apps"),
                 new UiView(PLATFORM, "settings", "Settings", "nav_settings"),
                 new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"),
+                new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"),
                 new UiView(NETWORK, "topo", "Topology", "nav_topo"),
                 new UiView(NETWORK, "device", "Devices", "nav_devs"),
                 new UiViewHidden("flow"),
@@ -102,6 +103,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
                         new ApplicationViewMessageHandler(),
                         new SettingsViewMessageHandler(),
                         new ClusterViewMessageHandler(),
+                        new ProcessorViewMessageHandler(),
                         new TunnelViewMessageHandler()
                 );
 
index 838a2ac..28f262a 100644 (file)
             unknown: "M35,40a5,5,0,0,1,5-5h30a5,5,0,0,1,5,5v30a5,5,0,0,1-5,5" +
             "h-30a5,5,0,0,1-5-5z",
 
+            query: "M51.4,69.9c0-0.9,0-1.6,0-2.1c0-2.7,0.4-5.1,1.2-7.1" +
+            "c0.6-1.5,1.5-3,2.8-4.5c0.9-1.1,2.6-2.7,5.1-4.8c2.4-2.1,4-3.8," +
+            "4.8-5.1 c0.7-1.3,1.1-2.6,1.1-4.1c0-2.7-1.1-5.1-3.2-7.1c-2.1-2" +
+            "-4.8-3.1-7.9-3.1c-3,0-5.5,0.9-7.5,2.8c-2,1.9-3.3,4.8-4,8.7l-7.2" +
+            "-0.8 c0.7-5.3,2.6-9.3,5.8-12.1c3.2-2.8,7.5-4.2,12.8-4.2c5.6,0," +
+            "10.1,1.5,13.4,4.5c3.3,3,5,6.7,5,10.9c0,2.5-0.6,4.8-1.8,6.8 " +
+            "s-3.5,4.6-6.9,7.6c-2.3,2-3.8,3.5-4.5,4.4c-0.7,1-1.2,2-1.6,3.3" +
+            "c-0.3,1.2-0.5,3.2-0.6,6H51.4z M51,83.8v-7.9h8v7.9H51z",
+
             node: "M15,100a5,5,0,0,1-5-5v-65a5,5,0,0,1,5-5h80a5,5,0,0,1,5,5" +
             "v65a5,5,0,0,1-5,5zM14,22.5l11-11a10,3,0,0,1,10-2h40a10,3,0,0,1," +
             "10,2l11,11zM16,35a5,5,0,0,1,10,0a5,5,0,0,1-10,0z",
             "M50,29l12,0,0-8,18,13-18,13,0-8-12,0zM60,57l-12,0,0-8-18,13," +
             "18,13,0-8,12,0z",
 
+            microwave: "M85,71.2c-8.9,10.5-29.6,8.7-45.3-3.5C23.9,55.4,19.8," +
+            "37,28.6,26.5C29.9,38.6,71.5,69.9,85,71.2z M92.7,76.2M16.2,15 " +
+            "M69.5,100.7v-4c0-1.4-1.2-2.2-2.6-2.2H19.3c-1.4,0-2.8,0.7-2.8,2.2" +
+            "v3.9c0,0.7,0.8,1,1.5,1h50.3C69,101.5,69.5,101.3,69.5,100.7z " +
+            "M77.3,7.5l0,3.7c9,0.1,16.3,7.1,16.2,15.7l3.9,0C97.5,16.3,88.5," +
+            "7.6,77.3,7.5z M77.6,14.7l0,2.5c5.3,0,9.7,4.2,9.6,9.3l2.6,0C89.9" +
+            ",20,84.4,14.7,77.6,14.7z M82.3,22.2c-1.3-1.2-2.9-1.9-4.7-1.9" +
+            "l0,1.2c1.4,0,2.8,0.6,3.8,1.5c1,1,1.6,2.3,1.6,3.7l1.3,0C84.3,25.1," +
+            "83.6,23.4,82.3,22.2z M38.9,69.5l-5.1,23h16.5l-2.5-17.2C44.1,73.3," +
+            "38.9,69.5,38.9,69.5zM58.1,54.1c13.7,10.1,26.5,16.8,29.2,13.7" +
+            "c2.7-3.1-5.6-13-19.3-24.4 M62.9,34.2 M62,37.9C47.7,27.3,33.7,20," +
+            "31,23.1c-2.7,3.2,7,14.2,20.6,26 M73.9,25.7c-2.9,0.1-5.2,2.3-5.1," +
+            "4.8c0,0.7,0.2,1.4,0.6,2l0,0L53.8,49.7l3.3,2.5L72.7,35l-0.4-0.3" +
+            "c0.6,0.2,1.3,0.3,1.9,0.3c2.9-0.1,5.2-2.3,5.1-4.9C79.3,27.6,76.8," +
+            "25.6,73.9,25.7z",
+
             chain: "M60.4,77.6c-4.9,5.2-9.6,11.3-15.3,16.3c-8.6,7.5-20.4,6.8" +
             "-28-0.8c-7.7-7.7-8.4-19.6-0.8-28.4c6.5-7.4,13.5-14.4,20.9-20.9" +
             "c7.5-6.7,19.2-6.7,26.5-0.8c3.5,2.8,4.4,6.1,2.2,8.7c-2.7,3.1" +
         return glyphs.get(id);
     }
 
+    function glyphDefined(id) {
+        return glyphs.has(id);
+    }
+
     // Note: defs should be a D3 selection of a single <defs> element
     function loadDefs(defs, glyphIds, noClear) {
         var list = fs.isA(glyphIds) || ids(),
                 registerGlyphSet: registerGlyphSet,
                 ids: ids,
                 glyph: glyph,
+                glyphDefined: glyphDefined,
                 loadDefs: loadDefs,
                 addGlyph: addGlyph
             };
index ba79431..15b44bc 100644 (file)
@@ -64,7 +64,8 @@
         nav_devs: 'switch',
         nav_links: 'ports',
         nav_hosts: 'endstation',
-        nav_intents: 'relatedIntents'
+        nav_intents: 'relatedIntents',
+        nav_processors: 'allTraffic'
     };
 
     function ensureIconLibDefs() {
     // Returns the D3 selection of the icon.
     function addDeviceIcon(elem, glyphId) {
         var cfg = config.device,
+            gid = gs.glyphDefined(glyphId) ? glyphId : 'query',
             g = elem.append('g')
                 .attr('class', 'svgIcon deviceIcon');
 
         });
 
         g.append('use').attr({
-            'xlink:href': '#' + glyphId,
+            'xlink:href': '#' + gid,
             width: cfg.dim,
             height: cfg.dim
         });
index 2985565..5ff4f7f 100644 (file)
@@ -25,6 +25,7 @@
 
     // internal state
     var enabled = true,
+        globalEnabled = true,
         keyHandler = {
             globalKeys: {},
             maskedKeys: {},
     }
 
     function quickHelp(view, key, code, ev) {
+        if (!globalEnabled) {
+            return false;
+        }
         qhs.showQuickHelp(keyHandler);
         return true;
     }
     }
 
     function toggleTheme(view, key, code, ev) {
+        if (!globalEnabled) {
+            return false;
+        }
         ts.toggleTheme();
         return true;
     }
         keyHandler.viewGestures = [];
     }
 
+    function checkNotGlobal(o) {
+        var oops = [];
+        if (fs.isO(o)) {
+            angular.forEach(o, function (val, key) {
+                if (keyHandler.globalKeys[key]) {
+                    oops.push(key);
+                }
+            });
+            if (oops.length) {
+                $log.warn('Ignoring reserved global key(s):', oops.join(','));
+                oops.forEach(function (key) {
+                    delete o[key];
+                });
+            }
+        }
+    }
+
     angular.module('onosUtil')
     .factory('KeyService',
         ['$log', 'FnService', 'ThemeService', 'NavService',
                 },
                 enableKeys: function (b) {
                     enabled = b;
-                }
+                },
+                enableGlobalKeys: function (b) {
+                    globalEnabled = b;
+                },
+                checkNotGlobal: checkNotGlobal
             };
     }]);
 
index fc08f68..e0e9cf5 100644 (file)
     margin: 8px 0;
 }
 
+#device-details-panel .editable {
+    cursor: pointer;
+    border-bottom: 1px dashed darkgreen;
+}
+
+#device-details-panel h2 input {
+    font-size: 1.0em;
+}
+
 #device-details-panel .top div.left {
     float: left;
     padding: 0 18px 0 0;
index 5d51d1d..63a04db 100644 (file)
                 <tr>
                     <td colId="available" class="table-icon" sortable></td>
                     <td colId="type" class="table-icon" sortable></td>
+                    <td colId="name" sortable>Friendly Name </td>
                     <td colId="id" sortable>Device ID </td>
                     <td colId="masterid" sortable>Master Instance </td>
-                    <td colId="num_ports" sortable>Ports </td>
+                    <td colId="num_ports" col-width="60px" sortable>Ports </td>
                     <td colId="mfr" sortable>Vendor </td>
                     <td colId="hw" sortable>H/W Version </td>
                     <td colId="sw" sortable>S/W Version </td>
-                    <td colId="protocol" sortable>Protocol </td>
+                    <td colId="protocol" col-width="80px" sortable>Protocol </td>
                 </tr>
             </table>
         </div>
@@ -64,6 +65,7 @@
                     <td class="table-icon">
                         <div icon icon-id="{{dev._iconid_type}}"></div>
                     </td>
+                    <td>{{dev.name}}</td>
                     <td>{{dev.id}}</td>
                     <td>{{dev.masterid}}</td>
                     <td>{{dev.num_ports}}</td>
index 7a2dc4f..5b7120f 100644 (file)
     'use strict';
 
     // injected refs
-    var $log, $scope, $location, fs, mast, ps, wss, is, ns;
+    var $log, $scope, $loc, fs, mast, ps, wss, is, ns, ks;
 
     // internal state
     var detailsPanel,
-        pStartY, pHeight,
-        top, bottom, iconDiv,
-        wSize;
+        pStartY,
+        pHeight,
+        top,
+        bottom,
+        iconDiv,
+        wSize,
+        editingName = false;
 
     // constants
     var topPdg = 13,
         pName = 'device-details-panel',
         detailsReq = 'deviceDetailsRequest',
         detailsResp = 'deviceDetailsResponse',
+        nameChangeReq = 'deviceNameChangeRequest',
+        nameChangeResp = 'deviceNameChangeResponse',
 
         propOrder = [
-            'type', 'masterid', 'chassisid',
+            'id', 'type', 'masterid', 'chassisid',
             'mfr', 'hw', 'sw', 'protocol', 'serial'
         ],
         friendlyProps = [
-            'Type', 'Master ID', 'Chassis ID',
+            'URI', 'Type', 'Master ID', 'Chassis ID',
             'Vendor', 'H/W Version', 'S/W Version', 'Protocol', 'Serial #'
         ],
         portCols = [
@@ -59,7 +65,9 @@
         if (detailsPanel.isVisible()) {
             $scope.selId = null;
             detailsPanel.hide();
+            return true;
         }
+        return false;
     }
 
     function addCloseBtn(div) {
         div.on('click', closePanel);
     }
 
+    function exitEditMode(nameH2, name) {
+        nameH2.html(name);
+        nameH2.classed('editable', true);
+        editingName = false;
+        ks.enableGlobalKeys(true);
+    }
+
+    function editNameSave() {
+        var nameH2 = top.select('h2'),
+            id = $scope.panelData.id,
+            val,
+            newVal;
+
+        if (editingName) {
+            val = nameH2.select('input').property('value').trim();
+            newVal = val || id;
+
+            exitEditMode(nameH2, newVal);
+            $scope.panelData.name = newVal;
+            wss.sendEvent(nameChangeReq, { id: id, name: val });
+        }
+    }
+
+    function editNameCancel() {
+        if (editingName) {
+            exitEditMode(top.select('h2'), $scope.panelData.name);
+            return true;
+        }
+        return false;
+    }
+
+    function editName() {
+        var nameH2 = top.select('h2'),
+            tf, el;
+
+        if (!editingName) {
+            nameH2.classed('editable', false);
+            nameH2.html('');
+            tf = nameH2.append('input').classed('name-input', true)
+                .attr('type', 'text')
+                .attr('value', $scope.panelData.name);
+            el = tf[0][0];
+            el.focus();
+            el.select();
+            editingName = true;
+            ks.enableGlobalKeys(false);
+        }
+    }
+
+    function handleEscape() {
+        return editNameCancel() || closePanel();
+    }
+
     function setUpPanel() {
         var container, closeBtn, tblDiv;
         detailsPanel.empty();
         closeBtn = top.append('div').classed('close-btn', true);
         addCloseBtn(closeBtn);
         iconDiv = top.append('div').classed('dev-icon', true);
-        top.append('h2');
+        top.append('h2').classed('editable', true).on('click', editName);
 
         tblDiv = top.append('div').classed('top-tables', true);
         tblDiv.append('div').classed('left', true).append('table');
                         .append('tbody');
 
         is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
-        top.select('h2').html(details.id);
+        top.select('h2').html(details.name);
 
         propOrder.forEach(function (prop, i) {
             // properties are split into two tables
-            addProp(i < 3 ? leftTbl : rightTbl, i, details[prop]);
+            addProp(i < 4 ? leftTbl : rightTbl, i, details[prop]);
         });
     }
 
         detailsPanel.width(tbWidth + ctnrPdg);
     }
 
+    function populateName(div, name) {
+        var lab = div.select('.label'),
+            val = div.select('.value');
+        lab.html('Friendly Name:');
+        val.html(name);
+    }
+
     function populateDetails(details) {
-        var topTbs, btmTbl, ports;
+        var nameDiv, topTbs, btmTbl, ports;
         setUpPanel();
 
+        nameDiv = top.select('.name-div');
         topTbs = top.select('.top-tables');
         btmTbl = bottom.select('table');
         ports = details.ports;
 
+        populateName(nameDiv, details.name);
         populateTop(topTbs, details);
         populateBottom(btmTbl, ports);
 
         $scope.$apply();
     }
 
+    function respNameCb(data) {
+        if (data.warn) {
+            $log.warn(data.warn, data.id);
+            top.select('h2').html(data.id);
+        }
+    }
+
     function createDetailsPane() {
         detailsPanel = ps.createPanel(pName, {
             width: wSize.width,
     .controller('OvDeviceCtrl',
         ['$log', '$scope', '$location', 'TableBuilderService', 'FnService',
             'MastService', 'PanelService', 'WebSocketService', 'IconService',
-            'NavService',
+            'NavService', 'KeyService',
 
         function (_$log_, _$scope_, _$location_,
-                  tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_) {
+                  tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_, _ks_) {
+            var params,
+                handlers = {};
+
             $log = _$log_;
             $scope = _$scope_;
-            $location = _$location_;
+            $loc = _$location_;
             fs = _fs_;
             mast = _mast_;
             ps = _ps_;
             wss = _wss_;
             is = _is_;
             ns = _ns_;
-            var params = $location.search(),
-                handlers = {};
+            ks = _ks_;
+
+            params = $loc.search();
+
             $scope.panelData = {};
             $scope.flowTip = 'Show flow view for selected device';
             $scope.portTip = 'Show port view for selected device';
 
             // details panel handlers
             handlers[detailsResp] = respDetailsCb;
+            handlers[nameChangeResp] = respNameCb;
             wss.bindHandlers(handlers);
 
             // query for if a certain device needs to be highlighted
             }
             // create key bindings to handle panel
             ks.keyBindings({
-                esc: [closePanel, 'Close the details panel'],
+                enter: editNameSave,
+                esc: [handleEscape, 'Close the details panel'],
                 _helpFormat: ['esc']
             });
             ks.gestureNotes([
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css
new file mode 100644 (file)
index 0000000..12cf637
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Processor View -- CSS file
+ */
+
+#ov-processor h2 {
+    display: inline-block;
+}
+
+#ov-processor div.ctrl-btns {
+    width: 40px;
+}
+
+.light #ov-processor .current-view use {
+    fill: white;
+}
+.dark #ov-processor .current-view use {
+    fill: #304860;
+}
+
+.light #ov-processor .current-view rect {
+    fill: deepskyblue;
+}
+.dark #ov-processor .current-view rect {
+    fill: #eee;
+}
+
+#ov-processor td.number {
+    text-align: right;
+}
+
+#ov-processor tr.no-data td {
+    text-align: center;
+}
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html
new file mode 100644 (file)
index 0000000..1c61504
--- /dev/null
@@ -0,0 +1,63 @@
+<!-- processor partial HTML -->
+<div id="ov-processor">
+    <div class="tabular-header">
+        <h2>
+            Packet Processors ({{tableData.length}} Processors total)
+        </h2>
+        <div class="ctrl-btns">
+            <div class="refresh" ng-class="{active: autoRefresh}"
+                 icon icon-size="36" icon-id="refresh"
+                 tooltip tt-msg="autoRefreshTip"
+                 ng-click="toggleRefresh()"></div>
+            <!--
+            <div class="separator"></div>
+
+            <div class="current-view"
+                 icon icon-id="processorTable" icon-size="36"></div>
+
+            <div class="active"
+                 icon icon-id="requestTable" icon-size="36"git sta
+                 tooltip tt-msg="requestTip"
+                 ng-click="nav('request')"></div>
+            -->
+        </div>
+    </div>
+
+    <div class="summary-list" onos-table-resize>
+        <div ng-show="loading" class="loading-wheel"
+             icon icon-id="loading" icon-size="75"></div>
+
+        <div class="table-header" onos-sortable-header>
+            <table>
+                <tr>
+                    <td class="number" colId="priority" sortable col-width="80px">Priority </td>
+                    <td colId="type" sortable col-width="80px">Type </td>
+                    <td colId="processor" sortable col-width="500px">Class </td>
+                    <td class="number"  colId="packets" sortable col-width="100px">Packets </td>
+                    <td class="number" colId="avgMillis" sortable col-width="100px">Average (ms) </td>
+                </tr>
+            </table>
+        </div>
+
+        <div class="table-body">
+            <table onos-flash-changes id-prop="id">
+                <tr ng-if="!tableData.length" class="no-data">
+                    <td colspan="5">
+                        No Processors found
+                    </td>
+                </tr>
+
+                <tr ng-repeat="processor in tableData track by $index"
+                    ng-repeat-complete row-id="{{processor.id}}">
+                    <td class="number">{{processor.priority}}</td>
+                    <td>{{processor.type}}</td>
+                    <td>{{processor.processor}}</td>
+                    <td class="number">{{processor.packets}}</td>
+                    <td class="number">{{processor.avgMillis}}</td>
+                </tr>
+            </table>
+        </div>
+
+    </div>
+
+</div>
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js
new file mode 100644 (file)
index 0000000..89d717b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Packet Processor View Module
+ */
+
+(function () {
+    'use strict';
+
+    // injected references
+    var $log, $scope, $location, fs, tbs, ns;
+
+    angular.module('ovProcessor', [])
+    .controller('OvProcessorCtrl',
+        ['$log', '$scope', '$location',
+            'FnService', 'TableBuilderService', 'NavService',
+
+        function (_$log_, _$scope_, _$location_, _fs_, _tbs_, _ns_) {
+            var params;
+            $log = _$log_;
+            $scope = _$scope_;
+            $location = _$location_;
+            fs = _fs_;
+            tbs = _tbs_;
+            ns = _ns_;
+            $scope.requestTip = 'Show packet requests';
+
+            params = $location.search();
+
+            tbs.buildTable({
+                scope: $scope,
+                tag: 'processor',
+                query: params
+            });
+
+            $scope.nav = function (path) {
+                if ($scope.devId) {
+                    ns.navTo(path);
+                }
+            };
+
+            $log.log('OvProcessorCtrl has been created');
+        }]);
+}());
index ee069d3..6108101 100644 (file)
@@ -17,7 +17,7 @@
         <div class="table-header" onos-sortable-header>
             <table>
                 <tr>
-                    <td colId="component" sortable col-width="200px">Component </td>
+                    <td colId="component" sortable col-width="300px">Component </td>
                     <td colId="id" sortable>Property </td>
                     <td colId="type" sortable col-width="70px">Type </td>
                     <td colId="value" sortable>Value </td>
index 2189410..42b6f4b 100644 (file)
@@ -95,6 +95,9 @@
     // and include them in the quick-help panel
     function mergeKeys(extra) {
         var _hf = actionMap._helpFormat[2];
+
+        ks.checkNotGlobal(extra);
+
         extra._keyOrder.forEach(function (k) {
             var d = extra[k],
                 cb = d && d.cb,
index 154f941..5df3c66 100644 (file)
     <script src="app/view/app/app.js"></script>
     <script src="app/view/settings/settings.js"></script>
     <script src="app/view/cluster/cluster.js"></script>
+    <script src="app/view/processor/processor.js"></script>
     <script src="app/view/tunnel/tunnel.js"></script>
 
     <!-- This is where contributed javascript will get injected -->
     <link rel="stylesheet" href="app/view/app/app.css">
     <link rel="stylesheet" href="app/view/settings/settings.css">
     <link rel="stylesheet" href="app/view/cluster/cluster.css">
+    <link rel="stylesheet" href="app/view/processor/processor.css">
     <link rel="stylesheet" href="app/view/tunnel/tunnel.css">
 
     <!-- This is where contributed stylesheets will get injected -->
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json
new file mode 100644 (file)
index 0000000..232d4ed
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000009",
+    "type": "ids",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "ids",
+      "of:0000000000000009"
+    ],
+    "metaUi": {
+      "x": 200,
+      "y": 400
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json
new file mode 100644 (file)
index 0000000..abd24d6
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:00000000000000010",
+    "type": "controller",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "controller",
+      "of:0000000000000010"
+    ],
+    "metaUi": {
+      "x": 350,
+      "y": 400
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json
new file mode 100644 (file)
index 0000000..1fe1837
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:00000000000000011",
+    "type": "virtual",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "virtual",
+      "of:0000000000000011"
+    ],
+    "metaUi": {
+      "x": 500,
+      "y": 400
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json
new file mode 100644 (file)
index 0000000..954376c
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:00000000000000012",
+    "type": "fiber_switch",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "fiber_switch",
+      "of:0000000000000012"
+    ],
+    "metaUi": {
+      "x": 650,
+      "y": 400
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json
new file mode 100644 (file)
index 0000000..3d40cec
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:00000000000000013",
+    "type": "microwave",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "microwave",
+      "of:0000000000000013"
+    ],
+    "metaUi": {
+      "x": 300,
+      "y": 500
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json
new file mode 100644 (file)
index 0000000..e33532d
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:00000000000000014",
+    "type": "other",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "other",
+      "of:0000000000000014"
+    ],
+    "metaUi": {
+      "x": 450,
+      "y": 500
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json
new file mode 100644 (file)
index 0000000..1773c91
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:00000000000000015",
+    "type": "-unmatched-",
+    "online": true,
+    "master": "ONOS-B",
+    "labels": [
+      "",
+      "-unmatched-",
+      "of:0000000000000015"
+    ],
+    "metaUi": {
+      "x": 600,
+      "y": 500
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json
new file mode 100644 (file)
index 0000000..20be9e2
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "event": "addInstance",
+  "payload": {
+    "id": "ONOS",
+    "ip": "192.168.56.101",
+    "online": true,
+    "uiAttached": true,
+    "switches": 4,
+    "labels": [
+      "ONOS",
+      "192.168.56.101"
+    ]
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json
new file mode 100644 (file)
index 0000000..5f8ad66
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000001",
+    "type": "switch",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "switch",
+      "of:0000000000000001"
+    ],
+    "metaUi": {
+      "x": 200,
+      "y": 200
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json
new file mode 100644 (file)
index 0000000..b6da44c
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000002",
+    "type": "router",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "router",
+      "of:0000000000000002"
+    ],
+    "metaUi": {
+      "x": 350,
+      "y": 200
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json
new file mode 100644 (file)
index 0000000..468749e
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000003",
+    "type": "roadm",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "roadm",
+      "of:0000000000000003"
+    ],
+    "metaUi": {
+      "x": 500,
+      "y": 200
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json
new file mode 100644 (file)
index 0000000..c8e234b
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000004",
+    "type": "otn",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "otn",
+      "of:0000000000000004"
+    ],
+    "metaUi": {
+      "x": 650,
+      "y": 200
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json
new file mode 100644 (file)
index 0000000..c54cc4a
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000005",
+    "type": "roadm_otn",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "roadm_otn",
+      "of:0000000000000005"
+    ],
+    "metaUi": {
+      "x": 300,
+      "y": 300
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json
new file mode 100644 (file)
index 0000000..19d50d4
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000006",
+    "type": "firewall",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "firewall",
+      "of:0000000000000006"
+    ],
+    "metaUi": {
+      "x": 450,
+      "y": 300
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json
new file mode 100644 (file)
index 0000000..d9a3b79
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000007",
+    "type": "balancer",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "balancer",
+      "of:0000000000000007"
+    ],
+    "metaUi": {
+      "x": 600,
+      "y": 300
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json
new file mode 100644 (file)
index 0000000..c89f565
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000008",
+    "type": "ips",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "ips",
+      "of:0000000000000008"
+    ],
+    "metaUi": {
+      "x": 750,
+      "y": 300
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json
new file mode 100644 (file)
index 0000000..ee3c1ea
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "comments": [
+    "Showing all device types"
+  ],
+  "title": "Show Device Types",
+  "params": {
+    "lastAuto": 16
+  },
+  "description": [
+    "Show all device types."
+  ]
+}