diff options
12 files changed, 835 insertions, 5 deletions
diff --git a/framework/src/onos/apps/dhcp/pom.xml b/framework/src/onos/apps/dhcp/pom.xml index 7a10776e..473caea6 100644 --- a/framework/src/onos/apps/dhcp/pom.xml +++ b/framework/src/onos/apps/dhcp/pom.xml @@ -36,7 +36,4 @@ <module>app</module> </modules> - <dependencies> - </dependencies> - </project> diff --git a/framework/src/onos/apps/routing-api/pom.xml b/framework/src/onos/apps/routing-api/pom.xml index 48fc2920..4c2d7d37 100644 --- a/framework/src/onos/apps/routing-api/pom.xml +++ b/framework/src/onos/apps/routing-api/pom.xml @@ -53,6 +53,21 @@ <dependency> <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${project.version}</version> + <scope>test</scope> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> <artifactId>onlab-junit</artifactId> <scope>test</scope> </dependency> diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/config/BgpConfig.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/config/BgpConfig.java index 2f1ede79..6085c60a 100644 --- a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/config/BgpConfig.java +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/config/BgpConfig.java @@ -17,12 +17,16 @@ package org.onosproject.routing.config; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Sets; import org.onlab.packet.IpAddress; import org.onosproject.core.ApplicationId; import org.onosproject.net.ConnectPoint; import org.onosproject.net.config.Config; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -49,6 +53,11 @@ public class BgpConfig extends Config<ApplicationId> { Set<BgpSpeakerConfig> speakers = Sets.newHashSet(); JsonNode speakersNode = object.get(SPEAKERS); + + if (speakersNode == null) { + return speakers; + } + speakersNode.forEach(jsonNode -> { Set<IpAddress> listenAddresses = Sets.newHashSet(); jsonNode.path(PEERS).forEach(addressNode -> @@ -71,6 +80,130 @@ public class BgpConfig extends Config<ApplicationId> { } /** + * Examines whether a name of BGP speaker exists in configuration. + * + * @param name name of BGP speaker being search + * @return speaker + */ + public BgpSpeakerConfig getSpeakerWithName(String name) { + for (BgpConfig.BgpSpeakerConfig speaker : bgpSpeakers()) { + if (speaker.name().isPresent() && speaker.name().get().equals(name)) { + return speaker; + } + } + return null; + } + + /** + * Adds BGP speaker to configuration. + * + * @param speaker BGP speaker configuration entry + */ + public void addSpeaker(BgpSpeakerConfig speaker) { + ObjectNode speakerNode = JsonNodeFactory.instance.objectNode(); + + speakerNode.put(NAME, speaker.name().get()); + + speakerNode.put(CONNECT_POINT, speaker.connectPoint().elementId().toString() + + "/" + speaker.connectPoint().port().toString()); + + ArrayNode peersNode = speakerNode.putArray(PEERS); + for (IpAddress peerAddress: speaker.peers()) { + peersNode.add(peerAddress.toString()); + } + + ArrayNode speakersArray = bgpSpeakers().isEmpty() ? + initBgpConfiguration() : (ArrayNode) object.get(SPEAKERS); + speakersArray.add(speakerNode); + } + + /** + * Removes BGP speaker from configuration. + * + * @param speakerName BGP speaker name + */ + public void removeSpeaker(String speakerName) { + ArrayNode speakersArray = (ArrayNode) object.get(SPEAKERS); + + for (int i = 0; i < speakersArray.size(); i++) { + if (speakersArray.get(i).hasNonNull(NAME) && + speakersArray.get(i).get(NAME).asText().equals(speakerName)) { + speakersArray.remove(i); + return; + } + } + } + + /** + * Adds peering address to BGP speaker. + * + * @param speakerName name of BGP speaker + * @param peerAddress peering address to be added + */ + public void addPeerToSpeaker(String speakerName, IpAddress peerAddress) { + JsonNode speakersNode = object.get(SPEAKERS); + speakersNode.forEach(jsonNode -> { + if (jsonNode.hasNonNull(NAME) && + jsonNode.get(NAME).asText().equals(speakerName)) { + ArrayNode peersNode = (ArrayNode) jsonNode.get(PEERS); + for (int i = 0; i < peersNode.size(); i++) { + if (peersNode.get(i).asText().equals(peerAddress.toString())) { + return; // Peer already exists. + } + } + peersNode.add(peerAddress.toString()); + } + }); + } + + /** + * Finds BGP speaker peering with a given external peer. + * + * @param peerAddress peering address to be removed + * @return speaker + */ + public BgpSpeakerConfig getSpeakerFromPeer(IpAddress peerAddress) { + for (BgpConfig.BgpSpeakerConfig speaker : bgpSpeakers()) { + if (speaker.peers().contains(peerAddress)) { + return speaker; + } + } + return null; + } + + /** + * Removes peering address from BGP speaker. + * + * @param speaker BGP speaker configuration entries + * @param peerAddress peering address to be removed + */ + public void removePeerFromSpeaker(BgpSpeakerConfig speaker, IpAddress peerAddress) { + JsonNode speakersNode = object.get(SPEAKERS); + speakersNode.forEach(jsonNode -> { + if (jsonNode.hasNonNull(NAME) && + jsonNode.get(NAME).asText().equals(speaker.name().get())) { + ArrayNode peersNode = (ArrayNode) jsonNode.get(PEERS); + for (int i = 0; i < peersNode.size(); i++) { + if (peersNode.get(i).asText().equals(peerAddress.toString())) { + peersNode.remove(i); + return; + } + } + } + }); + } + + /** + * Creates empty configuration for BGP speakers. + * + * @return empty array of BGP speakers + */ + private ArrayNode initBgpConfiguration() { + return object.putArray(SPEAKERS); + } + + + /** * Configuration for a BGP speaker. */ public static class BgpSpeakerConfig { @@ -97,5 +230,39 @@ public class BgpConfig extends Config<ApplicationId> { public Set<IpAddress> peers() { return peers; } + + /** + * Examines if BGP peer is connected. + * + * @param peer IP address of peer + * @return result of search + */ + public boolean isConnectedToPeer(IpAddress peer) { + for (final IpAddress entry : peers()) { + if (entry.equals(peer)) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BgpSpeakerConfig) { + final BgpSpeakerConfig that = (BgpSpeakerConfig) obj; + return Objects.equals(this.name, that.name) && + Objects.equals(this.connectPoint, that.connectPoint) && + Objects.equals(this.peers, that.peers); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(name, connectPoint, peers); + } } } diff --git a/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/config/BgpConfigTest.java b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/config/BgpConfigTest.java new file mode 100644 index 00000000..28f52b79 --- /dev/null +++ b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/config/BgpConfigTest.java @@ -0,0 +1,244 @@ +/* + * 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.config; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.IpAddress; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.ConfigApplyDelegate; +import org.onosproject.routing.RoutingService; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class BgpConfigTest { + + private static final ApplicationId APP_ID = + new TestApplicationId(RoutingService.ROUTER_APP_ID); + + private static final IpAddress IP1 = IpAddress.valueOf("10.0.1.1"); + private static final IpAddress IP2 = IpAddress.valueOf("10.0.2.1"); + private static final IpAddress IP3 = IpAddress.valueOf("10.0.3.1"); + private static final IpAddress IP4 = IpAddress.valueOf("10.0.101.1"); + private static final IpAddress IP5 = IpAddress.valueOf("10.0.201.1"); + public static final IpAddress IP_NON_EXIST = IpAddress.valueOf("10.101.1.1"); + + public static final ConnectPoint CONNECT_POINT1 = ConnectPoint. + deviceConnectPoint("of:0000000000000001/1"); + public static final ConnectPoint CONNECT_POINT2 = ConnectPoint. + deviceConnectPoint("of:00000000000000a3/1"); + + private static final String JSON_TREE = "{\"" + BgpConfig.SPEAKERS + + "\" : [{\"" + BgpConfig.NAME + "\" : \"bgp1\"," + + "\"" + BgpConfig.CONNECT_POINT + + "\" : \"of:0000000000000001/1\"," + + "\"" + BgpConfig.PEERS + "\" : [" + + "\"10.0.1.1\",\"10.0.2.1\",\"10.0.3.1\"]}]}"; + private static final String EMPTY_JSON_TREE = "{}"; + + private final ObjectMapper mapper = new ObjectMapper(); + private final ConfigApplyDelegate delegate = new MockCfgDelegate(); + private final BgpConfig.BgpSpeakerConfig initialSpeaker = createInitialSpeaker(); + + private Set<BgpConfig.BgpSpeakerConfig> speakers = new HashSet<>(); + private BgpConfig bgpConfig = new BgpConfig(); + private BgpConfig emptyBgpConfig = new BgpConfig(); + + @Before + public void setUp() throws Exception { + JsonNode tree = new ObjectMapper().readTree(JSON_TREE); + bgpConfig.init(APP_ID, "bgp-test", tree, mapper, delegate); + JsonNode emptyTree = new ObjectMapper().readTree(EMPTY_JSON_TREE); + emptyBgpConfig.init(APP_ID, "bgp-test", emptyTree, mapper, delegate); + speakers.add(initialSpeaker); + } + + /** + * Tests if speakers can be retrieved from JSON. + */ + @Test + public void testBgpSpeakers() throws Exception { + assertEquals(speakers, bgpConfig.bgpSpeakers()); + } + + /** + * Tests if speakers can be retrieved from empty JSON. + */ + @Test + public void testEmptyBgpSpeakers() throws Exception { + assertTrue(emptyBgpConfig.bgpSpeakers().isEmpty()); + } + + /** + * Tests if speaker can be found by name. + */ + @Test + public void testGetSpeakerWithName() throws Exception { + assertNotNull(bgpConfig.getSpeakerWithName("bgp1")); + assertNull(bgpConfig.getSpeakerWithName("bgp2")); + } + + /** + * Tests addition of new speaker. + */ + @Test + public void testAddSpeaker() throws Exception { + int initialSize = bgpConfig.bgpSpeakers().size(); + BgpConfig.BgpSpeakerConfig newSpeaker = createNewSpeaker(); + bgpConfig.addSpeaker(newSpeaker); + assertEquals(initialSize + 1, bgpConfig.bgpSpeakers().size()); + speakers.add(newSpeaker); + assertEquals(speakers, bgpConfig.bgpSpeakers()); + } + + /** + * Tests addition of new speaker to empty configuration. + */ + @Test + public void testAddSpeakerToEmpty() throws Exception { + BgpConfig.BgpSpeakerConfig newSpeaker = createNewSpeaker(); + emptyBgpConfig.addSpeaker(newSpeaker); + + assertFalse(emptyBgpConfig.bgpSpeakers().isEmpty()); + } + + /** + * Tests removal of existing speaker. + */ + @Test + public void testRemoveExistingSpeaker() throws Exception { + int initialSize = bgpConfig.bgpSpeakers().size(); + bgpConfig.removeSpeaker("bgp1"); + + assertEquals(initialSize - 1, bgpConfig.bgpSpeakers().size()); + } + + /** + * Tests removal of non-existing speaker. + */ + @Test + public void testRemoveInexistingSpeaker() throws Exception { + int initialSize = bgpConfig.bgpSpeakers().size(); + bgpConfig.removeSpeaker("bgp2"); + + assertEquals(initialSize, bgpConfig.bgpSpeakers().size()); + } + + /** + * Tests addition of new speaker. + */ + @Test + public void testAddPeerToSpeaker() throws Exception { + int initialSize = bgpConfig.getSpeakerWithName("bgp1").peers().size(); + bgpConfig.addPeerToSpeaker("bgp1", IP4); + + assertEquals(initialSize + 1, bgpConfig.getSpeakerWithName("bgp1").peers().size()); + } + + /** + * Tests addition of new speaker when peer already exists. + */ + @Test + public void testAddExistingPeerToSpeaker() throws Exception { + int initialSize = bgpConfig.getSpeakerWithName("bgp1").peers().size(); + bgpConfig.addPeerToSpeaker("bgp1", IP1); + + assertEquals(initialSize, bgpConfig.getSpeakerWithName("bgp1").peers().size()); + } + + /** + * Tests retrieval of speaker based on peering address. + */ + @Test + public void testGetSpeakerFromPeer() throws Exception { + assertNotNull(bgpConfig.getSpeakerFromPeer(IP1)); + assertNull(bgpConfig.getSpeakerFromPeer(IP_NON_EXIST)); + } + + /** + * Tests removal of peer. + */ + @Test + public void testRemoveExistingPeerFromSpeaker() throws Exception { + int initialSize = bgpConfig.getSpeakerWithName("bgp1").peers().size(); + bgpConfig.removePeerFromSpeaker(initialSpeaker, IP1); + + assertEquals(initialSize - 1, bgpConfig.getSpeakerWithName("bgp1").peers().size()); + } + + /** + * Tests peer removal when peer does not exist. + */ + @Test + public void testRemoveNonExistingPeerFromSpeaker() throws Exception { + int initialSize = bgpConfig.getSpeakerWithName("bgp1").peers().size(); + bgpConfig.removePeerFromSpeaker(initialSpeaker, IP_NON_EXIST); + + assertEquals(initialSize, bgpConfig.getSpeakerWithName("bgp1").peers().size()); + } + + /** + * Tests if connections to peers are found. + */ + @Test + public void testIsConnectedToPeer() { + BgpConfig.BgpSpeakerConfig speaker = createNewSpeaker(); + + assertTrue(speaker.isConnectedToPeer(IP4)); + assertFalse(speaker.isConnectedToPeer(IP_NON_EXIST)); + } + + private class MockCfgDelegate implements ConfigApplyDelegate { + + @Override + public void onApply(@SuppressWarnings("rawtypes") Config config) { + config.apply(); + } + + } + + private BgpConfig.BgpSpeakerConfig createInitialSpeaker() { + Optional<String> speakerName = Optional.of("bgp1"); + ConnectPoint connectPoint = CONNECT_POINT1; + Set<IpAddress> connectedPeers = new HashSet<>(Arrays.asList(IP1, IP2, IP3)); + + return new BgpConfig.BgpSpeakerConfig(speakerName, connectPoint, connectedPeers); + } + + private BgpConfig.BgpSpeakerConfig createNewSpeaker() { + Optional<String> speakerName = Optional.of("newSpeaker"); + ConnectPoint connectPoint = CONNECT_POINT2; + Set<IpAddress> connectedPeers = new HashSet<>( + Arrays.asList(IP4, IP5)); + + return new BgpConfig.BgpSpeakerConfig(speakerName, connectPoint, connectedPeers); + } +} diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/cli/BgpSpeakersListCommand.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/cli/BgpSpeakersListCommand.java index 23d7086c..064080fd 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/cli/BgpSpeakersListCommand.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/cli/BgpSpeakersListCommand.java @@ -50,13 +50,17 @@ public class BgpSpeakersListCommand extends AbstractShellCommand { ApplicationId appId = coreService.getAppId(RoutingService.ROUTER_APP_ID); BgpConfig config = configService.getConfig(appId, BgpConfig.class); + if (config == null) { + print("No speakers configured"); + return; + } List<BgpConfig.BgpSpeakerConfig> bgpSpeakers = Lists.newArrayList(config.bgpSpeakers()); Collections.sort(bgpSpeakers, SPEAKERS_COMPARATOR); - if (config == null || config.bgpSpeakers().isEmpty()) { + if (config.bgpSpeakers().isEmpty()) { print("No speakers configured"); } else { bgpSpeakers.forEach( diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/AddPeerCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/AddPeerCommand.java new file mode 100644 index 00000000..81231ab9 --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/AddPeerCommand.java @@ -0,0 +1,96 @@ +/* + * 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.sdnip.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.IpAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.incubator.net.intf.InterfaceService; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.routing.RoutingService; +import org.onosproject.routing.config.BgpConfig; + +/** + * Command to add new BGP peer to existing internal speaker. + */ +@Command(scope = "onos", name = "add-bgp-peer", + description = "Adds an external BGP router as peer to an existing BGP speaker") +public class AddPeerCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "name", + description = "Name of the internal BGP speaker", + required = true, multiValued = false) + String name = null; + + @Argument(index = 1, name = "ip", + description = "IP address of the BGP peer", + required = true, multiValued = false) + String ip = null; + + private static final String PEER_ADD_SUCCESS = "Peer Successfully Added."; + private static final String NO_CONFIGURATION = "No speakers configured"; + private static final String SPEAKER_NOT_FOUND = + "Speaker with name \'%s\' not found"; + private static final String NO_INTERFACE = + "No matching interface found for IP \'%s\'"; + + private IpAddress peerAddress = null; + + @Override + protected void execute() { + peerAddress = IpAddress.valueOf(ip); + + NetworkConfigService configService = get(NetworkConfigService.class); + CoreService coreService = get(CoreService.class); + ApplicationId appId = coreService.getAppId(RoutingService.ROUTER_APP_ID); + + BgpConfig config = configService.getConfig(appId, BgpConfig.class); + if (config == null || config.bgpSpeakers().isEmpty()) { + print(NO_CONFIGURATION); + return; + } + + BgpConfig.BgpSpeakerConfig speaker = config.getSpeakerWithName(name); + if (speaker == null) { + print(SPEAKER_NOT_FOUND, name); + return; + } else { + if (speaker.isConnectedToPeer(peerAddress)) { + return; // Peering already exists. + } + } + + InterfaceService interfaceService = get(InterfaceService.class); + if (interfaceService.getMatchingInterface(peerAddress) == null) { + print(NO_INTERFACE, ip); + return; + } + + addPeerToSpeakerConf(config); + configService.applyConfig(appId, BgpConfig.class, config.node()); + + print(PEER_ADD_SUCCESS); + } + + private void addPeerToSpeakerConf(BgpConfig config) { + log.debug("Creating BGP configuration for new peer: {}", ip); + config.addPeerToSpeaker(name, peerAddress); + } +} diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/AddSpeakerCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/AddSpeakerCommand.java new file mode 100644 index 00000000..84353852 --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/AddSpeakerCommand.java @@ -0,0 +1,90 @@ +/* + * 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.sdnip.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.IpAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.routing.RoutingService; +import org.onosproject.routing.config.BgpConfig; + +import java.util.HashSet; +import java.util.Optional; + +/** + * Command to add a new internal BGP speaker. + */ +@Command(scope = "onos", name = "add-bgp-speaker", + description = "Adds an internal BGP speaker") +public class AddSpeakerCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "name", + description = "Name of the internal BGP speaker", + required = true, multiValued = false) + String name = null; + + @Argument(index = 1, name = "connectionPoint", + description = "Interface to the BGP speaker", + required = true, multiValued = false) + String connectionPoint = null; + + private static final String SPEAKER_ADD_SUCCESS = "Speaker Successfully Added."; + + @Override + protected void execute() { + NetworkConfigService configService = get(NetworkConfigService.class); + CoreService coreService = get(CoreService.class); + ApplicationId appId = coreService.getAppId(RoutingService.ROUTER_APP_ID); + + BgpConfig config = configService.addConfig(appId, BgpConfig.class); + + BgpConfig.BgpSpeakerConfig speaker = config.getSpeakerWithName(name); + if (speaker != null) { + log.debug("Speaker already exists: {}", name); + return; + } + + addSpeakerToConf(config); + configService.applyConfig(appId, BgpConfig.class, config.node()); + + print(SPEAKER_ADD_SUCCESS); + } + + /** + * Adds the speaker to the BgpConfig service. + * + * @param config the BGP configuration + */ + private void addSpeakerToConf(BgpConfig config) { + log.debug("Adding new speaker to configuration: {}", name); + BgpConfig.BgpSpeakerConfig speaker = getSpeaker(); + + config.addSpeaker(speaker); + } + + private BgpConfig.BgpSpeakerConfig getSpeaker() { + ConnectPoint connectPoint = ConnectPoint. + deviceConnectPoint(connectionPoint); + return new BgpConfig.BgpSpeakerConfig(Optional.ofNullable(name), + connectPoint, new HashSet<IpAddress>()); + } +} diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RemovePeerCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RemovePeerCommand.java new file mode 100644 index 00000000..bfc6fb7b --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RemovePeerCommand.java @@ -0,0 +1,81 @@ +/* + * 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.sdnip.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onlab.packet.IpAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.routing.RoutingService; +import org.onosproject.routing.config.BgpConfig; + +/** + * Command to remove existing BGP peer. + */ +@Command(scope = "onos", name = "remove-bgp-peer", + description = "Removes a BGP peer") +public class RemovePeerCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "ip", + description = "IP address of the BGP peer", + required = true, multiValued = false) + String ip = null; + + private static final String PEER_REMOVE_SUCCESS = "Peer Successfully Removed."; + private static final String NO_CONFIGURATION = "No speakers configured"; + private static final String PEER_NOT_FOUND = + "Peer with IP \'%s\' not found"; + + private IpAddress peerAddress = null; + + @Override + protected void execute() { + peerAddress = IpAddress.valueOf(ip); + + NetworkConfigService configService = get(NetworkConfigService.class); + CoreService coreService = get(CoreService.class); + ApplicationId appId = coreService.getAppId(RoutingService.ROUTER_APP_ID); + + BgpConfig config = configService.getConfig(appId, BgpConfig.class); + if (config == null || config.bgpSpeakers().isEmpty()) { + print(NO_CONFIGURATION); + return; + } + + peerAddress = IpAddress.valueOf(ip); + + BgpConfig.BgpSpeakerConfig speaker = config.getSpeakerFromPeer(peerAddress); + if (speaker == null) { + print(PEER_NOT_FOUND, ip); + return; + } + + removePeerFromSpeakerConf(speaker, config); + configService.applyConfig(appId, BgpConfig.class, config.node()); + + print(PEER_REMOVE_SUCCESS); + } + + private void removePeerFromSpeakerConf(BgpConfig.BgpSpeakerConfig speaker, + BgpConfig config) { + log.debug("Removing BGP configuration for peer: {}", ip); + config.removePeerFromSpeaker(speaker, peerAddress); + } +} diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RemoveSpeakerCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RemoveSpeakerCommand.java new file mode 100644 index 00000000..6a51b42f --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RemoveSpeakerCommand.java @@ -0,0 +1,87 @@ +/* + * 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.sdnip.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.routing.RoutingService; +import org.onosproject.routing.config.BgpConfig; + +/** + * Command to remove a internal BGP speaker. + */ +@Command(scope = "onos", name = "remove-bgp-speaker", + description = "Removes an internal BGP speaker") +public class RemoveSpeakerCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "name", + description = "Name of the internal BGP speaker", + required = true, multiValued = false) + String name = null; + + private static final String SPEAKER_REMOVE_SUCCESS = "Speaker Successfully Removed."; + private static final String NO_CONFIGURATION = "No speakers configured"; + private static final String PEERS_EXIST = + "Speaker with name \'%s\' has peer connections"; + private static final String SPEAKER_NOT_FOUND = + "Speaker with name \'%s\' not found"; + + @Override + protected void execute() { + NetworkConfigService configService = get(NetworkConfigService.class); + CoreService coreService = get(CoreService.class); + ApplicationId appId = coreService.getAppId(RoutingService.ROUTER_APP_ID); + + BgpConfig config = configService.getConfig(appId, BgpConfig.class); + if (config == null || config.bgpSpeakers().isEmpty()) { + print(NO_CONFIGURATION); + return; + } + + BgpConfig.BgpSpeakerConfig speaker = config.getSpeakerWithName(name); + if (speaker == null) { + print(SPEAKER_NOT_FOUND, name); + return; + } else { + if (!speaker.peers().isEmpty()) { + // Removal not allowed when peer connections exist. + print(PEERS_EXIST, name); + return; + } + } + + removeSpeakerFromConf(config); + configService.applyConfig(appId, BgpConfig.class, config.node()); + + print(SPEAKER_REMOVE_SUCCESS); + } + + /** + * Removes the speaker from the BgpConfig service. + * + * @param bgpConfig the BGP configuration + */ + private void removeSpeakerFromConf(BgpConfig bgpConfig) { + log.debug("Removing speaker from configuration: {}", name); + + bgpConfig.removeSpeaker(name); + } +} diff --git a/framework/src/onos/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml index 3be1c79a..97b2f09f 100644 --- a/framework/src/onos/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml +++ b/framework/src/onos/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -19,5 +19,17 @@ <command> <action class="org.onosproject.sdnip.cli.PrimaryChangeCommand"/> </command> + <command> + <action class="org.onosproject.sdnip.cli.AddSpeakerCommand"/> + </command> + <command> + <action class="org.onosproject.sdnip.cli.RemoveSpeakerCommand"/> + </command> + <command> + <action class="org.onosproject.sdnip.cli.AddPeerCommand"/> + </command> + <command> + <action class="org.onosproject.sdnip.cli.RemovePeerCommand"/> + </command> </command-bundle> </blueprint> diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java index 8cd63e7d..1695e5ff 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java @@ -59,6 +59,7 @@ import org.onosproject.net.flow.FlowRuleStore; import org.onosproject.net.flow.FlowRuleStoreDelegate; import org.onosproject.net.flow.StoredFlowEntry; import org.onosproject.net.flow.TableStatisticsEntry; +import org.onosproject.persistence.PersistenceService; import org.onosproject.store.AbstractStore; import org.onosproject.store.cluster.messaging.ClusterCommunicationService; import org.onosproject.store.cluster.messaging.ClusterMessage; @@ -74,6 +75,7 @@ import org.onosproject.store.serializers.custom.DistributedStoreSerializers; import org.onosproject.store.service.EventuallyConsistentMap; import org.onosproject.store.service.EventuallyConsistentMapEvent; import org.onosproject.store.service.EventuallyConsistentMapListener; +import org.onosproject.store.service.Serializer; import org.onosproject.store.service.StorageService; import org.onosproject.store.service.WallClockTimestamp; import org.osgi.service.component.ComponentContext; @@ -113,6 +115,7 @@ public class NewDistributedFlowRuleStore private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 8; private static final boolean DEFAULT_BACKUP_ENABLED = true; + private static final boolean DEFAULT_PERSISTENCE_ENABLED = false; private static final int DEFAULT_BACKUP_PERIOD_MILLIS = 2000; private static final long FLOW_RULE_STORE_TIMEOUT_MILLIS = 5000; // number of devices whose flow entries will be backed up in one communication round @@ -129,6 +132,9 @@ public class NewDistributedFlowRuleStore @Property(name = "backupPeriod", intValue = DEFAULT_BACKUP_PERIOD_MILLIS, label = "Delay in ms between successive backup runs") private int backupPeriod = DEFAULT_BACKUP_PERIOD_MILLIS; + @Property(name = "persistenceEnabled", boolValue = false, + label = "Indicates whether or not changes in the flow table should be persisted to disk.") + private boolean persistenceEnabled = DEFAULT_PERSISTENCE_ENABLED; private InternalFlowTable flowTable = new InternalFlowTable(); @@ -153,6 +159,9 @@ public class NewDistributedFlowRuleStore @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected MastershipService mastershipService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PersistenceService persistenceService; + private Map<Long, NodeId> pendingResponses = Maps.newConcurrentMap(); private ExecutorService messageHandlingExecutor; @@ -716,7 +725,25 @@ public class NewDistributedFlowRuleStore * @return Map representing Flow Table of given device. */ private Map<FlowId, Set<StoredFlowEntry>> getFlowTable(DeviceId deviceId) { - return flowEntries.computeIfAbsent(deviceId, id -> Maps.newConcurrentMap()); + if (persistenceEnabled) { + return flowEntries.computeIfAbsent(deviceId, id -> persistenceService + .<FlowId, Set<StoredFlowEntry>>persistentMapBuilder() + .withName("FlowTable:" + deviceId.toString()) + .withSerializer(new Serializer() { + @Override + public <T> byte[] encode(T object) { + return SERIALIZER.encode(object); + } + + @Override + public <T> T decode(byte[] bytes) { + return SERIALIZER.decode(bytes); + } + }) + .build()); + } else { + return flowEntries.computeIfAbsent(deviceId, id -> Maps.newConcurrentMap()); + } } private Set<StoredFlowEntry> getFlowEntriesInternal(DeviceId deviceId, FlowId flowId) { diff --git a/framework/src/onos/tools/test/cells/aaron_local_cell b/framework/src/onos/tools/test/cells/aaron_local_cell new file mode 100644 index 00000000..e3b5f734 --- /dev/null +++ b/framework/src/onos/tools/test/cells/aaron_local_cell @@ -0,0 +1,10 @@ +# Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box + +export ONOS_NIC=192.168.56.* +export OC1="192.168.56.101" +export OC2="192.168.56.102" +export OC3="192.168.56.103" +export OCN="192.168.56.100" + +export ONOS_USE_SSH=true +export ONOS_APPS="drivers,openflow,fwd,proxyarp,mobility" |