mirror of
https://github.com/opennetworkinglab/onos.git
synced 2026-04-17 19:41:38 +02:00
Implement CLI and REST API for Xconnect
Deprecate the old way of configuring Xconnect via network config Change-Id: I5b9ac7852517c25805bcbfc0e7b3bec3a52eed9f
This commit is contained in:
parent
f76de30781
commit
c7b3c451c7
@ -125,6 +125,7 @@ import org.onosproject.segmentrouting.mcast.McastStoreKey;
|
||||
import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
|
||||
import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
|
||||
import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectService;
|
||||
import org.onosproject.store.serializers.KryoNamespaces;
|
||||
import org.onosproject.store.service.EventuallyConsistentMap;
|
||||
import org.onosproject.store.service.EventuallyConsistentMapBuilder;
|
||||
@ -230,6 +231,9 @@ public class SegmentRoutingManager implements SegmentRoutingService {
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
public LeadershipService leadershipService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
|
||||
public XconnectService xconnectService;
|
||||
|
||||
@Property(name = "activeProbing", boolValue = true,
|
||||
label = "Enable active probing to discover dual-homed hosts.")
|
||||
boolean activeProbing = true;
|
||||
@ -809,11 +813,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
|
||||
ImmutableMap.copyOf(defaultRoutingHandler.shouldProgramCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the application ID from the manager.
|
||||
*
|
||||
* @return application ID
|
||||
*/
|
||||
@Override
|
||||
public ApplicationId appId() {
|
||||
return appId;
|
||||
}
|
||||
@ -890,38 +890,21 @@ public class SegmentRoutingManager implements SegmentRoutingService {
|
||||
return tunnelHandler.getTunnel(tunnelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns internal VLAN for untagged hosts on given connect point.
|
||||
* <p>
|
||||
* The internal VLAN is either vlan-untagged for an access port,
|
||||
* or vlan-native for a trunk port.
|
||||
*
|
||||
* @param connectPoint connect point
|
||||
* @return internal VLAN or null if both vlan-untagged and vlan-native are undefined
|
||||
*/
|
||||
@Override
|
||||
public VlanId getInternalVlanId(ConnectPoint connectPoint) {
|
||||
VlanId untaggedVlanId = interfaceService.getUntaggedVlanId(connectPoint);
|
||||
VlanId nativeVlanId = interfaceService.getNativeVlanId(connectPoint);
|
||||
return untaggedVlanId != null ? untaggedVlanId : nativeVlanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns optional pair device ID of given device.
|
||||
*
|
||||
* @param deviceId device ID
|
||||
* @return optional pair device ID. Might be empty if pair device is not configured
|
||||
*/
|
||||
Optional<DeviceId> getPairDeviceId(DeviceId deviceId) {
|
||||
@Override
|
||||
public Optional<DeviceId> getPairDeviceId(DeviceId deviceId) {
|
||||
SegmentRoutingDeviceConfig deviceConfig =
|
||||
cfgService.getConfig(deviceId, SegmentRoutingDeviceConfig.class);
|
||||
return Optional.ofNullable(deviceConfig).map(SegmentRoutingDeviceConfig::pairDeviceId);
|
||||
}
|
||||
/**
|
||||
* Returns optional pair device local port of given device.
|
||||
*
|
||||
* @param deviceId device ID
|
||||
* @return optional pair device ID. Might be empty if pair device is not configured
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Optional<PortNumber> getPairLocalPort(DeviceId deviceId) {
|
||||
SegmentRoutingDeviceConfig deviceConfig =
|
||||
cfgService.getConfig(deviceId, SegmentRoutingDeviceConfig.class);
|
||||
|
||||
@ -15,10 +15,14 @@
|
||||
*/
|
||||
package org.onosproject.segmentrouting;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.onlab.packet.IpAddress;
|
||||
import org.onlab.packet.IpPrefix;
|
||||
import org.onlab.packet.VlanId;
|
||||
import org.onosproject.cluster.NodeId;
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.net.ConnectPoint;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.Link;
|
||||
@ -38,6 +42,7 @@ import org.onosproject.segmentrouting.mcast.McastStoreKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -46,12 +51,18 @@ import java.util.Set;
|
||||
public interface SegmentRoutingService {
|
||||
/**
|
||||
* VLAN cross-connect ACL priority.
|
||||
*
|
||||
* @deprecated in ONOS 1.12. Replaced by {@link org.onosproject.segmentrouting.xconnect.api.XconnectService}
|
||||
*/
|
||||
@Deprecated
|
||||
int XCONNECT_ACL_PRIORITY = 60000;
|
||||
|
||||
/**
|
||||
* VLAN cross-connect Bridging priority.
|
||||
*
|
||||
* @deprecated in ONOS 1.12. Replaced by {@link org.onosproject.segmentrouting.xconnect.api.XconnectService}
|
||||
*/
|
||||
@Deprecated
|
||||
int XCONNECT_PRIORITY = 1000;
|
||||
|
||||
/**
|
||||
@ -302,4 +313,51 @@ public interface SegmentRoutingService {
|
||||
* @return shouldProgram local cache
|
||||
*/
|
||||
Map<DeviceId, Boolean> getShouldProgramCache();
|
||||
|
||||
/**
|
||||
* Gets application id.
|
||||
*
|
||||
* @return application id
|
||||
*/
|
||||
default ApplicationId appId() {
|
||||
throw new NotImplementedException("appId not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns internal VLAN for untagged hosts on given connect point.
|
||||
* <p>
|
||||
* The internal VLAN is either vlan-untagged for an access port,
|
||||
* or vlan-native for a trunk port.
|
||||
*
|
||||
* @param connectPoint connect point
|
||||
* @return internal VLAN or null if both vlan-untagged and vlan-native are undefined
|
||||
*/
|
||||
@Beta
|
||||
default VlanId getInternalVlanId(ConnectPoint connectPoint) {
|
||||
throw new NotImplementedException("getInternalVlanId not implemented");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns optional pair device ID of given device.
|
||||
*
|
||||
* @param deviceId device ID
|
||||
* @return optional pair device ID. Might be empty if pair device is not configured
|
||||
*/
|
||||
@Beta
|
||||
default Optional<DeviceId> getPairDeviceId(DeviceId deviceId) {
|
||||
throw new NotImplementedException("getPairDeviceId not implemented");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns optional pair device local port of given device.
|
||||
*
|
||||
* @param deviceId device ID
|
||||
* @return optional pair device ID. Might be empty if pair device is not configured
|
||||
*/
|
||||
@Beta
|
||||
default Optional<PortNumber> getPairLocalPort(DeviceId deviceId) {
|
||||
throw new NotImplementedException("getPairLocalPort not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +54,10 @@ import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Handles cross connect related events.
|
||||
*
|
||||
* @deprecated in ONOS 1.12. Replaced by {@link org.onosproject.segmentrouting.xconnect.impl.XconnectManager}
|
||||
*/
|
||||
@Deprecated
|
||||
public class XConnectHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(XConnectHandler.class);
|
||||
private static final String CONFIG_NOT_FOUND = "XConnect config not found";
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.cli;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
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.DeviceId;
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectService;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Creates Xconnect.
|
||||
*/
|
||||
@Command(scope = "onos", name = "sr-xconnect-add", description = "Create Xconnect")
|
||||
public class XconnectAddCommand extends AbstractShellCommand {
|
||||
@Argument(index = 0, name = "deviceId",
|
||||
description = "Device ID",
|
||||
required = true, multiValued = false)
|
||||
private String deviceIdStr;
|
||||
|
||||
@Argument(index = 1, name = "vlanId",
|
||||
description = "VLAN ID",
|
||||
required = true, multiValued = false)
|
||||
private String vlanIdStr;
|
||||
|
||||
@Argument(index = 2, name = "port1",
|
||||
description = "Port 1",
|
||||
required = true, multiValued = false)
|
||||
private String port1Str;
|
||||
|
||||
@Argument(index = 3, name = "port2",
|
||||
description = "Port 2",
|
||||
required = true, multiValued = false)
|
||||
private String port2Str;
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute() {
|
||||
DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
|
||||
VlanId vlanId = VlanId.vlanId(vlanIdStr);
|
||||
PortNumber port1 = PortNumber.portNumber(port1Str);
|
||||
PortNumber port2 = PortNumber.portNumber(port2Str);
|
||||
Set<PortNumber> ports = Sets.newHashSet(port1, port2);
|
||||
|
||||
XconnectService xconnectService = get(XconnectService.class);
|
||||
xconnectService.addOrUpdateXconnect(deviceId, vlanId, ports);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.cli;
|
||||
|
||||
import org.apache.karaf.shell.commands.Command;
|
||||
import org.onosproject.cli.AbstractShellCommand;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectService;
|
||||
|
||||
/**
|
||||
* Lists Xconnects.
|
||||
*/
|
||||
@Command(scope = "onos", name = "sr-xconnect", description = "Lists all Xconnects")
|
||||
public class XconnectListCommand extends AbstractShellCommand {
|
||||
@Override
|
||||
protected void execute() {
|
||||
XconnectService xconnectService = get(XconnectService.class);
|
||||
xconnectService.getXconnects().forEach(desc -> print("%s", desc));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.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.net.DeviceId;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectService;
|
||||
|
||||
/**
|
||||
* Deletes Xconnect.
|
||||
*/
|
||||
@Command(scope = "onos", name = "sr-xconnect-remove", description = "Remove Xconnect")
|
||||
public class XconnectRemoveCommand extends AbstractShellCommand {
|
||||
@Argument(index = 0, name = "deviceId",
|
||||
description = "Device ID",
|
||||
required = true, multiValued = false)
|
||||
private String deviceIdStr;
|
||||
|
||||
@Argument(index = 1, name = "vlanId",
|
||||
description = "VLAN ID",
|
||||
required = true, multiValued = false)
|
||||
private String vlanIdStr;
|
||||
|
||||
@Override
|
||||
protected void execute() {
|
||||
DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
|
||||
VlanId vlanId = VlanId.vlanId(vlanIdStr);
|
||||
|
||||
XconnectService xconnectService = get(XconnectService.class);
|
||||
xconnectService.removeXonnect(deviceId, vlanId);
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,10 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* Configuration object for cross-connect.
|
||||
*
|
||||
* @deprecated in ONOS 1.12. Replaced by {@link org.onosproject.segmentrouting.xconnect.impl.XconnectManager}
|
||||
*/
|
||||
@Deprecated
|
||||
public class XConnectConfig extends Config<ApplicationId> {
|
||||
private static final String VLAN = "vlan";
|
||||
private static final String PORTS = "ports";
|
||||
|
||||
@ -1613,7 +1613,8 @@ public class McastHandler {
|
||||
// Spine-facing port should have no subnet and no xconnect
|
||||
if (srManager.deviceConfiguration() != null &&
|
||||
srManager.deviceConfiguration().getPortSubnets(ingressDevice, port).isEmpty() &&
|
||||
!srManager.xConnectHandler.hasXConnect(new ConnectPoint(ingressDevice, port))) {
|
||||
(srManager.xconnectService == null ||
|
||||
!srManager.xconnectService.hasXconnect(new ConnectPoint(ingressDevice, port)))) {
|
||||
portBuilder.add(port);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.xconnect.api;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.onlab.packet.VlanId;
|
||||
import org.onosproject.codec.CodecContext;
|
||||
import org.onosproject.codec.JsonCodec;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class XconnectCodec extends JsonCodec<XconnectDesc> {
|
||||
private static final String DEVICE_ID = "deviceId";
|
||||
private static final String VLAN_ID = "vlanId";
|
||||
private static final String PORTS = "ports";
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(XconnectCodec.class);
|
||||
|
||||
@Override
|
||||
public ObjectNode encode(XconnectDesc desc, CodecContext context) {
|
||||
final ObjectNode result = context.mapper().createObjectNode();
|
||||
result.put(DEVICE_ID, desc.key().deviceId().toString());
|
||||
result.put(VLAN_ID, desc.key().vlanId().toString());
|
||||
final ArrayNode portNode = result.putArray(PORTS);
|
||||
desc.ports().forEach(port -> portNode.add(port.toString()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XconnectDesc decode(ObjectNode json, CodecContext context) {
|
||||
DeviceId deviceId = DeviceId.deviceId(json.path(DEVICE_ID).asText());
|
||||
VlanId vlanId = VlanId.vlanId(json.path(VLAN_ID).asText());
|
||||
|
||||
Set<PortNumber> ports = Sets.newHashSet();
|
||||
JsonNode portNodes = json.get(PORTS);
|
||||
if (portNodes != null) {
|
||||
portNodes.forEach(portNode -> ports.add(PortNumber.portNumber(portNode.asInt())));
|
||||
}
|
||||
|
||||
XconnectKey key = new XconnectKey(deviceId, vlanId);
|
||||
return new XconnectDesc(key, ports);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.xconnect.api;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import org.onosproject.net.PortNumber;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Xconnect description.
|
||||
*/
|
||||
public class XconnectDesc {
|
||||
private XconnectKey key;
|
||||
private Set<PortNumber> ports;
|
||||
|
||||
/**
|
||||
* Constructs new Xconnect description with given device ID and VLAN ID.
|
||||
*
|
||||
* @param key Xconnect key
|
||||
* @param ports set of ports
|
||||
*/
|
||||
public XconnectDesc(XconnectKey key, Set<PortNumber> ports) {
|
||||
this.key = key;
|
||||
this.ports = ports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Xconnect key.
|
||||
*
|
||||
* @return Xconnect key
|
||||
*/
|
||||
public XconnectKey key() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets ports.
|
||||
*
|
||||
* @return set of ports
|
||||
*/
|
||||
public Set<PortNumber> ports() {
|
||||
return ports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof XconnectDesc)) {
|
||||
return false;
|
||||
}
|
||||
final XconnectDesc other = (XconnectDesc) obj;
|
||||
return Objects.equals(this.key, other.key) &&
|
||||
Objects.equals(this.ports, other.ports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, ports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(getClass())
|
||||
.add("key", key)
|
||||
.add("ports", ports)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.xconnect.api;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import org.onlab.packet.VlanId;
|
||||
import org.onosproject.net.DeviceId;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Xconnect key.
|
||||
*/
|
||||
public class XconnectKey {
|
||||
private DeviceId deviceId;
|
||||
private VlanId vlanId;
|
||||
|
||||
/**
|
||||
* Constructs new XconnectKey with given device ID and VLAN ID.
|
||||
*
|
||||
* @param deviceId device ID
|
||||
* @param vlanId vlan ID
|
||||
*/
|
||||
public XconnectKey(DeviceId deviceId, VlanId vlanId) {
|
||||
this.deviceId = deviceId;
|
||||
this.vlanId = vlanId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets device ID.
|
||||
*
|
||||
* @return device ID of the Xconnect key
|
||||
*/
|
||||
public DeviceId deviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets VLAN ID.
|
||||
*
|
||||
* @return VLAN ID of the Xconnect key
|
||||
*/
|
||||
public VlanId vlanId() {
|
||||
return vlanId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof XconnectKey)) {
|
||||
return false;
|
||||
}
|
||||
final XconnectKey other = (XconnectKey) obj;
|
||||
return Objects.equals(this.deviceId, other.deviceId) &&
|
||||
Objects.equals(this.vlanId, other.vlanId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(deviceId, vlanId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(getClass())
|
||||
.add("deviceId", deviceId)
|
||||
.add("vlanId", vlanId)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.xconnect.api;
|
||||
|
||||
import org.apache.felix.scr.annotations.Service;
|
||||
import org.onlab.packet.VlanId;
|
||||
import org.onosproject.net.ConnectPoint;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.PortNumber;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* VLAN cross connect between exactly two ports.
|
||||
*/
|
||||
@Service
|
||||
public interface XconnectService {
|
||||
|
||||
/**
|
||||
* VLAN cross-connect ACL priority.
|
||||
*/
|
||||
int XCONNECT_ACL_PRIORITY = 60000;
|
||||
|
||||
/**
|
||||
* VLAN cross-connect Bridging priority.
|
||||
*/
|
||||
int XCONNECT_PRIORITY = 1000;
|
||||
|
||||
/**
|
||||
* Creates or updates Xconnect.
|
||||
*
|
||||
* @param deviceId device ID
|
||||
* @param vlanId VLAN ID
|
||||
* @param ports set of ports
|
||||
*/
|
||||
void addOrUpdateXconnect(DeviceId deviceId, VlanId vlanId, Set<PortNumber> ports);
|
||||
|
||||
/**
|
||||
* Deletes Xconnect.
|
||||
*
|
||||
* @param deviceId device ID
|
||||
* @param vlanId VLAN ID
|
||||
*/
|
||||
void removeXonnect(DeviceId deviceId, VlanId vlanId);
|
||||
|
||||
/**
|
||||
* Gets Xconnects.
|
||||
*
|
||||
* @return set of Xconnect descriptions
|
||||
*/
|
||||
Set<XconnectDesc> getXconnects();
|
||||
|
||||
/**
|
||||
* Check if there is Xconnect configured on given connect point.
|
||||
*
|
||||
* @param cp connect point
|
||||
* @return true if there is Xconnect configured on the connect point
|
||||
*/
|
||||
boolean hasXconnect(ConnectPoint cp);
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* VLAN cross connect API.
|
||||
*/
|
||||
package org.onosproject.segmentrouting.xconnect.api;
|
||||
@ -0,0 +1,607 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.xconnect.impl;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.felix.scr.annotations.Activate;
|
||||
import org.apache.felix.scr.annotations.Component;
|
||||
import org.apache.felix.scr.annotations.Deactivate;
|
||||
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.MacAddress;
|
||||
import org.onlab.packet.VlanId;
|
||||
import org.onlab.util.KryoNamespace;
|
||||
import org.onosproject.codec.CodecService;
|
||||
import org.onosproject.core.ApplicationId;
|
||||
import org.onosproject.core.CoreService;
|
||||
import org.onosproject.mastership.MastershipService;
|
||||
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.device.DeviceEvent;
|
||||
import org.onosproject.net.device.DeviceListener;
|
||||
import org.onosproject.net.device.DeviceService;
|
||||
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.Criteria;
|
||||
import org.onosproject.net.flowobjective.DefaultFilteringObjective;
|
||||
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
|
||||
import org.onosproject.net.flowobjective.DefaultNextObjective;
|
||||
import org.onosproject.net.flowobjective.DefaultObjectiveContext;
|
||||
import org.onosproject.net.flowobjective.FilteringObjective;
|
||||
import org.onosproject.net.flowobjective.FlowObjectiveService;
|
||||
import org.onosproject.net.flowobjective.ForwardingObjective;
|
||||
import org.onosproject.net.flowobjective.NextObjective;
|
||||
import org.onosproject.net.flowobjective.Objective;
|
||||
import org.onosproject.net.flowobjective.ObjectiveContext;
|
||||
import org.onosproject.net.flowobjective.ObjectiveError;
|
||||
import org.onosproject.segmentrouting.SegmentRoutingService;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectCodec;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectDesc;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectKey;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectService;
|
||||
import org.onosproject.store.serializers.KryoNamespaces;
|
||||
import org.onosproject.store.service.ConsistentMap;
|
||||
import org.onosproject.store.service.MapEvent;
|
||||
import org.onosproject.store.service.MapEventListener;
|
||||
import org.onosproject.store.service.Serializer;
|
||||
import org.onosproject.store.service.StorageService;
|
||||
import org.onosproject.store.service.Versioned;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Component(immediate = true)
|
||||
public class XconnectManager implements XconnectService {
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
private CoreService coreService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
private CodecService codecService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
private StorageService storageService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
public NetworkConfigService netCfgService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
public DeviceService deviceService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
public FlowObjectiveService flowObjectiveService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||
public MastershipService mastershipService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
|
||||
public SegmentRoutingService srService;
|
||||
|
||||
private static final String APP_NAME = "org.onosproject.xconnect";
|
||||
private static final String ERROR_NOT_MASTER = "Not master controller";
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(XconnectManager.class);
|
||||
|
||||
private ApplicationId appId;
|
||||
private ConsistentMap<XconnectKey, Set<PortNumber>> xconnectStore;
|
||||
private ConsistentMap<XconnectKey, NextObjective> xconnectNextObjStore;
|
||||
|
||||
private final MapEventListener<XconnectKey, Set<PortNumber>> xconnectListener = new XconnectMapListener();
|
||||
private final DeviceListener deviceListener = new InternalDeviceListener();
|
||||
|
||||
@Activate
|
||||
void activate() {
|
||||
appId = coreService.registerApplication(APP_NAME);
|
||||
codecService.registerCodec(XconnectDesc.class, new XconnectCodec());
|
||||
|
||||
KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
|
||||
.register(KryoNamespaces.API)
|
||||
.register(XconnectKey.class);
|
||||
|
||||
xconnectStore = storageService.<XconnectKey, Set<PortNumber>>consistentMapBuilder()
|
||||
.withName("onos-sr-xconnect")
|
||||
.withRelaxedReadConsistency()
|
||||
.withSerializer(Serializer.using(serializer.build()))
|
||||
.build();
|
||||
xconnectStore.addListener(xconnectListener);
|
||||
|
||||
xconnectNextObjStore = storageService.<XconnectKey, NextObjective>consistentMapBuilder()
|
||||
.withName("onos-sr-xconnect-next")
|
||||
.withRelaxedReadConsistency()
|
||||
.withSerializer(Serializer.using(serializer.build()))
|
||||
.build();
|
||||
|
||||
deviceService.addListener(deviceListener);
|
||||
|
||||
log.info("Started");
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
void deactivate() {
|
||||
xconnectStore.removeListener(xconnectListener);
|
||||
deviceService.removeListener(deviceListener);
|
||||
codecService.unregisterCodec(XconnectDesc.class);
|
||||
|
||||
log.info("Stopped");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOrUpdateXconnect(DeviceId deviceId, VlanId vlanId, Set<PortNumber> ports) {
|
||||
log.info("Adding or updating xconnect. deviceId={}, vlanId={}, ports={}",
|
||||
deviceId, vlanId, ports);
|
||||
final XconnectKey key = new XconnectKey(deviceId, vlanId);
|
||||
xconnectStore.put(key, ports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeXonnect(DeviceId deviceId, VlanId vlanId) {
|
||||
log.info("Removing xconnect. deviceId={}, vlanId={}",
|
||||
deviceId, vlanId);
|
||||
final XconnectKey key = new XconnectKey(deviceId, vlanId);
|
||||
xconnectStore.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<XconnectDesc> getXconnects() {
|
||||
return xconnectStore.asJavaMap().entrySet().stream()
|
||||
.map(e -> new XconnectDesc(e.getKey(), e.getValue()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasXconnect(ConnectPoint cp) {
|
||||
return getXconnects().stream().anyMatch(desc ->
|
||||
desc.key().deviceId().equals(cp.deviceId()) && desc.ports().contains(cp.port())
|
||||
);
|
||||
}
|
||||
|
||||
private class XconnectMapListener implements MapEventListener<XconnectKey, Set<PortNumber>> {
|
||||
@Override
|
||||
public void event(MapEvent<XconnectKey, Set<PortNumber>> event) {
|
||||
XconnectKey key = event.key();
|
||||
Versioned<Set<PortNumber>> ports = event.newValue();
|
||||
Versioned<Set<PortNumber>> oldPorts = event.oldValue();
|
||||
|
||||
switch (event.type()) {
|
||||
case INSERT:
|
||||
populateXConnect(key, ports.value());
|
||||
break;
|
||||
case UPDATE:
|
||||
updateXConnect(key, oldPorts.value(), ports.value());
|
||||
break;
|
||||
case REMOVE:
|
||||
revokeXConnect(key, oldPorts.value());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalDeviceListener implements DeviceListener {
|
||||
@Override
|
||||
public void event(DeviceEvent event) {
|
||||
DeviceId deviceId = event.subject().id();
|
||||
if (!mastershipService.isLocalMaster(deviceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.type()) {
|
||||
case DEVICE_ADDED:
|
||||
case DEVICE_AVAILABILITY_CHANGED:
|
||||
case DEVICE_UPDATED:
|
||||
if (deviceService.isAvailable(deviceId)) {
|
||||
init(deviceId);
|
||||
} else {
|
||||
cleanup(deviceId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init(DeviceId deviceId) {
|
||||
getXconnects().stream()
|
||||
.filter(desc -> desc.key().deviceId().equals(deviceId))
|
||||
.forEach(desc -> populateXConnect(desc.key(), desc.ports()));
|
||||
}
|
||||
|
||||
void cleanup(DeviceId deviceId) {
|
||||
xconnectNextObjStore.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().deviceId().equals(deviceId))
|
||||
.forEach(entry -> xconnectNextObjStore.remove(entry.getKey()));
|
||||
log.debug("{} is removed from xConnectNextObjStore", deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates XConnect groups and flows for given key.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param ports a set of ports to be cross-connected
|
||||
*/
|
||||
private void populateXConnect(XconnectKey key, Set<PortNumber> ports) {
|
||||
if (!mastershipService.isLocalMaster(key.deviceId())) {
|
||||
log.info("Abort populating XConnect {}: {}", key, ERROR_NOT_MASTER);
|
||||
return;
|
||||
}
|
||||
|
||||
ports = addPairPort(key.deviceId(), ports);
|
||||
populateFilter(key, ports);
|
||||
populateFwd(key, populateNext(key, ports));
|
||||
populateAcl(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates filtering objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private void populateFilter(XconnectKey key, Set<PortNumber> ports) {
|
||||
ports.forEach(port -> {
|
||||
FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("XConnect FilterObj for {} on port {} populated",
|
||||
key, port),
|
||||
(objective, error) ->
|
||||
log.warn("Failed to populate XConnect FilterObj for {} on port {}: {}",
|
||||
key, port, error));
|
||||
flowObjectiveService.filter(key.deviceId(), filtObjBuilder.add(context));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates next objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private NextObjective populateNext(XconnectKey key, Set<PortNumber> ports) {
|
||||
NextObjective nextObj;
|
||||
if (xconnectNextObjStore.containsKey(key)) {
|
||||
nextObj = xconnectNextObjStore.get(key).value();
|
||||
log.debug("NextObj for {} found, id={}", key, nextObj.id());
|
||||
} else {
|
||||
NextObjective.Builder nextObjBuilder = nextObjBuilder(key, ports);
|
||||
ObjectiveContext nextContext = new DefaultObjectiveContext(
|
||||
// To serialize this with kryo
|
||||
(Serializable & Consumer<Objective>) (objective) ->
|
||||
log.debug("XConnect NextObj for {} added", key),
|
||||
(Serializable & BiConsumer<Objective, ObjectiveError>) (objective, error) ->
|
||||
log.warn("Failed to add XConnect NextObj for {}: {}", key, error)
|
||||
);
|
||||
nextObj = nextObjBuilder.add(nextContext);
|
||||
flowObjectiveService.next(key.deviceId(), nextObj);
|
||||
xconnectNextObjStore.put(key, nextObj);
|
||||
log.debug("NextObj for {} not found. Creating new NextObj with id={}", key, nextObj.id());
|
||||
}
|
||||
return nextObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates bridging forwarding objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param nextObj next objective
|
||||
*/
|
||||
private void populateFwd(XconnectKey key, NextObjective nextObj) {
|
||||
ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id());
|
||||
ObjectiveContext fwdContext = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("XConnect FwdObj for {} populated", key),
|
||||
(objective, error) ->
|
||||
log.warn("Failed to populate XConnect FwdObj for {}: {}", key, error));
|
||||
flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.add(fwdContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates ACL forwarding objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
*/
|
||||
private void populateAcl(XconnectKey key) {
|
||||
ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
|
||||
ObjectiveContext aclContext = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("XConnect AclObj for {} populated", key),
|
||||
(objective, error) ->
|
||||
log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
|
||||
flowObjectiveService.forward(key.deviceId(), aclObjBuilder.add(aclContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes XConnect groups and flows for given key.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private void revokeXConnect(XconnectKey key, Set<PortNumber> ports) {
|
||||
if (!mastershipService.isLocalMaster(key.deviceId())) {
|
||||
log.info("Abort populating XConnect {}: {}", key, ERROR_NOT_MASTER);
|
||||
return;
|
||||
}
|
||||
|
||||
ports = addPairPort(key.deviceId(), ports);
|
||||
revokeFilter(key, ports);
|
||||
if (xconnectNextObjStore.containsKey(key)) {
|
||||
NextObjective nextObj = xconnectNextObjStore.get(key).value();
|
||||
revokeFwd(key, nextObj, null);
|
||||
revokeNext(key, nextObj, null);
|
||||
} else {
|
||||
log.warn("NextObj for {} does not exist in the store.", key);
|
||||
}
|
||||
revokeAcl(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes filtering objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private void revokeFilter(XconnectKey key, Set<PortNumber> ports) {
|
||||
ports.forEach(port -> {
|
||||
FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("XConnect FilterObj for {} on port {} revoked",
|
||||
key, port),
|
||||
(objective, error) ->
|
||||
log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}",
|
||||
key, port, error));
|
||||
flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes next objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param nextObj next objective
|
||||
* @param nextFuture completable future for this next objective operation
|
||||
*/
|
||||
private void revokeNext(XconnectKey key, NextObjective nextObj,
|
||||
CompletableFuture<ObjectiveError> nextFuture) {
|
||||
ObjectiveContext context = new ObjectiveContext() {
|
||||
@Override
|
||||
public void onSuccess(Objective objective) {
|
||||
log.debug("Previous NextObj for {} removed", key);
|
||||
if (nextFuture != null) {
|
||||
nextFuture.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Objective objective, ObjectiveError error) {
|
||||
log.warn("Failed to remove previous NextObj for {}: {}", key, error);
|
||||
if (nextFuture != null) {
|
||||
nextFuture.complete(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
flowObjectiveService.next(key.deviceId(),
|
||||
(NextObjective) nextObj.copy().remove(context));
|
||||
xconnectNextObjStore.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes bridging forwarding objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param nextObj next objective
|
||||
* @param fwdFuture completable future for this forwarding objective operation
|
||||
*/
|
||||
private void revokeFwd(XconnectKey key, NextObjective nextObj,
|
||||
CompletableFuture<ObjectiveError> fwdFuture) {
|
||||
ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id());
|
||||
ObjectiveContext context = new ObjectiveContext() {
|
||||
@Override
|
||||
public void onSuccess(Objective objective) {
|
||||
log.debug("Previous FwdObj for {} removed", key);
|
||||
if (fwdFuture != null) {
|
||||
fwdFuture.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Objective objective, ObjectiveError error) {
|
||||
log.warn("Failed to remove previous FwdObj for {}: {}", key, error);
|
||||
if (fwdFuture != null) {
|
||||
fwdFuture.complete(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.remove(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes ACL forwarding objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
*/
|
||||
private void revokeAcl(XconnectKey key) {
|
||||
ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
|
||||
ObjectiveContext aclContext = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("XConnect AclObj for {} populated", key),
|
||||
(objective, error) ->
|
||||
log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
|
||||
flowObjectiveService.forward(key.deviceId(), aclObjBuilder.remove(aclContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates XConnect groups and flows for given key.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param prevPorts previous XConnect ports
|
||||
* @param ports new XConnect ports
|
||||
*/
|
||||
private void updateXConnect(XconnectKey key, Set<PortNumber> prevPorts,
|
||||
Set<PortNumber> ports) {
|
||||
// NOTE: ACL flow doesn't include port information. No need to update it.
|
||||
// Pair port is built-in and thus not going to change. No need to update it.
|
||||
|
||||
// remove old filter
|
||||
prevPorts.stream().filter(port -> !ports.contains(port)).forEach(port ->
|
||||
revokeFilter(key, ImmutableSet.of(port)));
|
||||
// install new filter
|
||||
ports.stream().filter(port -> !prevPorts.contains(port)).forEach(port ->
|
||||
populateFilter(key, ImmutableSet.of(port)));
|
||||
|
||||
CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>();
|
||||
CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>();
|
||||
|
||||
if (xconnectNextObjStore.containsKey(key)) {
|
||||
NextObjective nextObj = xconnectNextObjStore.get(key).value();
|
||||
revokeFwd(key, nextObj, fwdFuture);
|
||||
|
||||
fwdFuture.thenAcceptAsync(fwdStatus -> {
|
||||
if (fwdStatus == null) {
|
||||
log.debug("Fwd removed. Now remove group {}", key);
|
||||
revokeNext(key, nextObj, nextFuture);
|
||||
}
|
||||
});
|
||||
|
||||
nextFuture.thenAcceptAsync(nextStatus -> {
|
||||
if (nextStatus == null) {
|
||||
log.debug("Installing new group and flow for {}", key);
|
||||
populateFwd(key, populateNext(key, ports));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.warn("NextObj for {} does not exist in the store.", key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a next objective builder for XConnect.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param ports set of XConnect ports
|
||||
* @return next objective builder
|
||||
*/
|
||||
private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<PortNumber> ports) {
|
||||
int nextId = flowObjectiveService.allocateNextId();
|
||||
TrafficSelector metadata =
|
||||
DefaultTrafficSelector.builder().matchVlanId(key.vlanId()).build();
|
||||
NextObjective.Builder nextObjBuilder = DefaultNextObjective
|
||||
.builder().withId(nextId)
|
||||
.withType(NextObjective.Type.BROADCAST).fromApp(appId)
|
||||
.withMeta(metadata);
|
||||
ports.forEach(port -> {
|
||||
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
|
||||
tBuilder.setOutput(port);
|
||||
nextObjBuilder.addTreatment(tBuilder.build());
|
||||
});
|
||||
return nextObjBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bridging forwarding objective builder for XConnect.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param nextId next ID of the broadcast group for this XConnect key
|
||||
* @return forwarding objective builder
|
||||
*/
|
||||
private ForwardingObjective.Builder fwdObjBuilder(XconnectKey key, int nextId) {
|
||||
/*
|
||||
* Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
|
||||
* as the VLAN cross-connect broadcast rules
|
||||
*/
|
||||
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
|
||||
sbuilder.matchVlanId(key.vlanId());
|
||||
sbuilder.matchEthDst(MacAddress.NONE);
|
||||
|
||||
ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
|
||||
fob.withFlag(ForwardingObjective.Flag.SPECIFIC)
|
||||
.withSelector(sbuilder.build())
|
||||
.nextStep(nextId)
|
||||
.withPriority(XCONNECT_PRIORITY)
|
||||
.fromApp(appId)
|
||||
.makePermanent();
|
||||
return fob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ACL forwarding objective builder for XConnect.
|
||||
*
|
||||
* @param vlanId cross connect VLAN id
|
||||
* @return forwarding objective builder
|
||||
*/
|
||||
private ForwardingObjective.Builder aclObjBuilder(VlanId vlanId) {
|
||||
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
|
||||
sbuilder.matchVlanId(vlanId);
|
||||
|
||||
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
|
||||
|
||||
ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
|
||||
fob.withFlag(ForwardingObjective.Flag.VERSATILE)
|
||||
.withSelector(sbuilder.build())
|
||||
.withTreatment(tbuilder.build())
|
||||
.withPriority(XCONNECT_ACL_PRIORITY)
|
||||
.fromApp(appId)
|
||||
.makePermanent();
|
||||
return fob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a filtering objective builder for XConnect.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param port XConnect ports
|
||||
* @return next objective builder
|
||||
*/
|
||||
private FilteringObjective.Builder filterObjBuilder(XconnectKey key, PortNumber port) {
|
||||
FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
|
||||
fob.withKey(Criteria.matchInPort(port))
|
||||
.addCondition(Criteria.matchVlanId(key.vlanId()))
|
||||
.addCondition(Criteria.matchEthDst(MacAddress.NONE))
|
||||
.withPriority(XCONNECT_PRIORITY);
|
||||
return fob.permit().fromApp(appId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add pair port to the given set of port.
|
||||
*
|
||||
* @param deviceId device Id
|
||||
* @param ports ports specified in the xconnect config
|
||||
* @return port specified in the xconnect config plus the pair port (if configured)
|
||||
*/
|
||||
private Set<PortNumber> addPairPort(DeviceId deviceId, Set<PortNumber> ports) {
|
||||
if (srService == null) {
|
||||
return ports;
|
||||
}
|
||||
Set<PortNumber> newPorts = Sets.newHashSet(ports);
|
||||
srService.getPairLocalPort(deviceId).ifPresent(newPorts::add);
|
||||
return newPorts;
|
||||
}
|
||||
|
||||
// TODO DEVICE listener
|
||||
// up : init
|
||||
// down: removeDevice
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* VLAN cross connect implementation.
|
||||
*/
|
||||
package org.onosproject.segmentrouting.xconnect.impl;
|
||||
@ -98,10 +98,33 @@
|
||||
<entry key="-gAddr" value-ref="mcastGroupCompleter"/>
|
||||
</optional-completers>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.segmentrouting.cli.XconnectListCommand"/>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.segmentrouting.cli.XconnectAddCommand"/>
|
||||
<completers>
|
||||
<ref component-id="deviceIdCompleter"/>
|
||||
<ref component-id="placeholderCompleter"/>
|
||||
<ref component-id="portNumberCompleter"/>
|
||||
<ref component-id="portNumberCompleter"/>
|
||||
<null/>
|
||||
</completers>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.segmentrouting.cli.XconnectRemoveCommand"/>
|
||||
<completers>
|
||||
<ref component-id="deviceIdCompleter"/>
|
||||
<ref component-id="placeholderCompleter"/>
|
||||
<null/>
|
||||
</completers>
|
||||
</command>
|
||||
</command-bundle>
|
||||
|
||||
<bean id="nullCompleter" class="org.apache.karaf.shell.console.completer.NullCompleter"/>
|
||||
<bean id="placeholderCompleter" class="org.onosproject.cli.PlaceholderCompleter"/>
|
||||
<bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
|
||||
<bean id="portNumberCompleter" class="org.onosproject.cli.net.PortNumberCompleter"/>
|
||||
<bean id="pseudowireIdCompleter" class="org.onosproject.segmentrouting.cli.PseudowireIdCompleter"/>
|
||||
<bean id="mcastGroupCompleter" class="org.onosproject.mcast.cli.McastGroupCompleter"/>
|
||||
<bean id="connectpointCompleter" class="org.onosproject.cli.net.ConnectPointCompleter"/>
|
||||
|
||||
@ -9,7 +9,7 @@ COMPILE_DEPS = [
|
||||
osgi_jar_with_tests (
|
||||
deps = COMPILE_DEPS,
|
||||
web_context = '/onos/segmentrouting',
|
||||
api_title = 'Segment Routing Rest Server',
|
||||
api_title = 'Segment Routing REST API',
|
||||
api_version = '1.0',
|
||||
api_description = 'REST API for Segment Routing Application',
|
||||
api_package = 'org.onosproject.segmentrouting.web',
|
||||
|
||||
@ -29,12 +29,12 @@
|
||||
|
||||
<url>http://onosproject.org</url>
|
||||
|
||||
<description>Segment Routing REST Server</description>
|
||||
<description>Segment Routing REST API</description>
|
||||
|
||||
<properties>
|
||||
<web.context>/onos/segmentrouting</web.context>
|
||||
<api.version>1.0</api.version>
|
||||
<api.title>Segment Routing Rest Server</api.title>
|
||||
<api.title>Segment Routing REST APIr</api.title>
|
||||
<api.description>
|
||||
REST API for Segment Routing Application
|
||||
</api.description>
|
||||
|
||||
@ -21,11 +21,15 @@ import org.onlab.rest.AbstractWebApplication;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Segment Routing Web application.
|
||||
* Segment Routing REST API.
|
||||
*/
|
||||
public class SegmentRoutingWebApplication extends AbstractWebApplication {
|
||||
@Override
|
||||
public Set<Class<?>> getClasses() {
|
||||
return getClasses(PseudowireWebResource.class, McastWebResource.class);
|
||||
return getClasses(
|
||||
PseudowireWebResource.class,
|
||||
McastWebResource.class,
|
||||
XconnectWebResource.class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2018-present Open Networking Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS 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.web;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.onosproject.rest.AbstractWebResource;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectDesc;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.onlab.util.Tools.readTreeFromStream;
|
||||
|
||||
/**
|
||||
* Query, create and remove Xconnects.
|
||||
*/
|
||||
@Path("xconnect")
|
||||
public class XconnectWebResource extends AbstractWebResource {
|
||||
private static final String XCONNECTS = "xconnects";
|
||||
private static Logger log = LoggerFactory.getLogger(XconnectWebResource.class);
|
||||
|
||||
/**
|
||||
* Gets all Xconnects.
|
||||
*
|
||||
* @return an array of xconnects
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getXconnects() {
|
||||
XconnectService xconnectService = get(XconnectService.class);
|
||||
Set<XconnectDesc> xconnects = xconnectService.getXconnects();
|
||||
|
||||
ObjectNode result = encodeArray(XconnectDesc.class, XCONNECTS, xconnects);
|
||||
return ok(result).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Xconnect.
|
||||
*
|
||||
* @param input JSON stream for xconnect to create
|
||||
* @return 200 OK
|
||||
* @throws IOException Throws IO exception
|
||||
* @onos.rsModel XconnectCreate
|
||||
*/
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response addOrUpdateXconnect(InputStream input) throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode json = readTreeFromStream(mapper, input);
|
||||
XconnectDesc desc = codec(XconnectDesc.class).decode(json, this);
|
||||
|
||||
if (desc.ports().size() != 2) {
|
||||
throw new IllegalArgumentException("Ports should have only two items.");
|
||||
}
|
||||
|
||||
XconnectService xconnectService = get(XconnectService.class);
|
||||
xconnectService.addOrUpdateXconnect(desc.key().deviceId(), desc.key().vlanId(), desc.ports());
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete an existing Xconnect.
|
||||
*
|
||||
* @param input JSON stream for xconnect to remove
|
||||
* @return 204 NO CONTENT
|
||||
* @throws IOException Throws IO exception
|
||||
* @onos.rsModel XconnectDelete
|
||||
*/
|
||||
@DELETE
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response removeXconnect(InputStream input) throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode json = readTreeFromStream(mapper, input);
|
||||
XconnectDesc desc = codec(XconnectDesc.class).decode(json, this);
|
||||
|
||||
XconnectService xconnectService = get(XconnectService.class);
|
||||
xconnectService.removeXonnect(desc.key().deviceId(), desc.key().vlanId());
|
||||
|
||||
return Response.noContent().build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
{
|
||||
"type": "object",
|
||||
"title": "xconnect-creation",
|
||||
"required": [
|
||||
"deviceId",
|
||||
"vlanId",
|
||||
"ports"
|
||||
],
|
||||
"properties": {
|
||||
"deviceId": {
|
||||
"type": "string",
|
||||
"example": "of:0000000000000201",
|
||||
"description": "Device ID"
|
||||
},
|
||||
"vlanId": {
|
||||
"type": "string",
|
||||
"example": "94",
|
||||
"description": "VLAN ID"
|
||||
},
|
||||
"ports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "int8",
|
||||
"description": "Port number"
|
||||
},
|
||||
"example": [1, 2]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "object",
|
||||
"title": "xconnect-deletion",
|
||||
"required": [
|
||||
"deviceId",
|
||||
"vlanId"
|
||||
],
|
||||
"properties": {
|
||||
"deviceId": {
|
||||
"type": "string",
|
||||
"example": "of:0000000000000201",
|
||||
"description": "Device ID"
|
||||
},
|
||||
"vlanId": {
|
||||
"type": "string",
|
||||
"example": "94",
|
||||
"description": "VLAN ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user