Charles Chan
Committed by Ray Milkey

CORD-339 Network config host provider

* Implement new application org.onosproject.netcfghost
* Implement BasicHostConfig to include IP and location information
    - Update network-cfg.json to add host config example
    - Add network-cfg-2x2-leaf-spine.json for 2x2 leaf-spine network in SegmentRouting
* Update Segment Rounting
    - Punt ARP packets
      (which is done by HostLocationProvider previously)
    - Check existing hosts when device connected or configured

Change-Id: I03986ddc8203d740b5bf26903e3dbf866d4d4600
......@@ -513,6 +513,7 @@ public class DefaultRoutingHandler {
public void populatePortAddressingRules(DeviceId deviceId) {
rulePopulator.populateRouterMacVlanFilters(deviceId);
rulePopulator.populateRouterIpPunts(deviceId);
rulePopulator.populateArpPunts(deviceId);
}
/**
......
......@@ -511,6 +511,39 @@ public class RoutingRulePopulator {
}
/**
* Creates a forwarding objective to punt all IP packets, destined to the
* router's port IP addresses, to the controller. Note that the input
* port should not be matched on, as these packets can come from any input.
* Furthermore, these are applied only by the master instance.
*
* @param deviceId the switch dpid for the router
*/
public void populateArpPunts(DeviceId deviceId) {
if (!srManager.mastershipService.isLocalMaster(deviceId)) {
log.debug("Not installing port-IP punts - not the master for dev:{} ",
deviceId);
return;
}
ForwardingObjective.Builder puntArp = DefaultForwardingObjective.builder();
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sbuilder.matchEthType(Ethernet.TYPE_ARP);
tbuilder.setOutput(PortNumber.CONTROLLER);
puntArp.withSelector(sbuilder.build());
puntArp.withTreatment(tbuilder.build());
puntArp.withFlag(Flag.VERSATILE)
.withPriority(HIGHEST_PRIORITY)
.makePermanent()
.fromApp(srManager.appId);
log.debug("Installing forwarding objective to punt ARPs");
srManager.flowObjectiveService.
forward(deviceId,
puntArp.add(new SRObjectiveContext(deviceId,
SRObjectiveContext.ObjectiveType.FORWARDING)));
}
/**
* Populates a forwarding objective to send packets that miss other high
* priority Bridging Table entries to a group that contains all ports of
* its subnet.
......
......@@ -185,7 +185,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
};
private final HostListener hostListener = new InternalHostListener();
private final InternalHostListener hostListener = new InternalHostListener();
private Object threadSchedulerLock = new Object();
private static int numOfEventsQueued = 0;
......@@ -658,6 +658,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
// port addressing rules to the driver as well irrespective of whether
// this instance is the master or not.
defaultRoutingHandler.populatePortAddressingRules(device.id());
hostListener.readInitialHosts();
}
if (mastershipService.isLocalMaster(device.id())) {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
......@@ -725,6 +726,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
// port addressing rules to the driver as well, irrespective of whether
// this instance is the master or not.
defaultRoutingHandler.populatePortAddressingRules(device.id());
hostListener.readInitialHosts();
}
if (mastershipService.isLocalMaster(device.id())) {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
......@@ -751,7 +753,34 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
}
// TODO Move bridging table population to a separate class
private class InternalHostListener implements HostListener {
private void readInitialHosts() {
hostService.getHosts().forEach(host -> {
MacAddress mac = host.mac();
VlanId vlanId = host.vlan();
DeviceId deviceId = host.location().deviceId();
PortNumber port = host.location().port();
Set<IpAddress> ips = host.ipAddresses();
log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
// Populate bridging table entry
ForwardingObjective.Builder fob =
getForwardingObjectiveBuilder(mac, vlanId, port);
flowObjectiveService.forward(deviceId, fob.add(
new BridgingTableObjectiveContext(mac, vlanId)
));
// Populate IP table entry
ips.forEach(ip -> {
if (ip.isIp4()) {
routingRulePopulator.populateIpRuleForHost(
deviceId, ip.getIp4Address(), mac, port);
}
});
});
}
private ForwardingObjective.Builder getForwardingObjectiveBuilder(
MacAddress mac, VlanId vlanId, PortNumber port) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
......@@ -780,7 +809,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
Set<IpAddress> ips = event.subject().ipAddresses();
log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
// TODO Move bridging table population to a separate class
// Populate bridging table entry
ForwardingObjective.Builder fob =
getForwardingObjectiveBuilder(mac, vlanId, port);
......
......@@ -15,13 +15,83 @@
*/
package org.onosproject.net.config.basics;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.onlab.packet.IpAddress;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
import java.util.HashSet;
import java.util.Set;
/**
* Basic configuration for network end-station hosts.
*/
public class BasicHostConfig extends BasicElementConfig<HostId> {
private static final String IPS = "ips";
private static final String LOCATION = "location";
// TODO: determine what aspects of configuration to add for hosts
@Override
public boolean isValid() {
return hasOnlyFields(IPS, LOCATION) &&
this.location() != null &&
this.ipAddresses() != null;
}
/**
* Gets location of the host.
*
* @return location of the host. Or null if not specified with correct format.
*/
public ConnectPoint location() {
String location = get(LOCATION, null);
if (location != null) {
try {
return ConnectPoint.deviceConnectPoint(location);
} catch (Exception e) {
return null;
}
}
return null;
}
/**
* Sets the location of the host.
*
* @param location location of the host.
* @return the config of the host.
*/
public BasicHostConfig setLocation(String location) {
return (BasicHostConfig) setOrClear(LOCATION, location);
}
/**
* Gets IP addresses of the host.
*
* @return IP addresses of the host. Or null if not specified with correct format.
*/
public Set<IpAddress> ipAddresses() {
HashSet<IpAddress> ipAddresses = new HashSet<>();
if (object.has(IPS)) {
ArrayNode ipNodes = (ArrayNode) object.path(IPS);
try {
ipNodes.forEach(ipNode -> {
ipAddresses.add(IpAddress.valueOf(ipNode.asText()));
});
return ipAddresses;
} catch (Exception e) {
return null;
}
}
return null;
}
/**
* Sets the IP addresses of the host.
*
* @param ipAddresses IP addresses of the host.
* @return the config of the host.
*/
public BasicHostConfig setIps(Set<IpAddress> ipAddresses) {
return (BasicHostConfig) setOrClear(IPS, ipAddresses);
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-netcfg-host-provider</artifactId>
<packaging>bundle</packaging>
<description>
Host provider that uses network config service to discover hosts.
</description>
<url>http://onosproject.org</url>
<properties>
<onos.version>1.4.0-SNAPSHOT</onos.version>
<onos.app.name>org.onosproject.netcfghostprovider</onos.app.name>
<onos.app.origin>ON.Lab</onos.app.origin>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${onos.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
<version>${onos.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${onos.version}</version>
<scope>test</scope>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
/*
* Copyright 2014-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.provider.netcfghost;
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.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.basics.BasicHostConfig;
import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostProvider;
import org.onosproject.net.host.HostProviderRegistry;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
/**
* Host provider that uses network config service to discover hosts.
*/
@Component(immediate = true)
public class NetworkConfigHostProvider extends AbstractProvider implements HostProvider {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry networkConfigRegistry;
private static final String APP_NAME = "org.onosproject.provider.netcfghost";
private ApplicationId appId;
protected HostProviderService providerService;
private final Logger log = LoggerFactory.getLogger(getClass());
private final InternalNetworkConfigListener networkConfigListener =
new InternalNetworkConfigListener();
/**
* Creates an network config host location provider.
*/
public NetworkConfigHostProvider() {
super(new ProviderId("host", APP_NAME));
}
@Activate
protected void activate() {
appId = coreService.registerApplication(APP_NAME);
providerService = providerRegistry.register(this);
networkConfigRegistry.addListener(networkConfigListener);
readInitialConfig();
log.info("Started");
}
@Deactivate
protected void deactivate() {
networkConfigRegistry.removeListener(networkConfigListener);
providerRegistry.unregister(this);
providerService = null;
log.info("Stopped");
}
@Override
public void triggerProbe(Host host) {
/*
* Note: In CORD deployment, we assume that all hosts are configured.
* Therefore no probe is required.
*/
}
/**
* Adds host information.
* IP information will be appended if host exists.
*
* @param mac MAC address of the host
* @param vlan VLAN ID of the host
* @param hloc Location of the host
* @param ips Set of IP addresses of the host
*/
protected void addHost(MacAddress mac, VlanId vlan, HostLocation hloc, Set<IpAddress> ips) {
HostId hid = HostId.hostId(mac, vlan);
HostDescription desc = new DefaultHostDescription(mac, vlan, hloc, ips);
providerService.hostDetected(hid, desc, false);
}
/**
* Updates host information.
* IP information will be replaced if host exists.
*
* @param mac MAC address of the host
* @param vlan VLAN ID of the host
* @param hloc Location of the host
* @param ips Set of IP addresses of the host
*/
protected void updateHost(MacAddress mac, VlanId vlan, HostLocation hloc, Set<IpAddress> ips) {
HostId hid = HostId.hostId(mac, vlan);
HostDescription desc = new DefaultHostDescription(mac, vlan, hloc, ips);
providerService.hostDetected(hid, desc, true);
}
/**
* Removes host information.
*
* @param mac MAC address of the host
* @param vlan VLAN ID of the host
*/
protected void removeHost(MacAddress mac, VlanId vlan) {
HostId hid = HostId.hostId(mac, vlan);
providerService.hostVanished(hid);
}
private void readInitialConfig() {
networkConfigRegistry.getSubjects(HostId.class).forEach(hostId -> {
MacAddress mac = hostId.mac();
VlanId vlan = hostId.vlanId();
BasicHostConfig hostConfig =
networkConfigRegistry.getConfig(hostId, BasicHostConfig.class);
Set<IpAddress> ipAddresses = hostConfig.ipAddresses();
ConnectPoint location = hostConfig.location();
HostLocation hloc = new HostLocation(location, System.currentTimeMillis());
addHost(mac, vlan, hloc, ipAddresses);
});
}
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
// Do not process non-host, register and unregister events
if (!event.configClass().equals(BasicHostConfig.class) ||
event.type() == NetworkConfigEvent.Type.CONFIG_REGISTERED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UNREGISTERED) {
return;
}
HostId hostId = (HostId) event.subject();
MacAddress mac = hostId.mac();
VlanId vlan = hostId.vlanId();
BasicHostConfig hostConfig =
networkConfigRegistry.getConfig(hostId, BasicHostConfig.class);
Set<IpAddress> ipAddresses = null;
HostLocation hloc = null;
// Note: There will be no config presented in the CONFIG_REMOVE case
if (hostConfig != null) {
ipAddresses = hostConfig.ipAddresses();
ConnectPoint location = hostConfig.location();
hloc = new HostLocation(location, System.currentTimeMillis());
}
switch (event.type()) {
case CONFIG_ADDED:
addHost(mac, vlan, hloc, ipAddresses);
break;
case CONFIG_UPDATED:
updateHost(mac, vlan, hloc, ipAddresses);
break;
case CONFIG_REMOVED:
removeHost(mac, vlan);
break;
default:
break;
}
}
}
}
/*
* Copyright 2014-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.
*/
/**
* Host provider that uses network config service to discover hosts.
*/
package org.onosproject.provider.netcfghost;
/*
* Copyright 2014-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.provider.netcfghost;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.PortNumber;
import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostProvider;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.provider.AbstractProviderService;
import java.util.HashSet;
import java.util.Set;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
/**
* Set of tests of the host location provider for CORD.
*/
public class NetworkConfigHostProviderTest {
private NetworkConfigHostProvider provider = new NetworkConfigHostProvider();
private MockHostProviderService providerService = new MockHostProviderService(provider);
private MacAddress mac = MacAddress.valueOf("c0:ff:ee:c0:ff:ee");
private VlanId vlan = VlanId.vlanId(VlanId.UNTAGGED);
private DeviceId deviceId = DeviceId.deviceId("of:0000000000000001");
private PortNumber port = PortNumber.portNumber(5);
private HostLocation hloc = new HostLocation(deviceId, port, 100);
private Set<IpAddress> ips = new HashSet<>();
private HostId hostId = HostId.hostId(mac, vlan);
private HostDescription hostDescription;
@Before
public void setUp() {
provider.providerService = providerService;
// Initialize test variables
ips.add(IpAddress.valueOf("10.0.0.1"));
ips.add(IpAddress.valueOf("192.168.0.1"));
hostDescription = new DefaultHostDescription(mac, vlan, hloc, ips);
}
@Test
public void testAddHost() throws Exception {
provider.addHost(mac, vlan, hloc, ips);
assertThat(providerService.hostId, is(hostId));
assertThat(providerService.hostDescription, is(hostDescription));
assertThat(providerService.event, is("hostDetected"));
providerService.clear();
}
@Test
public void testUpdateHost() throws Exception {
provider.updateHost(mac, vlan, hloc, ips);
assertThat(providerService.hostId, is(hostId));
assertThat(providerService.hostDescription, is(hostDescription));
assertThat(providerService.event, is("hostDetected"));
providerService.clear();
}
@Test
public void testRemoveHost() throws Exception {
provider.removeHost(mac, vlan);
assertThat(providerService.hostId, is(hostId));
assertNull(providerService.hostDescription);
assertThat(providerService.event, is("hostVanished"));
providerService.clear();
}
/**
* Mock HostProviderService.
*/
private class MockHostProviderService
extends AbstractProviderService<HostProvider>
implements HostProviderService {
private HostId hostId = null;
private HostDescription hostDescription = null;
private String event = null;
public MockHostProviderService(HostProvider provider) {
super(provider);
}
@Override
public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) {
this.hostId = hostId;
this.hostDescription = hostDescription;
this.event = "hostDetected";
}
@Override
public void hostVanished(HostId hostId) {
this.hostId = hostId;
this.event = "hostVanished";
}
@Override
public void removeIpFromHost(HostId hostId, IpAddress ipAddress) {
// Note: This method is never used.
}
public void clear() {
this.hostId = null;
this.hostDescription = null;
this.event = null;
}
}
}
......@@ -35,6 +35,7 @@
<module>openflow</module>
<module>lldp</module>
<module>host</module>
<module>netcfghost</module>
<module>netconf</module>
<module>null</module>
<module>pcep</module>
......
{
"ports" : {
"of:0000000000000002/1" : {
"interfaces" : [
{
"ips" : [ "192.168.10.101/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
}
]
},
"of:0000000000000002/20" : {
"interfaces" : [
{
"ips" : [ "192.168.20.101/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "200"
}
]
}
"of:0000000000000002/1" : {
"interfaces" : [
{
"ips" : [ "192.168.10.101/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
}
]
},
"of:0000000000000002/2" : {
"interfaces" : [
{
"ips" : [ "192.168.20.101/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "200"
}
]
}
},
"devices" : {
"of:0000000000000002" : {
"segmentrouting" : {
"of:0000000000000002" : {
"segmentrouting" : {
"name" : "Leaf-R1",
"nodeSid" : 101,
"routerIp" : "10.0.1.254",
......@@ -32,36 +32,54 @@
{ "sid" : 200, "ports" : [4, 5] }
]
}
},
"of:0000000000000191" : {
"segmentrouting" : {
},
"of:0000000000000191" : {
"segmentrouting" : {
"name" : "Spine-R1",
"nodeSid" : 105,
"routerIp" : "192.168.0.11",
"routerMac" : "00:00:01:00:11:80",
"isEdgeRouter" : false
}
}
}
},
"apps" : {
"org.onosproject.router" : {
"bgp" : {
"bgpSpeakers" : [
{
"connectPoint" : "of:00000000000000aa/10",
"peers" : [
"192.168.10.1"
]
},
{
"connectPoint" : "of:00000000000000aa/20",
"peers" : [
"192.168.20.1"
]
}
]
}
}
"org.onosproject.router" : {
"bgp" : {
"bgpSpeakers" : [
{
"connectPoint" : "of:00000000000000aa/10",
"peers" : [
"192.168.10.1"
]
},
{
"connectPoint" : "of:00000000000000aa/20",
"peers" : [
"192.168.20.1"
]
}
]
}
}
},
"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"
}
}
}
}
......
{
"ports" : {
"of:0000000000000001/3" : {
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
}
]
},
"of:0000000000000001/4" : {
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
}
]
},
"of:0000000000000002/3" : {
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
}
]
},
"of:0000000000000002/4" : {
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
}
]
}
},
"devices" : {
"of:0000000000000001" : {
"segmentrouting" : {
"name" : "Leaf-R1",
"nodeSid" : 101,
"routerIp" : "10.0.1.254",
"routerMac" : "00:00:00:00:01:80",
"isEdgeRouter" : true
}
},
"of:0000000000000002" : {
"segmentrouting" : {
"name" : "Leaf-R2",
"nodeSid" : 102,
"routerIp" : "10.0.2.254",
"routerMac" : "00:00:00:00:02:80",
"isEdgeRouter" : true
}
},
"of:0000000000000191" : {
"segmentrouting" : {
"name" : "Spine-R1",
"nodeSid" : 103,
"routerIp" : "192.168.0.11",
"routerMac" : "00:00:01:00:11:80",
"isEdgeRouter" : false
}
},
"of:0000000000000192" : {
"segmentrouting" : {
"name" : "Spine-R2",
"nodeSid" : 104,
"routerIp" : "192.168.0.22",
"routerMac" : "00:00:01:00:22:80",
"isEdgeRouter" : false
}
}
},
"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"
}
}
}
}