Jonathan Hart
Committed by Gerrit Code Review

Change OLT app to push Q-in-Q tagging flows rather than transparent VLAN flows.

Device VLAN is set through configuration, subscriber VLAN can be added using
CLI (eventually this will come through a call from the AAA app).

Moving towards generalizing this app as an 'Access Device' app rather than purely OLT.

Change-Id: I9b82b39f6a2dee2c6f10f3fd13b261f3e0313db7
......@@ -37,6 +37,15 @@
<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>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.olt;
import org.onlab.packet.VlanId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.Config;
/**
* Config object for access device data.
*/
public class AccessDeviceConfig extends Config<DeviceId> {
private static final String UPLINK = "uplink";
private static final String VLAN = "vlan";
/**
* Gets the access device configuration for this device.
*
* @return access device configuration
*/
public AccessDeviceData getOlt() {
PortNumber uplink = PortNumber.portNumber(node.path(UPLINK).asText());
VlanId vlan = VlanId.vlanId(Short.parseShort(node.path(VLAN).asText()));
return new AccessDeviceData(subject(), uplink, vlan);
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.olt;
import org.onlab.packet.VlanId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Information about an access device.
*/
public class AccessDeviceData {
private static final String DEVICE_ID_MISSING = "Device ID cannot be null";
private static final String UPLINK_MISSING = "Uplink cannot be null";
private static final String VLAN_MISSING = "VLAN ID cannot be null";
private final DeviceId deviceId;
private final PortNumber uplink;
private final VlanId vlan;
/**
* Class constructor.
*
* @param deviceId access device ID
* @param uplink uplink port number
* @param vlan device VLAN ID
*/
public AccessDeviceData(DeviceId deviceId, PortNumber uplink, VlanId vlan) {
this.deviceId = checkNotNull(deviceId, DEVICE_ID_MISSING);
this.uplink = checkNotNull(uplink, UPLINK_MISSING);
this.vlan = checkNotNull(vlan, VLAN_MISSING);
}
/**
* Retrieves the access device ID.
*
* @return device ID
*/
public DeviceId deviceId() {
return deviceId;
}
/**
* Retrieves the uplink port number.
*
* @return port number
*/
public PortNumber uplink() {
return uplink;
}
/**
* Retrieves the VLAN ID assigned to the device.
*
* @return vlan ID
*/
public VlanId vlan() {
return vlan;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.olt;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
/**
* Service for interacting with an access device (OLT).
*/
public interface AccessDeviceService {
/**
* Provisions connectivity for a subscriber on an access device.
*
* @param port subscriber's connection point
* @param vlan VLAN ID to provision for subscriber
*/
void provisionSubscriber(ConnectPoint port, VlanId vlan);
/**
* Removes provisioned connectivity for a subscriber from an access device.
*
* @param port subscriber's connection point
*/
void removeSubscriber(ConnectPoint port);
}
......@@ -15,7 +15,6 @@
*/
package org.onosproject.olt;
import com.google.common.base.Strings;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -24,13 +23,20 @@ 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.apache.felix.scr.annotations.Service;
import org.onlab.packet.VlanId;
import org.onlab.util.Tools;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
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.config.basics.SubjectFactories;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
......@@ -45,15 +51,17 @@ import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.util.Dictionary;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Sample mobility application. Cleans up flowmods when a host moves.
* Provisions rules on access devices.
*/
@Service
@Component(immediate = true)
public class OLT {
public class OLT implements AccessDeviceService {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......@@ -65,10 +73,14 @@ public class OLT {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry networkConfig;
private final DeviceListener deviceListener = new InternalDeviceListener();
private ApplicationId appId;
private static final VlanId DEFAULT_VLAN = VlanId.vlanId((short) 0);
public static final int OFFSET = 200;
public static final int UPLINK_PORT = 129;
......@@ -94,11 +106,39 @@ public class OLT {
label = "The gfast device id")
private String gfastDevice = GFAST_DEVICE;
private Map<DeviceId, AccessDeviceData> oltData = new ConcurrentHashMap<>();
private InternalNetworkConfigListener configListener =
new InternalNetworkConfigListener();
private static final Class<AccessDeviceConfig> CONFIG_CLASS =
AccessDeviceConfig.class;
private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory =
new ConfigFactory<DeviceId, AccessDeviceConfig>(
SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
@Override
public AccessDeviceConfig createConfig() {
return new AccessDeviceConfig();
}
};
@Activate
public void activate() {
appId = coreService.registerApplication("org.onosproject.olt");
networkConfig.registerConfigFactory(configFactory);
networkConfig.addListener(configListener);
networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach(
subject -> {
AccessDeviceConfig config = networkConfig.getConfig(subject, AccessDeviceConfig.class);
if (config != null) {
AccessDeviceData data = config.getOlt();
oltData.put(data.deviceId(), data);
}
}
);
/*deviceService.addListener(deviceListener);
deviceService.getPorts(DeviceId.deviceId(oltDevice)).stream().forEach(
......@@ -129,6 +169,8 @@ public class OLT {
@Deactivate
public void deactivate() {
networkConfig.removeListener(configListener);
networkConfig.unregisterConfigFactory(configFactory);
log.info("Stopped");
}
......@@ -136,16 +178,13 @@ public class OLT {
public void modified(ComponentContext context) {
Dictionary<?, ?> properties = context.getProperties();
String s = Tools.get(properties, "uplinkPort");
uplinkPort = Strings.isNullOrEmpty(s) ? UPLINK_PORT : Integer.parseInt(s);
s = Tools.get(properties, "oltDevice");
oltDevice = Strings.isNullOrEmpty(s) ? OLT_DEVICE : s;
}
private short fetchVlanId(PortNumber port) {
long p = port.toLong() + OFFSET;
if (p > 4095) {
......@@ -155,7 +194,6 @@ public class OLT {
return (short) p;
}
private void provisionVlanOnPort(String deviceId, int uplinkPort, PortNumber p, short vlanId) {
DeviceId did = DeviceId.deviceId(deviceId);
......@@ -198,7 +236,73 @@ public class OLT {
flowObjectiveService.forward(did, upFwd);
flowObjectiveService.forward(did, downFwd);
}
@Override
public void provisionSubscriber(ConnectPoint port, VlanId vlan) {
AccessDeviceData olt = oltData.get(port.deviceId());
if (olt == null) {
log.warn("No data found for OLT device {}", port.deviceId());
return;
}
provisionVlans(olt.deviceId(), olt.uplink(), port.port(), vlan, olt.vlan());
}
private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
PortNumber subscriberPort,
VlanId subscriberVlan, VlanId deviceVlan) {
TrafficSelector upstream = DefaultTrafficSelector.builder()
.matchVlanId(DEFAULT_VLAN)
.matchInPort(subscriberPort)
.build();
TrafficSelector downstream = DefaultTrafficSelector.builder()
.matchVlanId(deviceVlan)
.matchInPort(uplinkPort)
.build();
TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
.setVlanId(subscriberVlan)
.pushVlan()
.setVlanId(deviceVlan)
.setOutput(uplinkPort)
.build();
TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
.popVlan()
.setVlanId(DEFAULT_VLAN)
.setOutput(subscriberPort)
.build();
ForwardingObjective upFwd = DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(1000)
.makePermanent()
.withSelector(upstream)
.fromApp(appId)
.withTreatment(upstreamTreatment)
.add();
ForwardingObjective downFwd = DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.VERSATILE)
.withPriority(1000)
.makePermanent()
.withSelector(downstream)
.fromApp(appId)
.withTreatment(downstreamTreatment)
.add();
flowObjectiveService.forward(deviceId, upFwd);
flowObjectiveService.forward(deviceId, downFwd);
}
@Override
public void removeSubscriber(ConnectPoint port) {
throw new UnsupportedOperationException("Not yet implemented");
}
private class InternalDeviceListener implements DeviceListener {
......@@ -226,7 +330,27 @@ public class OLT {
}
}
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
switch (event.type()) {
}
case CONFIG_ADDED:
case CONFIG_UPDATED:
if (event.configClass().equals(CONFIG_CLASS)) {
AccessDeviceConfig config =
networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS);
if (config != null) {
oltData.put(config.getOlt().deviceId(), config.getOlt());
}
}
break;
case CONFIG_UNREGISTERED:
case CONFIG_REMOVED:
default:
break;
}
}
}
}
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.olt;
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.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
/**
* Adds a subscriber to an access device.
*/
@Command(scope = "onos", name = "add-subscriber-access",
description = "Adds a subscriber to an access device")
public class SubscriberAddCommand extends AbstractShellCommand {
@Argument(index = 0, name = "deviceId", description = "Access device ID",
required = true, multiValued = false)
private String strDeviceId = null;
@Argument(index = 1, name = "port", description = "Subscriber port number",
required = true, multiValued = false)
private String strPort = null;
@Argument(index = 2, name = "vlanId",
description = "VLAN ID to add",
required = true, multiValued = false)
private String strVlanId = null;
@Override
protected void execute() {
AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
DeviceId deviceId = DeviceId.deviceId(strDeviceId);
PortNumber port = PortNumber.portNumber(strPort);
VlanId vlan = VlanId.vlanId(Short.parseShort(strVlanId));
ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
service.provisionSubscriber(connectPoint, vlan);
}
}
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT 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.olt.SubscriberAddCommand"/>
<completers>
<ref component-id="deviceIdCompleter"/>
<null/>
</completers>
</command>
</command-bundle>
<bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
</blueprint>
......@@ -190,14 +190,14 @@ public interface TrafficTreatment {
/**
* Push MPLS ether type.
*
* @return a treatment builder.
* @return a treatment builder
*/
Builder pushMpls();
/**
* Pops MPLS ether type.
*
* @return a treatment builder.
* @return a treatment builder
*/
Builder popMpls();
......@@ -205,7 +205,7 @@ public interface TrafficTreatment {
* Pops MPLS ether type and set the new ethertype.
*
* @param etherType an ether type
* @return a treatment builder.
* @return a treatment builder
* @deprecated in Drake Release
*/
@Deprecated
......@@ -215,22 +215,22 @@ public interface TrafficTreatment {
* Pops MPLS ether type and set the new ethertype.
*
* @param etherType an ether type
* @return a treatment builder.
* @return a treatment builder
*/
Builder popMpls(EthType etherType);
/**
* Sets the mpls label.
*
* @param mplsLabel MPLS label.
* @return a treatment builder.
* @param mplsLabel MPLS label
* @return a treatment builder
*/
Builder setMpls(MplsLabel mplsLabel);
/**
* Sets the mpls bottom-of-stack indicator bit.
*
* @param mplsBos boolean to set BOS=1 (true) or BOS=0 (false).
* @param mplsBos boolean to set BOS=1 (true) or BOS=0 (false)
* @return a treatment builder.
*/
Builder setMplsBos(boolean mplsBos);
......@@ -288,14 +288,14 @@ public interface TrafficTreatment {
/**
* Pops outermost VLAN tag.
*
* @return a treatment builder.
* @return a treatment builder
*/
Builder popVlan();
/**
* Pushes a new VLAN tag.
*
* @return a treatment builder.
* @return a treatment builder
*/
Builder pushVlan();
......@@ -335,8 +335,8 @@ public interface TrafficTreatment {
/**
* Sets the tunnel id.
*
* @param tunnelId a tunnel id.
* @return a treatment builder.
* @param tunnelId a tunnel id
* @return a treatment builder
*/
Builder setTunnelId(long tunnelId);
......
......@@ -30,7 +30,6 @@ import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.DefaultFlowRule;
......@@ -86,13 +85,13 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
flowRuleService = serviceDirectory.get(FlowRuleService.class);
coreService = serviceDirectory.get(CoreService.class);
try {
/*try {
DeviceProviderService providerService = registry.register(provider);
providerService.deviceConnected(deviceId,
description(deviceId, DEVICE, OLT));
} finally {
registry.unregister(provider);
}
}*/
appId = coreService.registerApplication(
"org.onosproject.driver.OLTPipeline");
......@@ -109,12 +108,12 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
PacketPriority.CONTROL.priorityValue(),
appId, 0, true, null);
flowRuleService.applyFlowRules(flowRule);
//flowRuleService.applyFlowRules(flowRule);
}
@Override
public void filter(FilteringObjective filter) {
throw new UnsupportedOperationException("Single table does not filter.");
throw new UnsupportedOperationException("OLT does not filter.");
}
@Override
......@@ -140,19 +139,11 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
TrafficSelector selector = fwd.selector();
TrafficTreatment treatment = fwd.treatment();
if ((fwd.treatment().deferred().size() == 0) &&
(fwd.treatment().immediate().size() == 0) &&
(fwd.treatment().tableTransition() == null) &&
(!fwd.treatment().clearedDeferred())) {
TrafficTreatment.Builder flowTreatment = DefaultTrafficTreatment.builder();
flowTreatment.add(Instructions.createDrop());
treatment = flowTreatment.build();
}
FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector)
.withTreatment(fwd.treatment())
.withTreatment(treatment)
.fromApp(fwd.appId())
.withPriority(fwd.priority());
......@@ -162,9 +153,7 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
ruleBuilder.makeTemporary(fwd.timeout());
}
switch (fwd.op()) {
case ADD:
flowBuilder.add(ruleBuilder.build());
break;
......@@ -190,16 +179,16 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
}
}
}));
}
@Override
public void next(NextObjective nextObjective) {
throw new UnsupportedOperationException("Single table does not next hop.");
throw new UnsupportedOperationException("OLT does not next hop.");
}
/**
* Build a device description.
*
* @param deviceId a deviceId
* @param key the key of the annotation
* @param value the value for the annotation
......
......@@ -45,7 +45,7 @@ public class EthType {
private final Deserializer<?> deserializer;
/**
* Constucts a new ethertype.
* Constructs a new ethertype.
*
* @param ethType The actual ethertype
* @param type it's textual representation
......