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:
Charles Chan 2018-06-19 20:31:57 -07:00 committed by Charles Chan
parent f76de30781
commit c7b3c451c7
22 changed files with 1373 additions and 33 deletions

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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
}

View File

@ -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;

View File

@ -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"/>

View File

@ -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',

View File

@ -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>

View File

@ -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
);
}
}

View File

@ -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();
}
}

View File

@ -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]
}
}
}

View File

@ -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"
}
}
}