From 6a07d2d622eaa06953f3353e39c080984076e8de Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Fri, 9 Oct 2015 18:32:44 -0700 Subject: Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986 Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f --- framework/src/onos/apps/aaa/pom.xml | 25 +- .../aaa/src/main/java/org/onosproject/aaa/AAA.java | 592 ++++++-------- .../main/java/org/onosproject/aaa/AAAConfig.java | 239 ++++++ .../java/org/onosproject/aaa/StateMachine.java | 351 ++++---- .../org/onosproject/aaa/StateMachineException.java | 28 + .../StateMachineInvalidTransitionException.java | 27 + .../src/test/java/org/onosproject/aaa/AAATest.java | 469 ++++++++++- .../java/org/onosproject/aaa/StateMachineTest.java | 132 ++- framework/src/onos/apps/acl/pom.xml | 30 +- .../src/main/java/org/onosproject/acl/AclRule.java | 290 +++++++ .../main/java/org/onosproject/acl/AclService.java | 56 ++ .../main/java/org/onosproject/acl/AclStore.java | 146 ++++ .../java/org/onosproject/acl/AclWebResource.java | 191 +++++ .../src/main/java/org/onosproject/acl/RuleId.java | 85 ++ .../java/org/onosproject/acl/impl/AclManager.java | 338 ++++++++ .../onosproject/acl/impl/DistributedAclStore.java | 251 ++++++ .../org/onosproject/acl/impl/package-info.java | 20 + .../java/org/onosproject/acl/package-info.java | 20 + .../onos/apps/acl/src/main/webapp/WEB-INF/web.xml | 2 +- .../org/onosproject/acl/AclWebResourceTest.java | 142 ++++ framework/src/onos/apps/bgprouter/pom.xml | 1 - .../org/onosproject/bgprouter/IcmpHandler.java | 1 + .../onosproject/cordfabric/CordFabricManager.java | 2 +- .../main/java/org/onosproject/cordvtn/CordVtn.java | 178 ++-- .../org/onosproject/cordvtn/CordVtnConfig.java | 22 +- .../onosproject/cordvtn/CordVtnConfigManager.java | 144 ++++ .../org/onosproject/cordvtn/CordVtnService.java | 36 +- .../org/onosproject/cordvtn/DefaultOvsdbNode.java | 40 +- .../onosproject/cordvtn/NodeConnectionManager.java | 166 ++-- .../java/org/onosproject/cordvtn/OvsdbNode.java | 25 +- .../main/java/org/onosproject/dhcp/DhcpStore.java | 2 +- .../org/onosproject/dhcp/impl/DhcpManager.java | 162 ++-- .../dhcp/impl/DistributedDhcpStore.java | 4 +- .../org/onosproject/dhcp/impl/DhcpManagerTest.java | 10 +- framework/src/onos/apps/flowanalyzer/pom.xml | 32 + .../flowanalyzer/FlowAnalysisCommand.java | 33 + .../org/onosproject/flowanalyzer/FlowAnalyzer.java | 213 ++++- .../resources/OSGI-INF/blueprint/shell-config.xml | 23 + .../flowanalyzer/DefaultMutableTopologyGraph.java | 28 + .../onosproject/flowanalyzer/FlowAnalyzerTest.java | 120 +++ .../flowanalyzer/MockFlowRuleService.java | 103 +++ .../onosproject/flowanalyzer/MockLinkService.java | 183 +++++ .../flowanalyzer/MockTopologyService.java | 21 + framework/src/onos/apps/igmp/pom.xml | 117 +++ .../org/onosproject/igmp/impl/IGMPComponent.java | 155 ++++ .../igmp/impl/IGMPProcessMembership.java | 39 + .../onosproject/igmp/impl/IGMPProcessQuery.java | 39 + framework/src/onos/apps/mfwd/pom.xml | 142 ++++ .../onosproject/mfwd/cli/McastDeleteCommand.java | 45 + .../org/onosproject/mfwd/cli/McastJoinCommand.java | 72 ++ .../org/onosproject/mfwd/cli/McastShowCommand.java | 62 ++ .../org/onosproject/mfwd/cli/package-info.java | 5 + .../java/org/onosproject/mfwd/impl/MRibCodec.java | 211 +++++ .../onosproject/mfwd/impl/McastConnectPoint.java | 68 ++ .../org/onosproject/mfwd/impl/McastForwarding.java | 237 ++++++ .../onosproject/mfwd/impl/McastIntentManager.java | 133 +++ .../java/org/onosproject/mfwd/impl/McastRoute.java | 190 +++++ .../org/onosproject/mfwd/impl/McastRouteBase.java | 431 ++++++++++ .../org/onosproject/mfwd/impl/McastRouteGroup.java | 110 +++ .../onosproject/mfwd/impl/McastRouteSource.java | 48 ++ .../org/onosproject/mfwd/impl/McastRouteTable.java | 338 ++++++++ .../org/onosproject/mfwd/impl/package-info.java | 4 + .../org/onosproject/mfwd/rest/McastResource.java | 149 ++++ .../resources/OSGI-INF/blueprint/shell-config.xml | 30 + .../onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml | 44 + .../optical/OpticalPathProvisioner.java | 16 +- framework/src/onos/apps/pim/pom.xml | 122 +++ .../org/onosproject/pim/cli/PIMShowCommand.java | 47 ++ .../java/org/onosproject/pim/cli/package-info.java | 4 + .../org/onosproject/pim/impl/PIMComponent.java | 153 ++++ .../java/org/onosproject/pim/impl/PIMNeighbor.java | 340 ++++++++ .../org/onosproject/pim/impl/PIMNeighbors.java | 395 +++++++++ .../onosproject/pim/impl/PIMNeighborsCodec.java | 97 +++ .../java/org/onosproject/pim/impl/PIMTimer.java | 53 ++ .../org/onosproject/pim/impl/package-info.java | 20 + .../resources/OSGI-INF.blueprint/shell-config.xml | 24 + framework/src/onos/apps/pom.xml | 5 +- .../java/org/onosproject/proxyarp/ProxyArp.java | 26 +- .../onosproject/reactive/routing/LocationType.java | 35 + .../reactive/routing/ReactiveRoutingFib.java | 395 +++++++++ .../reactive/routing/SdnIpReactiveRouting.java | 200 ++++- .../onosproject/reactive/routing/TrafficType.java | 56 ++ .../onosproject/routing/IntentRequestListener.java | 10 + .../routing/IntentSynchronizationService.java | 51 ++ .../org/onosproject/routing/RoutingService.java | 97 --- .../java/org/onosproject/routing/SdnIpService.java | 39 + .../org/onosproject/routing/RouteEntryTest.java | 104 ++- .../routing/config/impl/Configuration.java | 4 +- .../config/impl/RoutingConfigurationImpl.java | 17 +- .../java/org/onosproject/routing/impl/Router.java | 166 +--- .../org/onosproject/routing/impl/StaticRouter.java | 24 - .../org/onosproject/routing/impl/RouterTest.java | 192 ++++- .../org/onosproject/sdnip/IntentSynchronizer.java | 901 +++------------------ .../java/org/onosproject/sdnip/IntentUtils.java | 81 ++ .../onosproject/sdnip/PeerConnectivityManager.java | 74 +- .../src/main/java/org/onosproject/sdnip/SdnIp.java | 18 +- .../main/java/org/onosproject/sdnip/SdnIpFib.java | 216 +++++ .../sdnip/cli/PrimaryChangeCommand.java | 2 +- .../java/org/onosproject/sdnip/IntentSyncTest.java | 460 +++-------- .../sdnip/PeerConnectivityManagerTest.java | 81 +- .../java/org/onosproject/sdnip/SdnIpFibTest.java | 417 ++++++++++ .../onosproject/sdnip/TestIntentServiceHelper.java | 7 +- .../segmentrouting/DeviceConfiguration.java | 105 +-- .../onosproject/segmentrouting/IcmpHandler.java | 2 +- .../segmentrouting/SegmentRoutingManager.java | 113 ++- .../config/SegmentRoutingConfig.java | 128 +++ .../segmentrouting/config/package-info.java | 2 +- .../grouphandler/DeviceProperties.java | 2 +- .../cli/CounterTestIncrementCommand.java | 3 +- .../intentperf/IntentPerfInstaller.java | 4 +- framework/src/onos/apps/vtn/app/app.xml | 25 + framework/src/onos/apps/vtn/app/features.xml | 27 + framework/src/onos/apps/vtn/app/pom.xml | 44 + framework/src/onos/apps/vtn/pom.xml | 46 +- framework/src/onos/apps/vtn/vtnmgr/pom.xml | 53 ++ .../main/java/org/onosproject/vtn/VTNService.java | 68 ++ .../java/org/onosproject/vtn/impl/VTNManager.java | 672 +++++++++++++++ .../org/onosproject/vtn/impl/package-info.java | 20 + .../java/org/onosproject/vtn/package-info.java | 20 + framework/src/onos/apps/vtn/vtnrsc/pom.xml | 56 ++ .../org/onosproject/vtnrsc/AllocationPool.java | 38 + .../org/onosproject/vtnrsc/AllowedAddressPair.java | 94 +++ .../java/org/onosproject/vtnrsc/BindingHostId.java | 72 ++ .../onosproject/vtnrsc/DefaultAllocationPool.java | 81 ++ .../org/onosproject/vtnrsc/DefaultHostRoute.java | 79 ++ .../java/org/onosproject/vtnrsc/DefaultSubnet.java | 183 +++++ .../onosproject/vtnrsc/DefaultTenantNetwork.java | 160 ++++ .../org/onosproject/vtnrsc/DefaultVirtualPort.java | 229 ++++++ .../main/java/org/onosproject/vtnrsc/FixedIp.java | 93 +++ .../java/org/onosproject/vtnrsc/HostRoute.java | 39 + .../org/onosproject/vtnrsc/PhysicalNetwork.java | 78 ++ .../java/org/onosproject/vtnrsc/SecurityGroup.java | 77 ++ .../org/onosproject/vtnrsc/SegmentationId.java | 77 ++ .../main/java/org/onosproject/vtnrsc/Subnet.java | 129 +++ .../main/java/org/onosproject/vtnrsc/SubnetId.java | 76 ++ .../main/java/org/onosproject/vtnrsc/TenantId.java | 77 ++ .../java/org/onosproject/vtnrsc/TenantNetwork.java | 130 +++ .../org/onosproject/vtnrsc/TenantNetworkId.java | 76 ++ .../java/org/onosproject/vtnrsc/VirtualPort.java | 156 ++++ .../java/org/onosproject/vtnrsc/VirtualPortId.java | 70 ++ .../cli/network/TenantNetworkCreateCommand.java | 97 +++ .../cli/network/TenantNetworkQueryCommand.java | 60 ++ .../cli/network/TenantNetworkRemoveCommand.java | 45 + .../cli/network/TenantNetworkUpdateCommand.java | 99 +++ .../vtnrsc/cli/network/package-info.java | 20 + .../vtnrsc/cli/subnet/SubnetCreateCommand.java | 118 +++ .../vtnrsc/cli/subnet/SubnetQueryCommand.java | 61 ++ .../vtnrsc/cli/subnet/SubnetRemoveCommand.java | 46 ++ .../vtnrsc/cli/subnet/SubnetUpdateCommand.java | 118 +++ .../vtnrsc/cli/subnet/package-info.java | 20 + .../cli/virtualport/VirtualPortCreateCommand.java | 134 +++ .../cli/virtualport/VirtualPortQueryCommand.java | 94 +++ .../cli/virtualport/VirtualPortRemoveCommand.java | 45 + .../cli/virtualport/VirtualPortUpdateCommand.java | 135 +++ .../vtnrsc/cli/virtualport/package-info.java | 20 + .../java/org/onosproject/vtnrsc/package-info.java | 20 + .../onosproject/vtnrsc/subnet/SubnetService.java | 72 ++ .../vtnrsc/subnet/impl/SubnetManager.java | 183 +++++ .../vtnrsc/subnet/impl/package-info.java | 20 + .../onosproject/vtnrsc/subnet/package-info.java | 20 + .../vtnrsc/tenantnetwork/TenantNetworkService.java | 80 ++ .../tenantnetwork/impl/TenantNetworkManager.java | 167 ++++ .../vtnrsc/tenantnetwork/impl/package-info.java | 20 + .../vtnrsc/tenantnetwork/package-info.java | 20 + .../vtnrsc/tunnel/TunnelConfigService.java | 72 ++ .../onosproject/vtnrsc/tunnel/package-info.java | 20 + .../vtnrsc/virtualport/VirtualPortService.java | 100 +++ .../virtualport/impl/VirtualPortManager.java | 208 +++++ .../vtnrsc/virtualport/impl/package-info.java | 20 + .../vtnrsc/virtualport/package-info.java | 20 + .../vtnrsc/web/AllocationPoolsCodec.java | 40 + .../vtnrsc/web/AllowedAddressPairCodec.java | 40 + .../org/onosproject/vtnrsc/web/FixedIpCodec.java | 40 + .../onosproject/vtnrsc/web/HostRoutesCodec.java | 40 + .../onosproject/vtnrsc/web/SecurityGroupCodec.java | 39 + .../org/onosproject/vtnrsc/web/SubnetCodec.java | 53 ++ .../onosproject/vtnrsc/web/TenantNetworkCodec.java | 47 ++ .../onosproject/vtnrsc/web/VirtualPortCodec.java | 57 ++ .../org/onosproject/vtnrsc/web/package-info.java | 20 + .../resources/OSGI-INF/blueprint/shell-config.xml | 56 ++ framework/src/onos/apps/vtn/vtnweb/pom.xml | 87 ++ .../vtnweb/resources/SubnetWebResource.java | 379 +++++++++ .../vtnweb/resources/TenantNetworkWebResource.java | 373 +++++++++ .../vtnweb/resources/VirtualPortWebResource.java | 412 ++++++++++ .../onosproject/vtnweb/resources/package-info.java | 20 + .../vtn/vtnweb/src/main/webapp/WEB-INF/web.xml | 45 + 186 files changed, 17358 insertions(+), 2549 deletions(-) create mode 100644 framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java create mode 100644 framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java create mode 100644 framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java create mode 100644 framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java create mode 100644 framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java create mode 100644 framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java create mode 100644 framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml create mode 100644 framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java create mode 100644 framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java create mode 100644 framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java create mode 100644 framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java create mode 100644 framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java create mode 100644 framework/src/onos/apps/igmp/pom.xml create mode 100644 framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java create mode 100644 framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java create mode 100644 framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java create mode 100644 framework/src/onos/apps/mfwd/pom.xml create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java create mode 100644 framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java create mode 100644 framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml create mode 100644 framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml create mode 100644 framework/src/onos/apps/pim/pom.xml create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java create mode 100644 framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java create mode 100644 framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml create mode 100644 framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java create mode 100644 framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java create mode 100644 framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java create mode 100644 framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java create mode 100644 framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java create mode 100644 framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java create mode 100644 framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java create mode 100644 framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java create mode 100644 framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java create mode 100644 framework/src/onos/apps/vtn/app/app.xml create mode 100644 framework/src/onos/apps/vtn/app/features.xml create mode 100644 framework/src/onos/apps/vtn/app/pom.xml create mode 100644 framework/src/onos/apps/vtn/vtnmgr/pom.xml create mode 100644 framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java create mode 100644 framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java create mode 100644 framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/pom.xml create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml create mode 100644 framework/src/onos/apps/vtn/vtnweb/pom.xml create mode 100644 framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java create mode 100644 framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java create mode 100644 framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java create mode 100644 framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java create mode 100644 framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml (limited to 'framework/src/onos/apps') diff --git a/framework/src/onos/apps/aaa/pom.xml b/framework/src/onos/apps/aaa/pom.xml index 78fd4a62..b03930a9 100644 --- a/framework/src/onos/apps/aaa/pom.xml +++ b/framework/src/onos/apps/aaa/pom.xml @@ -44,36 +44,41 @@ org.onosproject - onlab-junit - test + onos-api + ${project.version} org.onosproject - onos-api + onos-app-xos-integration ${project.version} - + org.onosproject - onlab-osgi - ${project.version} + onlab-junit + test - org.apache.felix - org.apache.felix.scr.annotations + org.onosproject + onlab-osgi + ${project.version} + tests + test org.onosproject - onos-app-xos-integration + onos-api ${project.version} + tests + test - + org.apache.felix diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java index 7e3de885..479ec7ed 100644 --- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java @@ -15,13 +15,14 @@ */ 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(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 hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress)); Optional 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 index 00000000..c18d2bf2 --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java @@ -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 { + + 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); + } + +} diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java index 60959ada..84f69241 100644 --- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java @@ -18,13 +18,16 @@ 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 sessionIdMap; + private static Map 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 index 00000000..d4a4da77 --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java @@ -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 index 00000000..9f41a34f --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java @@ -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); + } +} diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java index 1b581ab1..75a60336 100644 --- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java @@ -15,29 +15,494 @@ */ 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 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 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 getHostsByIp(IpAddress ip) { + return ImmutableSet.of(new MockHost()); + } + } + + /** + * Mocks the network config registry. + */ + @SuppressWarnings("unchecked") + private static final class TestNetworkConfigRegistry + extends NetworkConfigRegistryAdapter { + @Override + public > C getConfig(S subject, Class 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)); + } } diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java index 2fe44ab9..04837e85 100644 --- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java @@ -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())); } } diff --git a/framework/src/onos/apps/acl/pom.xml b/framework/src/onos/apps/acl/pom.xml index 54dee432..454ac7e6 100644 --- a/framework/src/onos/apps/acl/pom.xml +++ b/framework/src/onos/apps/acl/pom.xml @@ -18,7 +18,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 4.0.0 @@ -36,10 +38,10 @@ http://onosproject.org - 1.4.0-SNAPSHOT org.onosproject.acl DLUT - /onos/acl + + /onos/v1/acl 1.0.0 ONOS ACL Application REST API @@ -64,19 +66,34 @@ org.onosproject onlab-junit - ${onos.version} org.onosproject onos-rest - ${onos.version} + ${project.version} + + + + org.onosproject + onlab-osgi + ${project.version} + tests + test + + + + org.onosproject + onos-rest + ${project.version} + tests + test org.onosproject onos-core-serializers - ${onos.version} + ${project.version} @@ -100,7 +117,6 @@ org.onosproject onlab-misc - ${onos.version} 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 index 00000000..8c91da4c --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java @@ -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. + *

+ * 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 index 00000000..487a6761 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java @@ -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 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 index 00000000..ff9e25f6 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java @@ -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 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 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 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 index 00000000..e792efba --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java @@ -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 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 index 00000000..468dab5c --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java @@ -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. + *

This class is immutable.

+ */ +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 index 00000000..f5c0c204 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java @@ -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 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 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 getDeviceIdSet(Ip4Prefix cidrAddr) { + Set deviceIdSet = new HashSet<>(); + final Iterable 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 dpidSet; + if (rule.srcIp() != null) { + dpidSet = getDeviceIdSet(rule.srcIp()); + } else { + dpidSet = getDeviceIdSet(rule.dstIp()); + } + + for (DeviceId deviceId : dpidSet) { + List 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 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 index 00000000..a5fcfcc7 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java @@ -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 ruleSet; + private ConsistentMap deviceToPriority; + private ConsistentMap> ruleToDevice; + private ConsistentMap> ruleToFlow; + private ConsistentMap> 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.consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("acl-rule-set") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + deviceToPriority = storageService.consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("device-to-priority") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + ruleToFlow = storageService.>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("rule-to-flow") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + denyRuleToAllowRule = storageService.>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("deny-to-allow") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + ruleToDevice = storageService.>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 getAclRules() { + List 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 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 getFlowByRule(RuleId ruleId) { + Versioned> 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 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 getAllowingRuleByDenyingRule(RuleId denyingRuleId) { + Versioned> 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 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 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 index 00000000..9da9b3b7 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java @@ -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 index 00000000..67f755c6 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java @@ -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; diff --git a/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml index 2c2d5cf3..fc188b7f 100644 --- a/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml +++ b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml @@ -33,7 +33,7 @@ com.sun.jersey.config.property.classnames - org.onos.acl.AclWebResource + org.onosproject.acl.AclWebResource 10 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 index 00000000..c554db6e --- /dev/null +++ b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java @@ -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 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.")); + } +} diff --git a/framework/src/onos/apps/bgprouter/pom.xml b/framework/src/onos/apps/bgprouter/pom.xml index decdf5c2..6503ee79 100644 --- a/framework/src/onos/apps/bgprouter/pom.xml +++ b/framework/src/onos/apps/bgprouter/pom.xml @@ -24,7 +24,6 @@ ../pom.xml
4.0.0 - onos-app-bgprouter bundle diff --git a/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java b/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java index 88265350..6130a2e2 100644 --- a/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java +++ b/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java @@ -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); diff --git a/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java b/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java index 1523e9c2..fa916865 100644 --- a/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java +++ b/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java @@ -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"); diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java index 072254de..cb8acab2 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java @@ -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 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.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 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 { + private class OvsdbHandler implements ConnectionHandler { @Override public void connected(Device device) { @@ -296,7 +236,7 @@ public class CordVtn implements CordVtnService { } } - private class VirtualMachineHandler implements ConnectionHandler { + private class VmHandler implements ConnectionHandler { @Override public void connected(Host host) { diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java index c2c37aba..fdaf752a 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java @@ -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 { 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 { 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 { } /** - * 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 index 00000000..043b3760 --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java @@ -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 + } + } +} diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java index d26a10aa..1f75dceb 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java @@ -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. * diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java index b8cdbe94..ce8b0f1d 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java @@ -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(); } } diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java index 0b7029ef..ebba4cd5 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java @@ -15,12 +15,19 @@ */ 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 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 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 } } diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java index bb2a0b7d..296bd439 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java @@ -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(); } diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java index c9fade9e..5615af1a 100644 --- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java @@ -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. diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java index 345d5ad0..96d94a2b 100644 --- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java @@ -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); diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java index dbdadb34..63f69d40 100644 --- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java @@ -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 diff --git a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java index 3ea3b1b8..fd4701c6 100644 --- a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java +++ b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java @@ -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 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) { + + } + } /** diff --git a/framework/src/onos/apps/flowanalyzer/pom.xml b/framework/src/onos/apps/flowanalyzer/pom.xml index f5dfcf2b..b0920412 100644 --- a/framework/src/onos/apps/flowanalyzer/pom.xml +++ b/framework/src/onos/apps/flowanalyzer/pom.xml @@ -40,6 +40,38 @@ org.osgi org.osgi.compendium
+ + org.onosproject + onlab-junit + test + + + + org.onosproject + onos-api + tests + test + + + org.apache.felix + org.apache.felix.scr.annotations + + + + org.onosproject + onos-cli + ${project.version} + + + org.osgi + org.osgi.core + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + + 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 index 00000000..2c61949b --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java @@ -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()); + } +} diff --git a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java index 5d99d746..6aaaaee8 100644 --- a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java +++ b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java @@ -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 label = new HashMap<>(); + Set 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 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 critpts = new HashSet<>(); + for (FlowEntry flow: label.keySet()) { + if ("Cycle".equals(label.get(flow))) { + Map labelSaved = label; + label = new HashMap(); + ignoredFlows.add(flow); + for (TopologyVertex v: graph.getVertexes()) { + DeviceId srcDevice = v.deviceId(); + Iterable 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 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 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 dstFlowTable = flowRuleService.getFlowEntries(egress); + + Set flowCriteria = flow.selector().criteria(); + + //filter the criteria in order to remove port dependency + Set 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 index 00000000..93cb27ee --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,23 @@ + + + + + + + + + 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 index 00000000..4ea3aa48 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java @@ -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 + 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 vertexes, Set 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 index 00000000..faa2f5f9 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java @@ -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 index 00000000..40bb0043 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java @@ -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 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 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 getFlowRulesById(ApplicationId id) { + return flows.stream() + .filter(flow -> flow.appId() == id.id()) + .collect(Collectors.toList()); + } + + @Override + public Iterable 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 index 00000000..2171c6f8 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java @@ -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 links = new ArrayList<>(); + + @Override + public int getLinkCount() { + return links.size(); + } + + @Override + public Iterable getLinks() { + return links; + } + + @Override + public Set getDeviceLinks(DeviceId deviceId) { + Set egress = getDeviceEgressLinks(deviceId); + egress.addAll(getDeviceIngressLinks(deviceId)); + return egress; + } + + @Override + public Set getDeviceEgressLinks(DeviceId deviceId) { + Set 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 getDeviceIngressLinks(DeviceId deviceId) { + Set 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 getEgressLinks(ConnectPoint pt) { + Set setL = new HashSet<>(); + for (Link l: links) { + if (l.src().equals(pt)) { + setL.add(l); + } + } + return setL; + } + + @Override + public Set getIngressLinks(ConnectPoint pt) { + Set setL = new HashSet<>(); + for (Link l: links) { + if (l.dst().equals(pt)) { + setL.add(l); + } + } + return setL; + } + + @Override + public Set getLinks(ConnectPoint pt) { + Set 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 index 00000000..0d25c977 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java @@ -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 index 00000000..7980d2c0 --- /dev/null +++ b/framework/src/onos/apps/igmp/pom.xml @@ -0,0 +1,117 @@ + + + + 4.0.0 + + + org.onosproject + onos-apps + 1.4.0-SNAPSHOT + ../pom.xml + + + onos-app-igmp + bundle + + Internet Group Message Protocol + + + org.onosproject.igmp + + + + + org.onosproject + onos-api + ${project.version} + + + + org.onosproject + onos-cli + ${project.version} + + + + + org.onosproject + onlab-osgi + ${project.version} + + + + org.onosproject + onlab-junit + test + + + + + org.osgi + org.osgi.compendium + + + + org.apache.felix + org.apache.felix.scr.annotations + 1.9.8 + provided + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + ${project.groupId}.${project.artifactId} + + + 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.* + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.8 + 1.8 + + + + + + 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 index 00000000..ae539c62 --- /dev/null +++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java @@ -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 index 00000000..3d7d6033 --- /dev/null +++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java @@ -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 index 00000000..eb256796 --- /dev/null +++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java @@ -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 index 00000000..835de836 --- /dev/null +++ b/framework/src/onos/apps/mfwd/pom.xml @@ -0,0 +1,142 @@ + + + + 4.0.0 + + + org.onosproject + onos-apps + 1.4.0-SNAPSHOT + ../pom.xml + + + onos-app-mfwd + bundle + + Multicast forwarding application + + + org.onosproject.mfwd + + + + + org.onosproject + onos-cli + ${project.version} + + + io.netty + netty + 3.9.0.Final + + + + org.osgi + org.osgi.compendium + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + + + + org.osgi + org.osgi.core + + + + org.onosproject + onlab-rest + ${project.version} + + + org.onosproject + onos-rest + ${project.version} + + + com.sun.jersey + jersey-servlet + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + + org.onosproject + onlab-junit + test + + + + org.easymock + easymock + test + + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + <_wab>src/main/webapp/ + + ${project.groupId}.${project.artifactId} + + + 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.* + + ${web.context} + + + + + + + + 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 index 00000000..ae5d9e93 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java @@ -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 index 00000000..7260fde5 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java @@ -0,0 +1,72 @@ +/* + * 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.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; + +import org.onosproject.mfwd.impl.McastConnectPoint; +import org.onosproject.mfwd.impl.McastRouteBase; +import org.onosproject.mfwd.impl.McastRouteTable; + +/** + * Installs a source, multicast group flow. + */ +@Command(scope = "onos", name = "mcast-join", + description = "Installs a source, multicast group flow") +public class McastJoinCommand 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; + + @Argument(index = 2, name = "ingressPort", + description = "Ingress port and Egress ports", + required = false, multiValued = false) + String ingressPort = null; + + @Argument(index = 3, name = "ports", + description = "Ingress port and Egress ports", + required = false, multiValued = true) + String[] ports = null; + + @Override + protected void execute() { + McastRouteTable mrib = McastRouteTable.getInstance(); + McastRouteBase mr = mrib.addRoute(sAddr, gAddr); + + // Port format "of:0000000000000023/4" + if (ingressPort != null) { + String inCP = ingressPort; + log.debug("Ingress port provided: " + inCP); + mr.addIngressPoint(inCP); + } + + for (int i = 0; i < ports.length; i++) { + String egCP = ports[i]; + log.debug("Egress port provided: " + egCP); + mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC); + } + print("Added the mcast route"); + } +} 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 index 00000000..7fa3a13a --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java @@ -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 index 00000000..7b5ed39a --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java @@ -0,0 +1,5 @@ +/** + * Sample Multicast forwarding framework using intents. + */ +package org.onosproject.mfwd.cli; + 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 index 00000000..c4f18527 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java @@ -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 { + + 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 mcastRouteTabOpt = Optional.ofNullable(mcastRouteTable); + + //checking whether the McastRouteTable is present. + if (mcastRouteTabOpt.isPresent()) { + Map mrib4 = mcastRouteTabOpt.get().getMrib4(); + Optional> mrib4Opt = Optional.ofNullable(mrib4); + + //checking whether the mrib4 is present. + if (mrib4Opt.isPresent()) { + + for (McastRouteGroup mg : mrib4Opt.get().values()) { + Collection mcastRoute = mg.getSources().values(); + Optional> 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 saddrOpt = Optional.ofNullable(sAddr); + Optional 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 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 mcastEgCPSet = mcastRouteSource.getEgressPoints(); + Optional> 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 inPntOpt = Optional.ofNullable(inPntObjNode); + + if (inPntOpt.isPresent()) { + + JsonNode inMcastCP = inPntOpt.get().path(MCASTCONNECTPOINT); + Optional 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 egPntOpt = Optional.ofNullable(egPntArrNode); + + if (egPntOpt.isPresent()) { + JsonNode egMcastCP = egPntOpt.get().path(MCASTCONNECTPOINT); + Optional egMcCpOpt = Optional.ofNullable(egMcastCP); + + if (egMcCpOpt.isPresent()) { + Iterator egCpIt = egMcCpOpt.get().elements(); + + while (egCpIt.hasNext()) { + + JsonNode egMcastCPObj = egCpIt.next(); + Optional 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 index 00000000..e2a6ff0d --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java @@ -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 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 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 index 00000000..f5bd1e01 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java @@ -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 index 00000000..90f65c94 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java @@ -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 index 00000000..12b7e6d4 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java @@ -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 getEgressPoints(); + + /** + * Get the egress connect points. + * + * @return a set of egress connect points + */ + public Set 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 index 00000000..730acfa7 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java @@ -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 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 getEgressPoints() { + return egressPoints; + } + + /** + * Get egress McastConnectPoints points as ConnectPoints for intent system. + * + * @return Set of egress ConnectPoints + */ + public Set getEgressConnectPoints() { + Set 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 index 00000000..4a58e1b1 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java @@ -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 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 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 index 00000000..68edc2e0 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java @@ -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 index 00000000..5a07bec7 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java @@ -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 mrib4; + private final Map mrib6; + private static McastRouteTable instance = null; + + private Boolean ipv6Enabled = false; + + /** + * Create the two v4 & v6 tables. + */ + private McastRouteTable() { + mrib4 = new ConcurrentHashMap(); + if (ipv6Enabled) { + mrib6 = new ConcurrentHashMap(); + } 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 getMrib4() { + return mrib4; + } + + /** + * Get the IPv6 MRIB. + * + * @return Return the set of prefix keyed McastGroups + */ + public Map 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 index 00000000..eaef5fcf --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java @@ -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 index 00000000..608e0447 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java @@ -0,0 +1,149 @@ +/* + * 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.rest; + +import java.io.IOException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.QueryParam; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.DELETE; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onosproject.mfwd.impl.McastConnectPoint; +import org.onosproject.mfwd.impl.McastRouteTable; +import org.onosproject.mfwd.impl.McastRouteBase; +import org.onosproject.mfwd.impl.MRibCodec; +import org.onosproject.rest.AbstractWebResource; + +import org.slf4j.Logger; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Rest API for Multicast Forwarding. + */ +@Path("mcast") +public class McastResource extends AbstractWebResource { + + 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 MCAST_GROUP = "mcastGroup"; + + /** + * Retrieve the multicast route table. + * + * @return the multicast route table. + * @throws IOException if an error occurs + */ + @Path("show") + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response showAll() throws IOException { + McastRouteTable mrt = McastRouteTable.getInstance(); + ObjectNode pushContent = new MRibCodec().encode(mrt , this); + return ok(pushContent.toString()).build(); + } + + /** + * Static join a multicast flow. + * + * @param sAddr source address to join + * @param gAddr group address to join + * @param ports ingress and egress ConnectPoints to join + * @return the Result of the join + * @throws IOException if something failed with the join command + */ + @Path("/join") + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.TEXT_PLAIN) + public Response join(@QueryParam("src") String sAddr, + @QueryParam("grp") String gAddr, + @DefaultValue("") @QueryParam("ports") String ports) + throws IOException { + + ObjectMapper mapper = new ObjectMapper(); + log.debug("Source IP Address: " + sAddr); + log.debug("Destination IP Address: " + gAddr); + log.debug("Ingress and Egress ports: " + ports); + + String output = "Insertion Faild"; + if (sAddr != null && gAddr != null && ports != null) { + + String[] portArr = ports.split(","); + log.debug("Port Array Length: " + portArr.length); + McastRouteTable mrt = McastRouteTable.getInstance(); + McastRouteBase mr = mrt.addRoute(sAddr, gAddr); + + // Port format "of:0000000000000023/4" + log.debug("checking inside outer if: " + portArr.length); + + if (mr != null && portArr != null && portArr.length > 0) { + + String inCP = portArr[0]; + log.debug("Ingress port provided: " + inCP); + mr.addIngressPoint(inCP); + + for (int i = 1; i < portArr.length; i++) { + String egCP = portArr[i]; + log.debug("Egress port provided: " + egCP); + mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC); + } + mrt.printMcastRouteTable(); + output = "Successfully Inserted"; + } + } else { + output = "Please Insert the rest uri correctly"; + } + return Response.ok(output).build(); + } + + /** + * Delete multicast state. + * + * @param src address to be deleted + * @param grp address to be deleted + * @return status of delete if successful + */ + @Path("/delete") + @DELETE + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + public Response removeMcastFlow(@QueryParam("src") String src, + @QueryParam("grp") String grp) { + + String resp = "Failed to delete"; + log.info("Source IP Address to delete: " + src); + log.info("Destination IP Address to delete: " + grp); + McastRouteTable mrt = McastRouteTable.getInstance(); + if (src != null && grp != null) { + mrt.removeRoute(src, grp); + resp = "Deleted flow for src " + src + " and grp " + grp; + } + + return Response.ok(resp).build(); + } +} 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 index 00000000..966cb4f2 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + 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 index 00000000..c4c4f459 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,44 @@ + + + + ONOS APP MFWD + + + JAX-RS Service + com.sun.jersey.spi.container.servlet.ServletContainer + + com.sun.jersey.config.property.resourceConfigClass + com.sun.jersey.api.core.ClassNamesResourceConfig + + + com.sun.jersey.config.property.classnames + + org.onosproject.mfwd.rest.McastResource + + + 1 + + + + JAX-RS Service + /* + + + diff --git a/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java b/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java index 85b5de27..8466b95e 100644 --- a/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java +++ b/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java @@ -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 index 00000000..83a366b6 --- /dev/null +++ b/framework/src/onos/apps/pim/pom.xml @@ -0,0 +1,122 @@ + + + + 4.0.0 + + + org.onosproject + onos-apps + 1.4.0-SNAPSHOT + ../pom.xml + + + onos-app-pim + bundle + + Protocol Independent Multicast Emulation + + + org.onosproject.pim + + + + + org.onosproject + onos-api + ${project.version} + + + + org.onosproject + onos-cli + ${project.version} + + + + + org.onosproject + onlab-osgi + ${project.version} + + + + org.onosproject + onlab-junit + test + + + + + org.osgi + org.osgi.compendium + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + + + + org.apache.felix + org.apache.felix.scr.annotations + 1.9.8 + provided + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + ${project.groupId}.${project.artifactId} + + + 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.* + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.8 + 1.8 + + + + + 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 index 00000000..0ef7e389 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java @@ -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 pimNbrs = PIMNeighbors.getConnectPointNeighbors(); + if (outputJson()) { + print("%s", json(pimNbrs)); + } else { + print(PIMNeighbors.printPimNeighbors()); + } + } + + private JsonNode json(HashMap 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 index 00000000..954dacbb --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java @@ -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 index 00000000..bd5e1486 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java @@ -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 index 00000000..1a96138f --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java @@ -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 index 00000000..cad90768 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java @@ -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 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 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 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 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 index 00000000..ee62eb79 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java @@ -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> { + // 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 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 index 00000000..c131a53b --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java @@ -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 index 00000000..29d1ce4e --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java @@ -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 index 00000000..c30e3792 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/framework/src/onos/apps/pom.xml b/framework/src/onos/apps/pom.xml index 334c690b..b955130a 100644 --- a/framework/src/onos/apps/pom.xml +++ b/framework/src/onos/apps/pom.xml @@ -53,11 +53,12 @@ olt cip flowanalyzer - vtnrsc vtn - vtnweb dhcp cordvtn + mfwd + igmp + pim diff --git a/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java b/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java index 2eb96df2..2eb1d5ec 100644 --- a/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java +++ b/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java @@ -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 index 00000000..01f4f700 --- /dev/null +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java @@ -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 index 00000000..8e86056e --- /dev/null +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java @@ -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 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 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 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 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 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 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; + } +} diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java index ad78a1ce..96aa06ee 100644 --- a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java @@ -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 srcInterface = + interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst(); + Set 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 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 index 00000000..134126b3 --- /dev/null +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java @@ -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. + *

+ * We classify traffic by the first packet of each traffic. + *

+ */ +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 +} diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java index 2d1bb311..1069ec5a 100644 --- a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java @@ -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 index 00000000..dc6a838d --- /dev/null +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java @@ -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. + *

+ * 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. + *

+ * + * @param intent intent to submit + */ + void submit(Intent intent); + + /** + * Withdraws an intent from the synchronizer. + *

+ * 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. + *

+ * + * @param intent intent to withdraw + */ + void withdraw(Intent intent); +} diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java index 8b7040e2..7399ed75 100644 --- a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java @@ -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 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. - *

- * We classify traffic by the first packet of each traffic. - *

- */ - 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 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 index 00000000..0945336c --- /dev/null +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java @@ -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(); + +} diff --git a/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java index c47d2768..b89eb2d1 100644 --- a/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java +++ b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java @@ -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}")); } } diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java index 45206903..c58bc1b9 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java @@ -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 bgpSpeakers; - private List peers; + private List bgpSpeakers = Collections.emptyList(); + private List peers = Collections.emptyList(); private MacAddress virtualGatewayMacAddress; // All IP prefixes from the configuration are local diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java index 0a6f9d4c..19c3f70b 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java @@ -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 diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java index 6700d530..75d789ab 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java @@ -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 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 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 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; - } - } } diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java index 29526fff..3c868202 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java @@ -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; diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java index 45bc309f..c73e18cb 100644 --- a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java @@ -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); + } } diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java index d8d8f45d..eaabed33 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java @@ -15,118 +15,79 @@ */ 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 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 peerIntents; - private final Map routeIntents; + + private final Map 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 getRouteIntents() { - List result = new LinkedList<>(); - - for (Map.Entry 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 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. - *

- * 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 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 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 updates, Collection 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 localIntents = new HashMap<>(); - Map fetchedIntents = new HashMap<>(); - Collection storeInMemoryIntents = new LinkedList<>(); - Collection addIntents = new LinkedList<>(); - Collection 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 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 intentsToAdd = new LinkedList<>(); + List 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 localIntents, - final Map fetchedIntents, - Collection storeInMemoryIntents, - Collection addIntents, - Collection 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 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 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 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 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 index 00000000..8e2a3df3 --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java @@ -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. + * + *

({@link Intent#equals(Object)} only checks ID equality)

+ * + *

Both intents must be of the same type.

+ * + * @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; + } + } +} diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java index 459db2b7..b2ce0f8a 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java @@ -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 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 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 buildSpeakerIntents(BgpConfig.BgpSpeakerConfig speaker) { @@ -167,8 +180,8 @@ public class PeerConnectivityManager { List 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); + } + } diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java index 3d1fe65c..1b3eda9d 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java @@ -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 index 00000000..c0001bdc --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java @@ -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 CONSTRAINTS + = ImmutableList.of(new PartialFailureConstraint()); + + private final Map 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 updates, Collection 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. + *

+ * 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 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(); + } + +} diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java index 72cc112e..7a17cfe0 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java @@ -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. diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java index fc5782e4..6dc3ce10 100644 --- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java @@ -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 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 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 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 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 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 - 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 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 { diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java index d89c3c2b..c4b2daad 100644 --- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java @@ -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 bgpSpeakers; private Map interfaces; @@ -98,8 +88,6 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { private BgpConfig bgpConfig; - private Map configuredInterfaces; - private Map configuredPeers; private List 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 setUpInterfaces() { - configuredInterfaces = new HashMap<>(); + Map configuredInterfaces = new HashMap<>(); String interfaceSw1Eth1 = "s1-eth1"; InterfaceIpAddress ia1 = @@ -242,7 +230,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { */ private Map setUpPeers() { - configuredPeers = new HashMap<>(); + Map 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 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.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 index 00000000..5466d520 --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java @@ -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 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 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 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 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 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 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 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 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 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); + } +} diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java index 69b18aa9..7f825e81 100644 --- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java @@ -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); } } diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java index ef9d444a..8fdf81a2 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java @@ -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 allSegmentIds = new ArrayList<>(); private final HashMap deviceConfigMap = new HashMap<>(); - private final NetworkConfigManager configService; private class SegmentRouterInfo { int nodeSid; @@ -60,48 +61,60 @@ public class DeviceConfiguration implements DeviceProperties { boolean isEdge; HashMap gatewayIps; HashMap subnets; - List adjacencySids; + List 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 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 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 portSubjects = + cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class); + portSubjects.forEach(subject -> { + InterfaceConfig config = + cfgService.getConfig(subject, InterfaceConfig.class); + Set 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 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 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 diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java index 0f8fa59d..f65f03e0 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java @@ -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); diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java index 874faabf..05663129 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java @@ -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 index 00000000..6dc3f0db --- /dev/null +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java @@ -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 { + 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 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 getAdjacencySids() { + ArrayList 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 ports = new ArrayList(); + 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 ports; + + public AdjacencySid(int asid, List ports) { + this.asid = asid; + this.ports = ports; + } + + public int getAsid() { + return asid; + } + + public List getPorts() { + return ports; + } + } +} \ No newline at end of file diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java index fdae9c9e..95f7e244 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java @@ -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. diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java index 816ca7bc..497f5256 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java @@ -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. diff --git a/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java b/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java index 12c8140a..d93ad78f 100644 --- a/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java +++ b/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java @@ -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); } } } diff --git a/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java b/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java index de9e9f21..ad3236e5 100644 --- a/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java +++ b/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java @@ -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 index 00000000..a0efd7f4 --- /dev/null +++ b/framework/src/onos/apps/vtn/app/app.xml @@ -0,0 +1,25 @@ + + + + ${project.description} + + mvn:${project.groupId}/onos-app-vtn-mgr/${project.version} + mvn:${project.groupId}/onos-app-vtn-web/${project.version} + mvn:${project.groupId}/onos-app-vtn-rsc/${project.version} + diff --git a/framework/src/onos/apps/vtn/app/features.xml b/framework/src/onos/apps/vtn/app/features.xml new file mode 100644 index 00000000..c82b41d5 --- /dev/null +++ b/framework/src/onos/apps/vtn/app/features.xml @@ -0,0 +1,27 @@ + + + + mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features + + onos-api + onos-drivers + mvn:${project.groupId}/onos-app-vtn-mgr/${project.version} + mvn:${project.groupId}/onos-app-vtn-web/${project.version} + mvn:${project.groupId}/onos-app-vtn-rsc/${project.version} + + diff --git a/framework/src/onos/apps/vtn/app/pom.xml b/framework/src/onos/apps/vtn/app/pom.xml new file mode 100644 index 00000000..4ed66172 --- /dev/null +++ b/framework/src/onos/apps/vtn/app/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + + org.onosproject + onos-app-vtn + 1.4.0-SNAPSHOT + ../pom.xml + + + onos-app-vtn-onosfw + pom + + ONOS framework applications + + + + org.onosproject + onos-app-vtn-rsc + ${project.version} + + + org.onosproject + onos-app-vtn-web + ${project.version} + + + org.onosproject + onos-app-vtn-mgr + ${project.version} + + + + diff --git a/framework/src/onos/apps/vtn/pom.xml b/framework/src/onos/apps/vtn/pom.xml index fb8fcb13..c2cfe2be 100644 --- a/framework/src/onos/apps/vtn/pom.xml +++ b/framework/src/onos/apps/vtn/pom.xml @@ -1,6 +1,6 @@ - + - + 4.0.0 + org.onosproject onos-apps @@ -27,32 +27,14 @@ onos-app-vtn - bundle + pom + ONOS framework applications - - UTF-8 - org.onosproject.vtn - - - - javax.ws.rs - jsr311-api - 1.1.1 - - - org.onosproject - onos-incubator-api - - - org.onosproject - onos-core-serializers - ${project.version} - - - org.onosproject - onos-app-vtnrsc - ${project.version} - - + + vtnrsc + vtnmgr + vtnweb + app + diff --git a/framework/src/onos/apps/vtn/vtnmgr/pom.xml b/framework/src/onos/apps/vtn/vtnmgr/pom.xml new file mode 100644 index 00000000..03e66708 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + org.onosproject + onos-app-vtn + 1.4.0-SNAPSHOT + ../pom.xml + + + onos-app-vtn-mgr + bundle + + + + javax.ws.rs + jsr311-api + 1.1.1 + + + org.onosproject + onos-incubator-api + + + org.onosproject + onos-core-serializers + ${project.version} + + + org.onosproject + onos-app-vtn-rsc + ${project.version} + + + 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 index 00000000..a20f852b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java @@ -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 index 00000000..090ef0f1 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java @@ -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 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 + .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 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 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 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 allPorts = deviceService.getPorts(deviceId); + PortNumber inPort = host.location().port(); + List localVmPorts = getLocalPorts(deviceId, ifaceId); + List 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 bridgeDescriptions = bridgeConfig + .getBridges(); + + Iterator it = bridgeDescriptions + .iterator(); + if (it.hasNext()) { + BridgeDescription sw = it.next(); + Set 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 devices = deviceService.getAvailableDevices(); + + String tunnelName = "vxlan-" + currentControllerIp; + List allPorts = deviceService.getPorts(deviceId); + PortNumber inPort = host.location().port(); + + List 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 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 bridgeDescriptions = bridgeConfig + .getBridges(); + + Iterator it = bridgeDescriptions + .iterator(); + if (it.hasNext()) { + BridgeDescription sw = it.next(); + Set 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 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 localVmPorts, + List 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 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 ports = virtualPortService + .getPorts(network.id()); + return ports.stream().map(p -> p.portId().portId()) + .collect(Collectors.toSet()); + } + + private List getLocalPorts(DeviceId deviceId, String ifaceId) { + DriverHandler handler = driverService + .createHandler(getController(deviceId)); + BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class); + Iterable ifaceIds = getIfaceIds(ifaceId); + return bridgeConfig.getLocalPorts(ifaceIds); + } + + private DeviceId getController(DeviceId deviceId) { + Iterable 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 index 00000000..f18dbf8a --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java @@ -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 index 00000000..371466c3 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java @@ -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 index 00000000..8696295c --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/pom.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + + org.onosproject + onos-app-vtn + 1.4.0-SNAPSHOT + ../pom.xml + + + + onos-app-vtn-rsc + bundle + + + + org.onosproject + onos-api + + + org.onosproject + onos-cli + ${project.version} + + + org.apache.felix + org.apache.felix.scr.annotations + + + org.apache.karaf.shell + org.apache.karaf.shell.console + + + org.onosproject + onos-core-serializers + ${project.version} + + + 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 index 00000000..3d6ba8e8 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java @@ -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 index 00000000..4e1028d8 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java @@ -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 & + * 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 index 00000000..c715d08a --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java @@ -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 index 00000000..8a480194 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java @@ -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 index 00000000..8679100d --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java @@ -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 index 00000000..6049b558 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java @@ -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 hostRoutes; + private final Set 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 hostRoutes, Mode ipV6AddressMode, + Mode ipV6RaMode, + Set 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 hostRoutes() { + return hostRoutes; + } + + @Override + public Mode ipV6AddressMode() { + return ipV6AddressMode; + } + + @Override + public Mode ipV6RaMode() { + return ipV6RaMode; + } + + @Override + public Iterable 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 index 00000000..8c941ea2 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java @@ -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 index 00000000..9ee85da7 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java @@ -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 fixedIps; + private final BindingHostId bindingHostId; + private final String bindingVnicType; + private final String bindingVifType; + private final String bindingVifDetails; + private final Set allowedAddressPairs; + private final Set 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 strMap, + State state, + MacAddress macAddress, + TenantId tenantId, + DeviceId deviceId, + Set fixedIps, + BindingHostId bindingHostId, + Set allowedAddressPairs, + Set 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 allowedAddressPairs() { + return allowedAddressPairs; + } + + @Override + public Set 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 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 index 00000000..c6569a7b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java @@ -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 index 00000000..b18cb950 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java @@ -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 index 00000000..e96e666a --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java @@ -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 index 00000000..9ec1dc63 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java @@ -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 index 00000000..a076265f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java @@ -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 index 00000000..f563a78f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java @@ -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 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 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 index 00000000..4bcc3329 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java @@ -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 index 00000000..c4d99e49 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java @@ -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 index 00000000..256352f4 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java @@ -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 index 00000000..fbb9e480 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java @@ -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 index 00000000..d2d7c146 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java @@ -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 allowedAddressPairs(); + + /** + * Returns set of IP addresses for the port, include the IP addresses and subnet + * identity. + * + * @return FixedIps Set of fixedIp + */ + Set 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 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 index 00000000..3038bdff --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java @@ -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 index 00000000..bcfdacfa --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java @@ -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 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 index 00000000..47ea83c2 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java @@ -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 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 index 00000000..0ea22853 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java @@ -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 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 index 00000000..2a738f72 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java @@ -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 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 index 00000000..1622c800 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java @@ -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 index 00000000..56236408 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java @@ -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 hostRoutes = Sets.newHashSet(); + + @Option(name = "-a", aliases = "--allocationPools", + description = "Subnet jsonnode allocationPools", required = false, multiValued = false) + Set 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 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 index 00000000..f5a94f0f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java @@ -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 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 index 00000000..241af87e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java @@ -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 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 index 00000000..b0578a1e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java @@ -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 hostRoutes = Sets.newHashSet(); + + @Option(name = "-a", aliases = "--allocationPools", + description = "Subnet jsonnode allocationPools", required = false, multiValued = false) + Set 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 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 index 00000000..b3a2ff51 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java @@ -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 index 00000000..4c555e33 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java @@ -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 allowedAddressPairs = Sets.newHashSet(); + + @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.", + required = false, multiValued = false) + Set securityGroups = Sets.newHashSet(); + + @Override + protected void execute() { + Map 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 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 index 00000000..47126d1b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java @@ -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 ports = service.getPorts(TenantNetworkId.networkId(networkId)); + printPorts(ports); + } else if (vPortId == null && networkId == null && deviceId != null && tenantId == null) { + Collection ports = service.getPorts(DeviceId.deviceId(deviceId)); + printPorts(ports); + } else if (vPortId == null && networkId == null && deviceId == null && tenantId != null) { + Collection ports = service.getPorts(DeviceId.deviceId(tenantId)); + printPorts(ports); + } else if (vPortId == null && networkId == null && deviceId == null && tenantId == null) { + Collection ports = service.getPorts(); + printPorts(ports); + } else { + print("cannot input more than one parameter"); + } + + } + + private void printPorts(Collection 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 index 00000000..1a3cb4f0 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java @@ -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 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 index 00000000..6df4b23c --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java @@ -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 allowedAddressPairs = Sets.newHashSet(); + + @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.", + required = false, multiValued = false) + Set securityGroups = Sets.newHashSet(); + + @Override + protected void execute() { + VirtualPortService service = get(VirtualPortService.class); + Map 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 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 index 00000000..fac214a1 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java @@ -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 index 00000000..b245fb14 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java @@ -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 index 00000000..82eb9611 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java @@ -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 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 subnets); + + /** + * Updates existing subnets. + * + * @param subnets the iterable collection of subnets + * @return true if all subnets were updated successfully + */ + boolean updateSubnets(Iterable 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 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 index 00000000..890beb29 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java @@ -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 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.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 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 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 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 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 index 00000000..79040d8d --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java @@ -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 index 00000000..7b2bdb90 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java @@ -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 index 00000000..e246cc4e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java @@ -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 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 networks); + + /** + * Updates tenantNetworks by tenantNetworks. + * + * @param networks the collection of tenantNetworks + * @return true if all given identifiers updated successfully. + */ + boolean updateNetworks(Iterable networks); + + /** + * Deletes tenantNetwork by tenantNetworkIds. + * + * @param networksIds the collection of tenantNetworkIds + * @return true if the specified tenantNetworks deleted successfully. + */ + boolean removeNetworks(Iterable 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 index 00000000..0dfc99e2 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java @@ -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 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.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 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 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 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 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 index 00000000..f381fda6 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java @@ -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 index 00000000..1489c973 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java @@ -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 index 00000000..6f3cf653 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java @@ -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 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 subnets); + + /** + * Updates existing subnets. + * + * @param subnets the iterable collection of subnets + * @return true if all subnets were updated successfully + */ + boolean updateSubnets(Iterable 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 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 index 00000000..3a84e6e3 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java @@ -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 index 00000000..05ebccf9 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java @@ -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 getPorts(); + + /** + * Returns the collection of the virtualPorts associated with the networkId. + * + * @param networkId the network identifer + * @return collection of virtualPort. + */ + Collection getPorts(TenantNetworkId networkId); + + /** + * Returns the collection of the virtualPorts associated with the tenantId. + * + * @param tenantId the tenant identifier + * @return collection of virtualPorts. + */ + Collection getPorts(TenantId tenantId); + + /** + * Returns the collection of the virtualPorts associated with the deviceId. + * + * @param deviceId the device identifier + * @return collection of virtualPort. + */ + Collection 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 virtualPorts); + + /** + * Updates virtualPorts by virtualPorts. + * + * @param virtualPorts the iterable collection of virtualPorts + * @return true if all given identifiers updated successfully. + */ + boolean updatePorts(Iterable 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 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 index 00000000..926809c9 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java @@ -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 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.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 getPorts() { + return Collections.unmodifiableCollection(vPortStore.values()); + } + + @Override + public Collection getPorts(TenantNetworkId networkId) { + checkNotNull(networkId, NETWORKID_NOT_NULL); + return vPortStore.values().stream().filter(d -> d.networkId().equals(networkId)) + .collect(Collectors.toList()); + } + + @Override + public Collection getPorts(TenantId tenantId) { + checkNotNull(tenantId, TENANTID_NOT_NULL); + return vPortStore.values().stream().filter(d -> d.tenantId().equals(tenantId)) + .collect(Collectors.toList()); + } + + @Override + public Collection 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 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 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 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 index 00000000..24eb0d3f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java @@ -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 index 00000000..06a01a04 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java @@ -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 index 00000000..57c97c1c --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java @@ -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 { + + @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 index 00000000..7960808f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java @@ -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 { + + @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 index 00000000..96c9bb4e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java @@ -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 { + + @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 index 00000000..69ca6b3f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java @@ -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 { + + @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 index 00000000..c2ded196 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java @@ -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 { + + @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 index 00000000..122b75a9 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java @@ -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 { + @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 index 00000000..48ba3b97 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java @@ -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 { + + @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 index 00000000..e57d56bc --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java @@ -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 { + @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 index 00000000..34636a9f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java @@ -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 index 00000000..c6a9c81b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/src/onos/apps/vtn/vtnweb/pom.xml b/framework/src/onos/apps/vtn/vtnweb/pom.xml new file mode 100644 index 00000000..bcb71d9f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/pom.xml @@ -0,0 +1,87 @@ + + + + 4.0.0 + + org.onosproject + onos-app-vtn + 1.4.0-SNAPSHOT + ../pom.xml + + + + onos-app-vtn-web + bundle + + /onos/vtn + + + + + javax.ws.rs + jsr311-api + 1.1.1 + + + org.onosproject + onos-app-vtn-rsc + ${project.version} + + + + + + org.apache.felix + maven-bundle-plugin + true + + + <_wab>src/main/webapp/ + + ${project.groupId}.${project.artifactId} + + + 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.* + + ${web.context} + + + + + + + \ 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 index 00000000..deb9ca37 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java @@ -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 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 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 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 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 createOrUpdateByInputStream(JsonNode subnode) { + checkNotNull(subnode, JSON_NOT_NULL); + Iterable 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 changeJsonToSubs(JsonNode subnetNodes) { + checkNotNull(subnetNodes, JSON_NOT_NULL); + Map 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 hostRoutesIt = jsonNodeToHostRoutes(hostRoutes); + JsonNode allocationPools = subnetNode.get("allocation_pools"); + Iterable 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 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 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 hostRoutesIt = jsonNodeToHostRoutes(hostRoutes); + JsonNode allocationPools = subnetNodes.get("allocation_pools"); + Iterable 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 jsonNodeToAllocationPools(JsonNode allocationPools) { + checkNotNull(allocationPools, JSON_NOT_NULL); + ConcurrentMap 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 jsonNodeToHostRoutes(JsonNode hostRoutes) { + checkNotNull(hostRoutes, JSON_NOT_NULL); + ConcurrentMap 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 item type + * @param message not found message + * @return item if not null + * @throws org.onlab.util.ItemNotFoundException if item is null + */ + protected 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 index 00000000..0b877822 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java @@ -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 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 networks = get(TenantNetworkService.class) + .getNetworks(); + Iterator 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 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 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 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 changeJson2obj(String flag, + TenantNetworkId networkId, + JsonNode node) { + checkNotNull(node, JSON_NOT_NULL); + TenantNetwork network = null; + ConcurrentMap 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 changeJson2objs(JsonNode nodes) { + checkNotNull(nodes, JSON_NOT_NULL); + TenantNetwork network = null; + ConcurrentMap 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 item type + * @param message not found message + * @return item if not null + * @throws org.onlab.util.ItemNotFoundException if item is null + */ + protected 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 index 00000000..03d3a653 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java @@ -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 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 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 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 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 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 changeJsonToPorts(JsonNode vPortNodes) { + checkNotNull(vPortNodes, JSON_NOT_NULL); + Map portMap = new HashMap<>(); + Map 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 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 allowedAddressPairs = + jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode); + JsonNode securityGroupNode = vPortnode.get("security_groups"); + Collection 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 changeJsonToPort(JsonNode vPortNodes) { + checkNotNull(vPortNodes, JSON_NOT_NULL); + Map vportMap = new HashMap<>(); + Map 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 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 allowedAddressPairs = + jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode); + JsonNode securityGroupNode = vPortNodes.get("security_groups"); + Collection 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 jsonNodeToAllowedAddressPair(JsonNode allowedAddressPairs) { + checkNotNull(allowedAddressPairs, JSON_NOT_NULL); + ConcurrentMap 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 jsonNodeToSecurityGroup(JsonNode securityGroups) { + checkNotNull(securityGroups, JSON_NOT_NULL); + ConcurrentMap 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 item type + * @param message not found message + * @return item if not null + * @throws org.onlab.util.ItemNotFoundException if item is null + */ + protected 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 index 00000000..c81fc3d8 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java @@ -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 index 00000000..4cc12455 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,45 @@ + + + + VTNRSC REST API v1.0 + + + JAX-RS Service + com.sun.jersey.spi.container.servlet.ServletContainer + + com.sun.jersey.config.property.resourceConfigClass + com.sun.jersey.api.core.ClassNamesResourceConfig + + + com.sun.jersey.config.property.classnames + + org.onosproject.vtnweb.resources.TenantNetworkWebResource, + org.onosproject.vtnweb.resources.SubnetWebResource, + org.onosproject.vtnweb.resources.VirtualPortWebResource + + + 1 + + + + JAX-RS Service + /* + + -- cgit 1.2.3-korg