CORD fabric app
Change-Id: I2d261762b432170463e1dbc40432193cad28c9b2
diff --git a/apps/cordfabric/pom.xml b/apps/cordfabric/pom.xml
new file mode 100644
index 0000000..008702a
--- /dev/null
+++ b/apps/cordfabric/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>onos-apps</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.2.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>onos-app-cordfabric</artifactId>
+
+ <packaging>bundle</packaging>
+ <description>Simple fabric application for CORD</description>
+
+ <properties>
+ <onos.app.name>org.onosproject.cordfabric</onos.app.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java
new file mode 100644
index 0000000..a6f9d43
--- /dev/null
+++ b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cordfabric;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+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.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+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.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * CORD fabric application.
+ */
+@Service
+@Component(immediate = true)
+public class CordFabricManager implements FabricService {
+
+ private final Logger log = getLogger(getClass());
+
+ private ApplicationId appId;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ private static final int PRIORITY = 1000;
+
+ private final Multimap<VlanId, ConnectPoint> vlans = HashMultimap.create();
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.cordfabric");
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public void addVlan(VlanId vlanId, List<ConnectPoint> ports) {
+ checkNotNull(vlanId);
+ checkNotNull(ports);
+ checkArgument(ports.size() > 1);
+ verifyPorts(ports);
+
+ removeVlan(vlanId);
+
+ ports.forEach(cp -> {
+ if (vlans.put(vlanId, cp)) {
+ addForwarding(vlanId, cp.deviceId(), cp.port(),
+ ports.stream()
+ .filter(p -> p != cp)
+ .map(ConnectPoint::port)
+ .collect(Collectors.toList()));
+ }
+ });
+ }
+
+ @Override
+ public void removeVlan(VlanId vlanId) {
+ vlans.removeAll(vlanId)
+ .forEach(cp -> removeForwarding(vlanId, cp.deviceId(), cp.port()));
+ }
+
+ @Override
+ public Multimap<VlanId, ConnectPoint> getVlans() {
+ return Multimaps.unmodifiableMultimap(vlans);
+ }
+
+ private static void verifyPorts(List<ConnectPoint> ports) {
+ DeviceId deviceId = ports.get(0).deviceId();
+ for (ConnectPoint connectPoint : ports) {
+ if (!connectPoint.deviceId().equals(deviceId)) {
+ throw new IllegalArgumentException("Ports must all be on the same device");
+ }
+ }
+ }
+
+ private void addForwarding(VlanId vlanId, DeviceId deviceId, PortNumber inPort,
+ List<PortNumber> outPorts) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchVlanId(vlanId)
+ .matchInPort(inPort)
+ .build();
+
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+
+ outPorts.forEach(p -> treatmentBuilder.setOutput(p));
+
+ ForwardingObjective objective = DefaultForwardingObjective.builder()
+ .fromApp(appId)
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(PRIORITY)
+ .withSelector(selector)
+ .withTreatment(treatmentBuilder.build())
+ .add(new ObjectiveHandler());
+
+ flowObjectiveService.forward(deviceId, objective);
+ }
+
+ private void removeForwarding(VlanId vlanId, DeviceId deviceId, PortNumber inPort) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchVlanId(vlanId)
+ .matchInPort(inPort)
+ .build();
+
+ ForwardingObjective objective = DefaultForwardingObjective.builder()
+ .fromApp(appId)
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(PRIORITY)
+ .withSelector(selector)
+ .withTreatment(DefaultTrafficTreatment.builder().build())
+ .remove(new ObjectiveHandler());
+
+ flowObjectiveService.forward(deviceId, objective);
+ }
+
+ private static class ObjectiveHandler implements ObjectiveContext {
+ private static Logger log = LoggerFactory.getLogger(ObjectiveHandler.class);
+
+ @Override
+ public void onSuccess(Objective objective) {
+ log.info("Flow objective operation successful: {}", objective);
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.info("Flow objective operation failed: {}", objective);
+ }
+ }
+}
diff --git a/apps/cordfabric/src/main/java/org/onosproject/cordfabric/FabricService.java b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/FabricService.java
new file mode 100644
index 0000000..60ece29
--- /dev/null
+++ b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/FabricService.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.cordfabric;
+
+import com.google.common.collect.Multimap;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.List;
+
+/**
+ * Service used to interact with fabric.
+ */
+public interface FabricService {
+
+ /**
+ * Remaps a vlan to the specified ports. The specified ports will be the
+ * only ports in this vlan once the operation completes.
+ *
+ * @param vlanId vlan ID to add/modify
+ * @param ports list of ports to add to the vlan
+ */
+ void addVlan(VlanId vlanId, List<ConnectPoint> ports);
+
+ /**
+ * Removes a vlan from all ports in the fabric.
+ *
+ * @param vlanId ID of vlan to remove
+ */
+ void removeVlan(VlanId vlanId);
+
+ /**
+ * Returns the vlan to port mapping for all vlans/ports configured in the
+ * fabric.
+ *
+ * @return mapping of vlan to port
+ */
+ Multimap<VlanId, ConnectPoint> getVlans();
+}
diff --git a/apps/cordfabric/src/main/java/org/onosproject/cordfabric/cli/FabricAddCommand.java b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/cli/FabricAddCommand.java
new file mode 100644
index 0000000..cedde04
--- /dev/null
+++ b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/cli/FabricAddCommand.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cordfabric.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordfabric.FabricService;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adds a vlan to the fabric.
+ */
+@Command(scope = "onos", name = "add-fabric-vlan",
+ description = "Adds a VLAN to the fabric")
+public class FabricAddCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "vlanid", description = "VLAN ID",
+ required = true, multiValued = false)
+ private String vlanIdString = null;
+
+ @Argument(index = 1, name = "ports",
+ description = "List of ports in the VLAN",
+ required = true, multiValued = true)
+ private String[] portStrings = null;
+
+ @Override
+ protected void execute() {
+ FabricService service = AbstractShellCommand.get(FabricService.class);
+
+ VlanId vlan = VlanId.vlanId(Short.parseShort(vlanIdString));
+
+ if (portStrings.length < 2) {
+ throw new IllegalArgumentException("Must have at least 2 ports");
+ }
+
+ List<ConnectPoint> ports = new ArrayList<>(portStrings.length);
+
+ for (String portString : portStrings) {
+ ports.add(ConnectPoint.deviceConnectPoint(portString));
+ }
+
+ service.addVlan(vlan, ports);
+ }
+}
diff --git a/apps/cordfabric/src/main/java/org/onosproject/cordfabric/cli/FabricShowCommand.java b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/cli/FabricShowCommand.java
new file mode 100644
index 0000000..1db4222
--- /dev/null
+++ b/apps/cordfabric/src/main/java/org/onosproject/cordfabric/cli/FabricShowCommand.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.cordfabric.cli;
+
+import com.google.common.collect.Multimap;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordfabric.FabricService;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Shows the vlans in the fabric.
+ */
+@Command(scope = "onos", name = "fabric",
+ description = "Shows the fabric vlans")
+public class FabricShowCommand extends AbstractShellCommand {
+
+ private static final String VLAN_HEADER_LINE_FORMAT = "VLAN %s";
+ private static final String PORT_LINE_FORMAT = "\t%s";
+
+ @Override
+ protected void execute() {
+ FabricService service = AbstractShellCommand.get(FabricService.class);
+
+ Multimap<VlanId, ConnectPoint> vlans = service.getVlans();
+
+ vlans.keySet().forEach(vlanId -> {
+ print(VLAN_HEADER_LINE_FORMAT, vlanId);
+ vlans.get(vlanId).forEach(cp -> print(PORT_LINE_FORMAT, cp));
+ });
+ }
+}
diff --git a/apps/cordfabric/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/cordfabric/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..9e43327
--- /dev/null
+++ b/apps/cordfabric/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -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.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.cordfabric.cli.FabricShowCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.cordfabric.cli.FabricAddCommand"/>
+ <completers>
+ <ref component-id="placeholderCompleter"/>
+ <ref component-id="connectPointCompleter"/>
+ </completers>
+ </command>
+ </command-bundle>
+
+ <bean id="connectPointCompleter" class="org.onosproject.cli.net.ConnectPointCompleter"/>
+ <bean id="placeholderCompleter" class="org.onosproject.cli.PlaceholderCompleter"/>
+</blueprint>
diff --git a/apps/pom.xml b/apps/pom.xml
index dbed6e7..c78e780 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -47,6 +47,7 @@
<module>bgprouter</module>
<module>test</module>
<module>segmentrouting</module>
+ <module>cordfabric</module>
</modules>
<properties>
diff --git a/cli/src/main/java/org/onosproject/cli/PlaceholderCompleter.java b/cli/src/main/java/org/onosproject/cli/PlaceholderCompleter.java
new file mode 100644
index 0000000..dbd2a97
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/PlaceholderCompleter.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.cli;
+
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * A completer that can be used as a placeholder for arguments that don't
+ * need/want completers.
+ */
+public class PlaceholderCompleter extends AbstractCompleter {
+
+ @Override
+ public int complete(String s, int i, List<String> list) {
+ // Populate a string completer with what the user has typed so far
+ StringsCompleter delegate = new StringsCompleter();
+ SortedSet<String> strings = delegate.getStrings();
+ if (s != null) {
+ strings.add(s);
+ }
+ return delegate.complete(s, i, list);
+ }
+}