Charles Chan
Committed by Gerrit Code Review

CORD-349 Support VLAN cross-connect traffic

Change related to this topic:
- Support VLAN cross-connect traffic
    Utilize ports subjectClass to achieve. For non-xConnect port, set interface VLAN to -1
- Remove VLAN checking since we have multiple VLANs per port
- Hash the L2 interface group key generation to include VLAN as well
- Update the network-cfg.json sample

Other refactoring changes:
- Read next objective stores from srManager directly
- Use constant for flow priority
- CORD-267 Javadoc fix

Change-Id: I4ca8c2d9c8b3633a4a0101c5070d19343f7e5b90
Showing 31 changed files with 675 additions and 234 deletions
......@@ -41,6 +41,10 @@ import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handler of ARP packets that responses or forwards ARP packets that
* are sent to the controller.
*/
public class ArpHandler {
private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
......
......@@ -37,6 +37,10 @@ import java.util.concurrent.locks.ReentrantLock;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Default routing handler that is responsible for route computing and
* routing rule population.
*/
public class DefaultRoutingHandler {
private static Logger log = LoggerFactory
......@@ -512,6 +516,7 @@ public class DefaultRoutingHandler {
*/
public void populatePortAddressingRules(DeviceId deviceId) {
rulePopulator.populateRouterMacVlanFilters(deviceId);
rulePopulator.populateXConnectVlanFilters(deviceId);
rulePopulator.populateRouterIpPunts(deviceId);
}
......
......@@ -20,7 +20,7 @@ import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Tunnel class.
* Default Tunnel class.
*/
public class DefaultTunnel implements Tunnel {
......
......@@ -38,6 +38,10 @@ import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handler of ICMP packets that responses or forwards ICMP packets that
* are sent to the controller.
*/
public class IcmpHandler {
private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
......
......@@ -37,6 +37,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handler of IP packets that forwards IP packets that are sent to the controller,
* except the ICMP packets which are processed by @link{IcmpHandler}.
*/
public class IpHandler {
private static Logger log = LoggerFactory.getLogger(IpHandler.class);
......
......@@ -24,16 +24,24 @@ public interface Policy {
* Enums for policy type.
*/
enum Type {
// Tunnel flow policy type
/**
* Tunnel flow policy type.
*/
TUNNEL_FLOW,
// Load balancing policy type
/**
* Load balancing policy type.
*/
LOADBALANCE,
// policy to avoid specific routers or links
/**
* policy to avoid specific routers or links.
*/
AVOID,
// Access Control policy type
/**
* Access Control policy type.
*/
DENY
}
......
......@@ -48,18 +48,43 @@ public class PolicyHandler {
private FlowObjectiveService flowObjectiveService;
private TunnelHandler tunnelHandler;
private final EventuallyConsistentMap<String, Policy> policyStore;
/**
* Result of policy creation.
*/
public enum Result {
/**
* Success.
*/
SUCCESS,
/**
* The same policy exists already.
*/
POLICY_EXISTS,
/**
* The policy ID exists already.
*/
ID_EXISTS,
/**
* Cannot find associated tunnel.
*/
TUNNEL_NOT_FOUND,
/**
* Policy was not found.
*/
POLICY_NOT_FOUND,
/**
* Policy type {} is not supported yet.
*/
UNSUPPORTED_TYPE
}
/**
* Creates a reference.
* Constructs policy handler.
*
* @param appId segment routing application ID
* @param deviceConfiguration DeviceConfiguration reference
......
......@@ -23,6 +23,7 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.grouphandler.NeighborSet;
......@@ -49,11 +50,15 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Populator of segment routing flow rules.
*/
public class RoutingRulePopulator {
private static final Logger log = LoggerFactory
.getLogger(RoutingRulePopulator.class);
......@@ -63,6 +68,10 @@ public class RoutingRulePopulator {
private DeviceConfiguration config;
private static final int HIGHEST_PRIORITY = 0xffff;
//
private static final int XCONNECT_PRIORITY = 1000;
private static final int DEFAULT_PRIORITY = 100;
private static final int FLOOD_PRIORITY = 5;
private static final long OFPP_MAX = 0xffffff00L;
......@@ -120,6 +129,14 @@ public class RoutingRulePopulator {
rulePopulationCounter.incrementAndGet();
}
/**
* Removes IP rules for host when the host is gone.
*
* @param deviceId device ID of the device that host attaches to
* @param hostIp IP address of the host
* @param hostMac MAC address of the host
* @param outPort port that host attaches to
*/
public void revokeIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
MacAddress hostMac, PortNumber outPort) {
log.debug("Revoke IP table entry for host {} at {}:{}",
......@@ -175,7 +192,7 @@ public class RoutingRulePopulator {
.withSelector(selector)
.nextStep(portNextObjId)
.fromApp(srManager.appId).makePermanent()
.withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
.withPriority(DEFAULT_PRIORITY).withFlag(ForwardingObjective.Flag.SPECIFIC);
}
/**
......@@ -369,7 +386,7 @@ public class RoutingRulePopulator {
for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
.makePermanent()).withSelector(selector)
.withPriority(100))
.withPriority(DEFAULT_PRIORITY))
.withFlag(ForwardingObjective.Flag.SPECIFIC);
srManager.flowObjectiveService.
forward(deviceId,
......@@ -464,7 +481,8 @@ public class RoutingRulePopulator {
FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
fob.withKey(Criteria.matchInPort(port.number()))
.addCondition(Criteria.matchEthDst(deviceMac))
.addCondition(Criteria.matchVlanId(VlanId.NONE));
.addCondition(Criteria.matchVlanId(VlanId.NONE))
.withPriority(DEFAULT_PRIORITY);
// vlan assignment is valid only if this instance is master
if (srManager.mastershipService.isLocalMaster(deviceId)) {
TrafficTreatment tt = DefaultTrafficTreatment.builder()
......@@ -558,7 +576,7 @@ public class RoutingRulePopulator {
fob.withFlag(Flag.SPECIFIC)
.withSelector(sbuilder.build())
.nextStep(nextId)
.withPriority(5)
.withPriority(FLOOD_PRIORITY)
.fromApp(srManager.appId)
.makePermanent();
......@@ -572,6 +590,86 @@ public class RoutingRulePopulator {
});
}
/**
* Creates a filtering objective to permit VLAN cross-connect traffic.
*
* @param deviceId the DPID of the switch
*/
public void populateXConnectVlanFilters(DeviceId deviceId) {
Map<VlanId, List<ConnectPoint>> xConnectsForDevice =
config.getXConnects();
xConnectsForDevice.forEach((vlanId, connectPoints) -> {
// Only proceed the xConnect for given device
for (ConnectPoint connectPoint : connectPoints) {
if (!connectPoint.deviceId().equals(deviceId)) {
return;
}
}
connectPoints.forEach(connectPoint -> {
FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
fob.withKey(Criteria.matchInPort(connectPoint.port()))
.addCondition(Criteria.matchVlanId(vlanId))
.addCondition(Criteria.matchEthDst(MacAddress.NONE))
.withPriority(XCONNECT_PRIORITY);
fob.permit().fromApp(srManager.appId);
srManager.flowObjectiveService
.filter(deviceId, fob.add(new SRObjectiveContext(deviceId,
SRObjectiveContext.ObjectiveType.FILTER)));
});
});
}
/**
* Populates a forwarding objective that points the VLAN cross-connect
* packets to a broadcast group.
*
* @param deviceId switch ID to set the rules
*/
public void populateXConnectBroadcastRule(DeviceId deviceId) {
Map<VlanId, List<ConnectPoint>> xConnects =
config.getXConnects();
xConnects.forEach((vlanId, connectPoints) -> {
// Only proceed the xConnect for given device
for (ConnectPoint connectPoint : connectPoints) {
if (!connectPoint.deviceId().equals(deviceId)) {
return;
}
}
int nextId = srManager.getXConnectNextObjectiveId(deviceId, vlanId);
if (nextId < 0) {
log.error("Cannot install cross-connect broadcast rule in dev:{} " +
"due to missing nextId:{}", deviceId, nextId);
return;
}
/*
* Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
* as the VLAN cross-connect broadcast rules
*/
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
sbuilder.matchVlanId(vlanId);
sbuilder.matchEthDst(MacAddress.NONE);
ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
fob.withFlag(Flag.SPECIFIC)
.withSelector(sbuilder.build())
.nextStep(nextId)
.withPriority(DEFAULT_PRIORITY)
.fromApp(srManager.appId)
.makePermanent();
srManager.flowObjectiveService.forward(
deviceId,
fob.add(new SRObjectiveContext(
deviceId,
SRObjectiveContext.ObjectiveType.FORWARDING)
)
);
});
}
private static class SRObjectiveContext implements ObjectiveContext {
enum ObjectiveType {
......
......@@ -69,7 +69,6 @@ import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkListener;
import org.onosproject.net.link.LinkService;
......@@ -77,8 +76,8 @@ import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey;
import org.onosproject.segmentrouting.grouphandler.XConnectNextObjectiveStoreKey;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.EventuallyConsistentMapBuilder;
import org.onosproject.store.service.StorageService;
......@@ -102,6 +101,9 @@ import java.util.concurrent.TimeUnit;
@Service
@Component(immediate = true)
/**
* Segment routing manager.
*/
public class SegmentRoutingManager implements SegmentRoutingService {
private static Logger log = LoggerFactory
......@@ -111,15 +113,9 @@ public class SegmentRoutingManager implements SegmentRoutingService {
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......@@ -157,20 +153,31 @@ public class SegmentRoutingManager implements SegmentRoutingService {
@SuppressWarnings("rawtypes")
private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
private Map<DeviceId, DefaultGroupHandler> groupHandlerMap =
new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
// Per device next objective ID store with (device id + neighbor set) as key
private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
new ConcurrentHashMap<>();
/**
* Per device next objective ID store with (device id + neighbor set) as key.
*/
public EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
nsNextObjStore = null;
// Per device next objective ID store with (device id + subnet) as key
private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
/**
* Per device next objective ID store with (device id + subnet) as key.
*/
public EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
subnetNextObjStore = null;
// Per device next objective ID store with (device id + port) as key
private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
/**
* Per device next objective ID store with (device id + port) as key.
*/
public EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
/**
* Per cross-connect objective ID store with VLAN ID as key.
*/
public EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
xConnectNextObjStore = null;
// Per device, per-subnet assigned-vlans store, with (device id + subnet
// IPv4 prefix) as key
private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId>
subnetVidStore = null;
subnetVidStore = null;
private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
private EventuallyConsistentMap<String, Policy> policyStore = null;
......@@ -204,7 +211,13 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private KryoNamespace.Builder kryoBuilder = null;
/**
* The starting value of per-subnet VLAN ID assignment.
*/
private static final short ASSIGNED_VLAN_START = 4093;
/**
* The default VLAN ID assigned to the interfaces without subnet config.
*/
public static final short ASSIGNED_VLAN_NO_SUBNET = 4094;
@Activate
......@@ -262,6 +275,15 @@ public class SegmentRoutingManager implements SegmentRoutingService {
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
log.debug("Creating EC map xconnectnextobjectivestore");
EventuallyConsistentMapBuilder<XConnectNextObjectiveStoreKey, Integer>
xConnectNextObjStoreBuilder = storageService.eventuallyConsistentMapBuilder();
xConnectNextObjStore = xConnectNextObjStoreBuilder
.withName("xconnectnextobjectivestore")
.withSerializer(kryoBuilder)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
storageService.eventuallyConsistentMapBuilder();
tunnelStore = tunnelMapBuilder
......@@ -394,9 +416,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
* Vlan id 4094 expected to be used for all ports that are not assigned subnets.
* Vlan id 4095 is reserved and unused. Only a single vlan id is assigned
* per subnet.
* XXX This method should avoid any vlans configured on the ports, but
* currently the app works only on untagged packets and as a result
* ignores any vlan configuration.
*
* @param deviceId switch dpid
* @param subnet IPv4 prefix for which assigned vlan is desired
......@@ -404,6 +423,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
* null if no vlan assignment was found and this instance is not
* the master for the device.
*/
// TODO: We should avoid assigning VLAN IDs that are used by VLAN cross-connection.
public VlanId getSubnetAssignedVlanId(DeviceId deviceId, Ip4Prefix subnet) {
VlanId assignedVid = subnetVidStore.get(new SubnetAssignedVidStoreKey(
deviceId, subnet));
......@@ -508,7 +528,26 @@ public class SegmentRoutingManager implements SegmentRoutingService {
if (ghdlr != null) {
return ghdlr.getPortNextObjectiveId(portNum, treatment, meta);
} else {
log.warn("getPortNextObjectiveId query - groupHandler for device {}"
log.warn("getPortNextObjectiveId query - groupHandler for device {}"
+ " not found", deviceId);
return -1;
}
}
/**
* Returns the next objective ID of type broadcast associated with the VLAN
* cross-connection.
*
* @param deviceId Device ID for the cross-connection
* @param vlanId VLAN ID for the cross-connection
* @return next objective ID or -1 if it was not found
*/
public int getXConnectNextObjectiveId(DeviceId deviceId, VlanId vlanId) {
DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
if (ghdlr != null) {
return ghdlr.getXConnectNextObjectiveId(vlanId);
} else {
log.warn("getPortNextObjectiveId query - groupHandler for device {}"
+ " not found", deviceId);
return -1;
}
......@@ -707,9 +746,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
deviceConfiguration,
linkService,
flowObjectiveService,
nsNextObjStore,
subnetNextObjStore,
portNextObjStore,
this);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting processDeviceAdded.");
......@@ -727,6 +763,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
groupHandler.createGroupsForXConnect(device.id());
routingRulePopulator.populateXConnectBroadcastRule(device.id());
}
}
......@@ -742,10 +780,18 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private class InternalConfigListener implements NetworkConfigListener {
SegmentRoutingManager segmentRoutingManager;
/**
* Constructs the internal network config listener.
*
* @param srMgr segment routing manager
*/
public InternalConfigListener(SegmentRoutingManager srMgr) {
this.segmentRoutingManager = srMgr;
}
/**
* Reads network config and initializes related data structure accordingly.
*/
public void configureNetwork() {
deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
......@@ -777,9 +823,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
deviceConfiguration,
linkService,
flowObjectiveService,
nsNextObjStore,
subnetNextObjStore,
portNextObjStore,
segmentRoutingManager);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting configureNetwork.");
......@@ -798,6 +841,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
groupHandler.createGroupsForXConnect(device.id());
routingRulePopulator.populateXConnectBroadcastRule(device.id());
}
}
......
......@@ -6,13 +6,18 @@ import org.onlab.packet.Ip4Prefix;
import org.onosproject.net.DeviceId;
/**
* Class definition for key used to map per device subnets to assigned Vlan ids.
*
* Key of assigned VLAN ID store.
*/
public class SubnetAssignedVidStoreKey {
private final DeviceId deviceId;
private final Ip4Prefix subnet;
/**
* Constructs the key of per subnet VLAN ID store.
*
* @param deviceId device ID of the VLAN cross-connection
* @param subnet subnet information
*/
public SubnetAssignedVidStoreKey(DeviceId deviceId, Ip4Prefix subnet) {
this.deviceId = deviceId;
this.subnet = subnet;
......
......@@ -43,16 +43,54 @@ public class TunnelHandler {
private Map<DeviceId, DefaultGroupHandler> groupHandlerMap;
private LinkService linkService;
/**
* Result of tunnel creation or removal.
*/
public enum Result {
/**
* Success.
*/
SUCCESS,
/**
* More than one router needs to specified to created a tunnel.
*/
WRONG_PATH,
/**
* The same tunnel exists already.
*/
TUNNEL_EXISTS,
/**
* The same tunnel ID exists already.
*/
ID_EXISTS,
/**
* Tunnel not found.
*/
TUNNEL_NOT_FOUND,
/**
* Cannot remove the tunnel used by a policy.
*/
TUNNEL_IN_USE,
/**
* Failed to create/remove groups for the tunnel.
*/
INTERNAL_ERROR
}
/**
* Constructs tunnel handler.
*
* @param linkService link service
* @param deviceConfiguration device configuration
* @param groupHandlerMap group handler map
* @param tunnelStore tunnel store
*/
public TunnelHandler(LinkService linkService,
DeviceConfiguration deviceConfiguration,
Map<DeviceId, DefaultGroupHandler> groupHandlerMap,
......
......@@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableSet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.incubator.net.config.basics.ConfigException;
import org.onosproject.incubator.net.config.basics.InterfaceConfig;
import org.onosproject.incubator.net.intf.Interface;
......@@ -33,6 +34,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -48,8 +50,8 @@ public class DeviceConfiguration implements DeviceProperties {
private static final Logger log = LoggerFactory
.getLogger(DeviceConfiguration.class);
private final List<Integer> allSegmentIds = new ArrayList<>();
private final ConcurrentHashMap<DeviceId, SegmentRouterInfo> deviceConfigMap
= new ConcurrentHashMap<>();
private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
private final Map<VlanId, List<ConnectPoint>> xConnects = new ConcurrentHashMap<>();
private class SegmentRouterInfo {
int nodeSid;
......@@ -62,14 +64,14 @@ public class DeviceConfiguration implements DeviceProperties {
Map<Integer, Set<Integer>> adjacencySids;
public SegmentRouterInfo() {
this.gatewayIps = new HashMap<>();
this.subnets = new HashMap<>();
gatewayIps = new HashMap<>();
subnets = new HashMap<>();
}
}
/**
* Constructor. Reads all the configuration for all devices of type
* Segment Router and organizes into various maps for easier access.
* Constructs device configuration for all Segment Router devices,
* organizing the data into various maps for easier access.
*
* @param cfgService config service
*/
......@@ -88,8 +90,8 @@ public class DeviceConfiguration implements DeviceProperties {
info.isEdge = config.isEdgeRouter();
info.adjacencySids = config.adjacencySids();
this.deviceConfigMap.put(info.deviceId, info);
this.allSegmentIds.add(info.nodeSid);
deviceConfigMap.put(info.deviceId, info);
allSegmentIds.add(info.nodeSid);
});
// Read gatewayIps and subnets from port subject.
......@@ -106,17 +108,42 @@ public class DeviceConfiguration implements DeviceProperties {
return;
}
networkInterfaces.forEach(networkInterface -> {
DeviceId dpid = networkInterface.connectPoint().deviceId();
PortNumber port = networkInterface.connectPoint().port();
SegmentRouterInfo info = this.deviceConfigMap.get(dpid);
VlanId vlanId = networkInterface.vlan();
ConnectPoint connectPoint = networkInterface.connectPoint();
DeviceId dpid = connectPoint.deviceId();
PortNumber port = connectPoint.port();
SegmentRouterInfo info = deviceConfigMap.get(dpid);
// skip if there is no corresponding device for this ConenctPoint
if (info != null) {
// Extract subnet information
Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
interfaceAddresses.forEach(interfaceAddress -> {
info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
});
// Extract VLAN cross-connect information
// Do not setup cross-connect if VLAN is NONE
if (vlanId.equals(VlanId.NONE)) {
return;
}
List<ConnectPoint> connectPoints = xConnects.get(vlanId);
if (connectPoints != null) {
if (connectPoints.size() != 1) {
log.warn("Cross-connect should only have two endpoints. Aborting.");
return;
}
if (!connectPoints.get(0).deviceId().equals(connectPoint.deviceId())) {
log.warn("Cross-connect endpoints must be on the same switch. Aborting.");
return;
}
connectPoints.add(connectPoint);
} else {
connectPoints = new LinkedList<>();
connectPoints.add(connectPoint);
xConnects.put(vlanId, connectPoints);
}
}
});
......@@ -235,6 +262,11 @@ public class DeviceConfiguration implements DeviceProperties {
return subnetPortMap;
}
@Override
public Map<VlanId, List<ConnectPoint>> getXConnects() {
return xConnects;
}
/**
* Returns the device identifier or data plane identifier (dpid)
* of a segment router given its segment id.
......
......@@ -21,6 +21,8 @@ import java.util.Map;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
......@@ -93,4 +95,11 @@ public interface DeviceProperties {
* @return a map that contains all subnet-to-ports mapping of given device
*/
Map<Ip4Prefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId);
/**
* Returns the VLAN cross-connect configuration.
*
* @return A map of that maps VLAN ID to a list of cross-connect endpoints
*/
Map<VlanId, List<ConnectPoint>> getXConnects();
}
......
......@@ -35,24 +35,24 @@ import java.util.Set;
* Configuration object for Segment Routing Application.
*/
public class SegmentRoutingConfig extends Config<DeviceId> {
public static final String NAME = "name";
public static final String IP = "routerIp";
public static final String MAC = "routerMac";
public static final String SID = "nodeSid";
public static final String EDGE = "isEdgeRouter";
public static final String ADJSIDS = "adjacencySids";
public static final String ADJSID = "adjSid";
public static final String PORTS = "ports";
private static final String NAME = "name";
private static final String IP = "routerIp";
private static final String MAC = "routerMac";
private static final String SID = "nodeSid";
private static final String EDGE = "isEdgeRouter";
private static final String ADJSIDS = "adjacencySids";
private static final String ADJSID = "adjSid";
private static final String PORTS = "ports";
@Override
public boolean isValid() {
return hasOnlyFields(NAME, IP, MAC, SID, EDGE, ADJSIDS, ADJSID, PORTS) &&
this.name() != null &&
this.routerIp() != null &&
this.routerMac() != null &&
this.nodeSid() != -1 &&
this.isEdgeRouter() != null &&
this.adjacencySids() != null;
name() != null &&
routerIp() != null &&
routerMac() != null &&
nodeSid() != -1 &&
isEdgeRouter() != null &&
adjacencySids() != null;
}
/**
......
......@@ -26,7 +26,6 @@ import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.link.LinkService;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceProperties;
import org.onosproject.store.service.EventuallyConsistentMap;
/**
* Default ECMP group handler creation module for an edge device.
......@@ -47,22 +46,13 @@ import org.onosproject.store.service.EventuallyConsistentMap;
* 8) what about ecmp no label case
*/
public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
// TODO Access stores through srManager
protected DefaultEdgeGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<
NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
super(deviceId, appId, config, linkService, flowObjService, srManager);
}
@Override
......
......@@ -26,7 +26,6 @@ import org.onosproject.net.link.LinkService;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceProperties;
import org.onosproject.store.service.EventuallyConsistentMap;
/**
* Default ECMP group handler creation module for a transit device.
......@@ -41,22 +40,13 @@ import org.onosproject.store.service.EventuallyConsistentMap;
* 2) all ports to D3 + with no label push,
*/
public class DefaultTransitGroupHandler extends DefaultGroupHandler {
// TODO Access stores through srManager
protected DefaultTransitGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<
NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
super(deviceId, appId, config, linkService, flowObjService, srManager);
}
@Override
......
......@@ -21,22 +21,38 @@ import java.util.Objects;
import org.onosproject.net.DeviceId;
/**
* Class definition of Key for Neighborset to NextObjective store.
* Key of Neighborset next objective store.
*/
public class NeighborSetNextObjectiveStoreKey {
private final DeviceId deviceId;
private final NeighborSet ns;
/**
* Constructs the key of neighbor set next objective store.
*
* @param deviceId device ID
* @param ns neighbor set
*/
public NeighborSetNextObjectiveStoreKey(DeviceId deviceId,
NeighborSet ns) {
this.deviceId = deviceId;
this.ns = ns;
}
/**
* Returns the device ID in the key.
*
* @return device ID
*/
public DeviceId deviceId() {
return this.deviceId;
}
/**
* Returns the neighbor set in the key.
*
* @return neighbor set
*/
public NeighborSet neighborSet() {
return this.ns;
}
......
......@@ -31,7 +31,6 @@ import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceProperties;
import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficTreatment;
......@@ -51,33 +50,31 @@ public class PolicyGroupHandler extends DefaultGroupHandler {
private HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier> dependentGroups = new HashMap<>();
/**
* Policy group handler constructor.
* Constructs policy group handler.
*
* @param deviceId device identifier
* @param appId application identifier
* @param config interface to retrieve the device properties
* @param linkService link service object
* @param flowObjService flow objective service object
* @param nsNextObjStore NeighborSet next objective store map
* @param subnetNextObjStore subnet next objective store map
* @param srManager segment routing manager
*/
// TODO Access stores through srManager
public PolicyGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
super(deviceId, appId, config, linkService, flowObjService, srManager);
}
/**
* Creates policy group chain.
*
* @param id unique identifier associated with the policy group
* @param params a list of policy group params
* @return policy group identifier
*/
public PolicyGroupIdentifier createPolicyGroupChain(String id,
List<PolicyGroupParams> params) {
List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
......@@ -222,69 +219,18 @@ public class PolicyGroupHandler extends DefaultGroupHandler {
}
//TODO: Use nextObjective APIs to handle the group chains
/*@Override
protected void handleGroupEvent(GroupEvent event) {
if (event.type() == GroupEvent.Type.GROUP_ADDED) {
if (dependentGroups.get(event.subject().appCookie()) != null) {
PolicyGroupIdentifier dependentGroupKey = dependentGroups.get(event.subject().appCookie());
dependentGroups.remove(event.subject().appCookie());
boolean fullyResolved = true;
for (GroupBucketIdentifier bucketId:
dependentGroupKey.bucketIds()) {
if (bucketId.type() != BucketOutputType.GROUP) {
continue;
}
if (dependentGroups.containsKey(bucketId.outGroup())) {
fullyResolved = false;
break;
}
}
if (fullyResolved) {
List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
for (GroupBucketIdentifier bucketId:
dependentGroupKey.bucketIds()) {
TrafficTreatment.Builder tBuilder =
DefaultTrafficTreatment.builder();
if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
tBuilder.pushMpls()
.setMpls(MplsLabel.
mplsLabel(bucketId.label()));
}
//TODO: BoS
if (bucketId.type() == BucketOutputType.PORT) {
DeviceId neighbor = portDeviceMap.
get(bucketId.outPort());
tBuilder.setOutput(bucketId.outPort())
.setEthDst(deviceConfig.
getDeviceMac(neighbor))
.setEthSrc(nodeMacAddr);
} else {
if (groupService.
getGroup(deviceId,
getGroupKey(bucketId.
outGroup())) == null) {
throw new IllegalStateException();
}
GroupId indirectGroupId = groupService.
getGroup(deviceId,
getGroupKey(bucketId.
outGroup())).id();
tBuilder.group(indirectGroupId);
}
outBuckets.add(DefaultGroupBucket.
createSelectGroupBucket(tBuilder.build()));
}
GroupDescription desc = new
DefaultGroupDescription(deviceId,
GroupDescription.Type.SELECT,
new GroupBuckets(outBuckets));
groupService.addGroup(desc);
}
}
}
}*/
/*
@Override
protected void handleGroupEvent(GroupEvent event) {}
*/
/**
* Generates policy group key.
*
* @param id unique identifier associated with the policy group
* @param params a list of policy group params
* @return policy group identifier
*/
public PolicyGroupIdentifier generatePolicyGroupKey(String id,
List<PolicyGroupParams> params) {
List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
......@@ -354,6 +300,11 @@ public class PolicyGroupHandler extends DefaultGroupHandler {
return innermostGroupkey;
}
/**
* Removes policy group chain.
*
* @param key policy group identifier
*/
public void removeGroupChain(PolicyGroupIdentifier key) {
checkArgument(key != null);
List<PolicyGroupIdentifier> groupsToBeDeleted = new ArrayList<>();
......
......@@ -28,7 +28,7 @@ public class PolicyGroupIdentifier {
private List<GroupBucketIdentifier> bucketIds;
/**
* Constructor.
* Constructs policy group identifier.
*
* @param id unique identifier associated with the policy group
* @param input policy group params associated with this group
......
......@@ -7,15 +7,23 @@ import org.onosproject.net.flow.TrafficTreatment;
import java.util.Objects;
/**
* Class definition of Key for Device/Port to NextObjective store. Since there
* can be multiple next objectives to the same physical port, we differentiate
* between them by including the treatment in the key.
* Key of Device/Port to NextObjective store.
*
* Since there can be multiple next objectives to the same physical port,
* we differentiate between them by including the treatment in the key.
*/
public class PortNextObjectiveStoreKey {
private final DeviceId deviceId;
private final PortNumber portNum;
private final TrafficTreatment treatment;
/**
* Constructs the key of port next objective store.
*
* @param deviceId device ID
* @param portNum port number
* @param treatment treatment that will be applied to the interface
*/
public PortNextObjectiveStoreKey(DeviceId deviceId, PortNumber portNum,
TrafficTreatment treatment) {
this.deviceId = deviceId;
......
......@@ -22,12 +22,18 @@ import org.onosproject.net.DeviceId;
import java.util.Objects;
/**
* Class definition of Key for Subnet to NextObjective store.
* Key of Subnet to NextObjective store.
*/
public class SubnetNextObjectiveStoreKey {
private final DeviceId deviceId;
private final IpPrefix prefix;
/**
* Constructs the key of subnet next objective store.
*
* @param deviceId device ID
* @param prefix subnet information
*/
public SubnetNextObjectiveStoreKey(DeviceId deviceId,
IpPrefix prefix) {
this.deviceId = deviceId;
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.segmentrouting.grouphandler;
import org.onlab.packet.VlanId;
import org.onosproject.net.DeviceId;
import java.util.Objects;
/**
* Key of VLAN cross-connect next objective store.
*/
public class XConnectNextObjectiveStoreKey {
private final DeviceId deviceId;
private final VlanId vlanId;
/**
* Constructs the key of cross-connect next objective store.
*
* @param deviceId device ID of the VLAN cross-connection
* @param vlanId VLAN ID of the VLAN cross-connection
*/
public XConnectNextObjectiveStoreKey(DeviceId deviceId, VlanId vlanId) {
this.deviceId = deviceId;
this.vlanId = vlanId;
}
/**
* Returns the device ID of this key.
*
* @return device ID
*/
public DeviceId deviceId() {
return this.deviceId;
}
/**
* Returns the VLAN ID of this key.
*
* @return VLAN ID
*/
public VlanId vlanId() {
return this.vlanId;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof XConnectNextObjectiveStoreKey)) {
return false;
}
XConnectNextObjectiveStoreKey that =
(XConnectNextObjectiveStoreKey) o;
return (Objects.equals(this.deviceId, that.deviceId) &&
Objects.equals(this.vlanId, that.vlanId));
}
// The list of neighbor ids and label are used for comparison.
@Override
public int hashCode() {
return Objects.hash(deviceId, vlanId);
}
@Override
public String toString() {
return "Device: " + deviceId + " VlanId: " + vlanId;
}
}
......@@ -21,6 +21,9 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.segmentrouting.Policy;
import org.onosproject.segmentrouting.TunnelPolicy;
/**
* Codec of Policy class.
*/
public final class PolicyCodec extends JsonCodec<Policy> {
// JSON field names
......
......@@ -26,6 +26,9 @@ import org.onosproject.segmentrouting.Tunnel;
import java.util.ArrayList;
import java.util.List;
/**
* Codec of Tunnel class.
*/
public final class TunnelCodec extends JsonCodec<Tunnel> {
// JSON field names
......
......@@ -305,29 +305,35 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
VlanId assignedVlan = null;
if (vidCriterion != null && vidCriterion.vlanId() == VlanId.NONE) {
// untagged packets are assigned vlans in OF-DPA
if (filt.meta() == null) {
log.error("Missing metadata in filtering objective required "
+ "for vlan assignment in dev {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
for (Instruction i : filt.meta().allInstructions()) {
if (i instanceof ModVlanIdInstruction) {
assignedVlan = ((ModVlanIdInstruction) i).vlanId();
// For VLAN cross-connect packets, use the configured VLAN
if (vidCriterion != null) {
if (vidCriterion.vlanId() != VlanId.NONE) {
assignedVlan = vidCriterion.vlanId();
// For untagged packets, assign a VLAN ID
} else {
if (filt.meta() == null) {
log.error("Missing metadata in filtering objective required " +
"for vlan assignment in dev {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
for (Instruction i : filt.meta().allInstructions()) {
if (i instanceof ModVlanIdInstruction) {
assignedVlan = ((ModVlanIdInstruction) i).vlanId();
}
}
if (assignedVlan == null) {
log.error("Driver requires an assigned vlan-id to tag incoming "
+ "untagged packets. Not processing vlan filters on "
+ "device {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
}
if (assignedVlan == null) {
log.error("Driver requires an assigned vlan-id to tag incoming "
+ "untagged packets. Not processing vlan filters on "
+ "device {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
}
if (ethCriterion == null) {
if (ethCriterion == null || ethCriterion.mac().equals(MacAddress.NONE)) {
log.debug("filtering objective missing dstMac, cannot program TMAC table");
} else {
for (FlowRule tmacRule : processEthDstFilter(portCriterion, ethCriterion,
......@@ -340,8 +346,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
if (ethCriterion == null || vidCriterion == null) {
log.debug("filtering objective missing dstMac or vlan, cannot program"
+ "Vlan Table");
log.debug("filtering objective missing dstMac or VLAN, "
+ "cannot program VLAN Table");
} else {
for (FlowRule vlanRule : processVlanIdFilter(portCriterion, vidCriterion,
assignedVlan,
......
......@@ -837,7 +837,6 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
if (vlanIdCriterion.vlanId() != VlanId.NONE) {
selector.matchVlanId(vlanIdCriterion.vlanId());
selector.matchInPort(p.port());
treatment.deferred().popVlan();
} else {
selector.matchInPort(p.port());
treatment.immediate().pushVlan().setVlanId(assignedVlan);
......@@ -887,25 +886,31 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
VlanId assignedVlan = null;
if (vlanIdCriterion != null && vlanIdCriterion.vlanId() == VlanId.NONE) {
// Assign a VLAN ID to untagged packets
if (filt.meta() == null) {
log.error("Missing metadata in filtering objective required "
+ "for vlan assignment in dev {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
for (Instruction i : filt.meta().allInstructions()) {
if (i instanceof ModVlanIdInstruction) {
assignedVlan = ((ModVlanIdInstruction) i).vlanId();
if (vlanIdCriterion != null) {
// For VLAN cross-connect packets, use the configured VLAN
if (vlanIdCriterion.vlanId() != VlanId.NONE) {
assignedVlan = vlanIdCriterion.vlanId();
// For untagged packets, assign a VLAN ID
} else {
if (filt.meta() == null) {
log.error("Missing metadata in filtering objective required " +
"for vlan assignment in dev {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
for (Instruction i : filt.meta().allInstructions()) {
if (i instanceof ModVlanIdInstruction) {
assignedVlan = ((ModVlanIdInstruction) i).vlanId();
}
}
if (assignedVlan == null) {
log.error("Driver requires an assigned vlan-id to tag incoming "
+ "untagged packets. Not processing vlan filters on "
+ "device {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
}
if (assignedVlan == null) {
log.error("Driver requires an assigned vlan-id to tag incoming "
+ "untagged packets. Not processing vlan filters on "
+ "device {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
}
......@@ -923,9 +928,9 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
}
if (ethCriterion == null || vlanIdCriterion == null) {
log.debug("filtering objective missing dstMac or vlan, cannot program"
+ "Vlan Table");
if (vlanIdCriterion == null) {
log.debug("filtering objective missing VLAN ID criterion, "
+ "cannot program VLAN Table");
} else {
for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
filt,
......
{
"ports" : {
"of:0000000000000001/3" : {
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"vlan" : "-1"
},
{
"vlan" : "100"
}
]
},
"of:0000000000000001/4" : {
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"vlan" : "-1"
}
]
},
"of:0000000000000001/5" : {
"interfaces" : [
{
"vlan" : "100"
}
]
},
"of:0000000000000002/3" : {
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"vlan" : "-1"
}
]
},
"of:0000000000000002/4" : {
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"vlan" : "-1"
}
]
}
},
"devices" : {
"of:0000000000000001" : {
"segmentrouting" : {
"name" : "Leaf-R1",
"nodeSid" : 101,
"routerIp" : "10.0.1.254",
"routerMac" : "00:00:00:00:01:80",
"isEdgeRouter" : true,
"adjacencySids" : []
}
},
"of:0000000000000002" : {
"segmentrouting" : {
"name" : "Leaf-R2",
"nodeSid" : 102,
"routerIp" : "10.0.2.254",
"routerMac" : "00:00:00:00:02:80",
"isEdgeRouter" : true,
"adjacencySids" : []
}
},
"of:0000000000000191" : {
"segmentrouting" : {
"name" : "Spine-R1",
"nodeSid" : 103,
"routerIp" : "192.168.0.11",
"routerMac" : "00:00:01:00:11:80",
"isEdgeRouter" : false,
"adjacencySids" : []
}
},
"of:0000000000000192" : {
"segmentrouting" : {
"name" : "Spine-R2",
"nodeSid" : 104,
"routerIp" : "192.168.0.22",
"routerMac" : "00:00:01:00:22:80",
"isEdgeRouter" : false,
"adjacencySids" : []
}
}
},
"hosts" : {
"00:00:00:00:00:01/4093" : {
"basic": {
"ips": ["10.0.1.1"],
"location": "of:0000000000000001/3"
}
},
"00:00:00:00:00:02/4093" : {
"basic": {
"ips": ["10.0.1.2"],
"location": "of:0000000000000001/4"
}
},
"00:00:00:00:00:03/4093" : {
"basic": {
"ips": ["10.0.2.1"],
"location": "of:0000000000000002/3"
}
},
"00:00:00:00:00:04/4093" : {
"basic": {
"ips": ["10.0.2.2"],
"location": "of:0000000000000002/4"
}
}
}
}
......@@ -4,8 +4,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -13,8 +12,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -22,8 +20,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -31,8 +28,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
}
......
......@@ -4,8 +4,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -13,8 +12,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -22,8 +20,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -31,8 +28,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -40,8 +36,7 @@
"interfaces" : [
{
"ips" : [ "10.0.3.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -49,8 +44,7 @@
"interfaces" : [
{
"ips" : [ "10.0.3.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -58,8 +52,7 @@
"interfaces" : [
{
"ips" : [ "10.0.4.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -67,8 +60,7 @@
"interfaces" : [
{
"ips" : [ "10.0.4.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
}
......