Charles Chan
Committed by Gerrit Code Review

CORD-348 Fabric multicast support - error handling

Automatically failover to backup spine if
- ingress - transit link down
- transit - egress link down
- transit device down

Can recover from fatal error with human involved
- ingress switch down
- egress switch down
- all links to spine down

Scan through McastRouteStore when
- SR activate
- link up

Also include following features
- Use flow objective context in McastHandler
- Update Mcast VLAN config sample

Change-Id: I75007d9efd7646e7c4e57fa6d3fc6943543153cf
......@@ -42,25 +42,33 @@ import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
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.ForwardingObjective;
import org.onosproject.net.flowobjective.NextObjective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.mcast.McastEvent;
import org.onosproject.net.mcast.McastRouteInfo;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.segmentrouting.storekey.McastNextObjectiveStoreKey;
import org.onosproject.segmentrouting.storekey.McastStoreKey;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
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.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkState;
/**
* Handles multicast-related events.
......@@ -71,8 +79,27 @@ public class McastHandler {
private final ApplicationId coreAppId;
private StorageService storageService;
private TopologyService topologyService;
private final KryoNamespace.Builder kryoBuilder;
private final ConsistentMap<McastNextObjectiveStoreKey, NextObjective> mcastNextObjStore;
private final ConsistentMap<McastStoreKey, NextObjective> mcastNextObjStore;
private final KryoNamespace.Builder mcastKryo;
private final ConsistentMap<McastStoreKey, McastRole> mcastRoleStore;
/**
* Role in the multicast tree.
*/
public enum McastRole {
/**
* The device is the ingress device of this group.
*/
INGRESS,
/**
* The device is the transit device of this group.
*/
TRANSIT,
/**
* The device is the egress device of this group.
*/
EGRESS
}
/**
* Constructs the McastEventHandler.
......@@ -81,19 +108,36 @@ public class McastHandler {
*/
public McastHandler(SegmentRoutingManager srManager) {
coreAppId = srManager.coreService.getAppId(CoreService.CORE_APP_NAME);
this.srManager = srManager;
this.storageService = srManager.storageService;
this.topologyService = srManager.topologyService;
kryoBuilder = new KryoNamespace.Builder()
mcastKryo = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(McastNextObjectiveStoreKey.class);
.register(McastStoreKey.class)
.register(McastRole.class);
mcastNextObjStore = storageService
.<McastNextObjectiveStoreKey, NextObjective>consistentMapBuilder()
.<McastStoreKey, NextObjective>consistentMapBuilder()
.withName("onos-mcast-nextobj-store")
.withSerializer(Serializer.using(kryoBuilder.build()))
.withSerializer(Serializer.using(mcastKryo.build()))
.build();
mcastRoleStore = storageService
.<McastStoreKey, McastRole>consistentMapBuilder()
.withName("onos-mcast-role-store")
.withSerializer(Serializer.using(mcastKryo.build()))
.build();
}
/**
* Read initial multicast from mcast store.
*/
public void init() {
srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> {
ConnectPoint source = srManager.multicastRouteService.fetchSource(mcastRoute);
Set<ConnectPoint> sinks = srManager.multicastRouteService.fetchSinks(mcastRoute);
sinks.forEach(sink -> {
processSinkAddedInternal(source, sink, mcastRoute.group());
});
});
}
/**
......@@ -166,6 +210,9 @@ public class McastHandler {
// Process the egress device
boolean isLast = removePortFromDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan);
if (isLast) {
mcastRoleStore.remove(new McastStoreKey(mcastIp, sink.deviceId()));
}
// If this is the last sink on the device, also update upstream
Optional<Path> mcastPath = getPath(source.deviceId(), sink.deviceId(), mcastIp);
......@@ -176,6 +223,7 @@ public class McastHandler {
if (isLast) {
isLast = removePortFromDevice(link.src().deviceId(), link.src().port(),
mcastIp, assignedVlan);
mcastRoleStore.remove(new McastStoreKey(mcastIp, link.src().deviceId()));
}
}
}
......@@ -192,6 +240,9 @@ public class McastHandler {
IpAddress mcastIp) {
VlanId assignedVlan = assignedVlan();
// Process the ingress device
addFilterToDevice(source.deviceId(), source.port(), assignedVlan);
// When source and sink are on the same device
if (source.deviceId().equals(sink.deviceId())) {
// Source and sink are on even the same port. There must be something wrong.
......@@ -200,22 +251,88 @@ public class McastHandler {
return;
}
addPortToDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan);
mcastRoleStore.put(new McastStoreKey(mcastIp, sink.deviceId()), McastRole.INGRESS);
return;
}
// Process the ingress device
addFilterToDevice(source.deviceId(), source.port(), assignedVlan);
// Find a path. If present, create/update groups and flows for each hop
Optional<Path> mcastPath = getPath(source.deviceId(), sink.deviceId(), mcastIp);
if (mcastPath.isPresent()) {
mcastPath.get().links().forEach(link -> {
List<Link> links = mcastPath.get().links();
checkState(links.size() == 2,
"Path in leaf-spine topology should always be two hops: ", links);
links.forEach(link -> {
addFilterToDevice(link.dst().deviceId(), link.dst().port(), assignedVlan);
addPortToDevice(link.src().deviceId(), link.src().port(), mcastIp, assignedVlan);
});
// Process the egress device
addPortToDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan);
// Setup mcast roles
mcastRoleStore.put(new McastStoreKey(mcastIp, source.deviceId()),
McastRole.INGRESS);
mcastRoleStore.put(new McastStoreKey(mcastIp, links.get(0).dst().deviceId()),
McastRole.TRANSIT);
mcastRoleStore.put(new McastStoreKey(mcastIp, sink.deviceId()),
McastRole.EGRESS);
} else {
log.warn("Unable to find a path from {} to {}. Abort sinkAdded",
source.deviceId(), sink.deviceId());
}
}
/**
* Processes the LINK_DOWN event.
*
* @param affectedLink Link that is going down
*/
protected void processLinkDown(Link affectedLink) {
VlanId assignedVlan = assignedVlan();
getAffectedGroups(affectedLink).forEach(mcastIp -> {
// Find out the ingress, transit and egress device of affected group
DeviceId ingressDevice = getDevice(mcastIp, McastRole.INGRESS)
.stream().findAny().orElse(null);
DeviceId transitDevice = getDevice(mcastIp, McastRole.TRANSIT)
.stream().findAny().orElse(null);
Set<DeviceId> egressDevices = getDevice(mcastIp, McastRole.EGRESS);
if (ingressDevice == null || transitDevice == null || egressDevices == null) {
log.warn("Missing ingress {}, transit {}, or egress {} devices",
ingressDevice, transitDevice, egressDevices);
return;
}
// Remove entire transit
removeGroupFromDevice(transitDevice, mcastIp, assignedVlan);
// Remove transit-facing port on ingress device
PortNumber ingressTransitPort = ingressTransitPort(mcastIp);
if (ingressTransitPort != null) {
removePortFromDevice(ingressDevice, ingressTransitPort, mcastIp, assignedVlan);
mcastRoleStore.remove(new McastStoreKey(mcastIp, transitDevice));
}
// Construct a new path for each egress device
egressDevices.forEach(egressDevice -> {
Optional<Path> mcastPath = getPath(ingressDevice, egressDevice, mcastIp);
if (mcastPath.isPresent()) {
List<Link> links = mcastPath.get().links();
links.forEach(link -> {
addPortToDevice(link.src().deviceId(), link.src().port(), mcastIp, assignedVlan);
addFilterToDevice(link.dst().deviceId(), link.dst().port(), assignedVlan);
});
// Setup new transit mcast role
mcastRoleStore.put(new McastStoreKey(mcastIp,
links.get(0).dst().deviceId()), McastRole.TRANSIT);
} else {
log.warn("Fail to recover egress device {} from link failure {}",
egressDevice, affectedLink);
removeGroupFromDevice(egressDevice, mcastIp, assignedVlan);
}
});
});
}
/**
......@@ -228,7 +345,8 @@ public class McastHandler {
private void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan) {
// Do nothing if the port is configured as suppressed
ConnectPoint connectPt = new ConnectPoint(deviceId, port);
if (srManager.deviceConfiguration.suppressSubnet().contains(connectPt) ||
if (srManager.deviceConfiguration == null ||
srManager.deviceConfiguration.suppressSubnet().contains(connectPt) ||
srManager.deviceConfiguration.suppressHost().contains(connectPt)) {
log.info("Ignore suppressed port {}", connectPt);
return;
......@@ -236,8 +354,13 @@ public class McastHandler {
FilteringObjective.Builder filtObjBuilder =
filterObjBuilder(deviceId, port, assignedVlan);
srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add());
// TODO add objective context
ObjectiveContext context = new DefaultObjectiveContext(
(objective) -> log.debug("Successfully add filter on {}/{}, vlan {}",
deviceId, port.toLong(), assignedVlan),
(objective, error) ->
log.warn("Failed to add filter on {}/{}, vlan {}: {}",
deviceId, port.toLong(), assignedVlan, error));
srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
}
/**
......@@ -251,17 +374,14 @@ public class McastHandler {
*/
private void addPortToDevice(DeviceId deviceId, PortNumber port,
IpAddress mcastIp, VlanId assignedVlan) {
log.info("Add port {} to {}. mcastIp={}, assignedVlan={}",
port, deviceId, mcastIp, assignedVlan);
McastNextObjectiveStoreKey mcastNextObjectiveStoreKey =
new McastNextObjectiveStoreKey(mcastIp, deviceId);
McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, deviceId);
ImmutableSet.Builder<PortNumber> portBuilder = ImmutableSet.builder();
if (!mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) {
if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
// First time someone request this mcast group via this device
portBuilder.add(port);
} else {
// This device already serves some subscribers of this mcast group
NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value();
NextObjective nextObj = mcastNextObjStore.get(mcastStoreKey).value();
// Stop if the port is already in the nextobj
Set<PortNumber> existingPorts = getPorts(nextObj.next());
if (existingPorts.contains(port)) {
......@@ -271,14 +391,19 @@ public class McastHandler {
portBuilder.addAll(existingPorts).add(port).build();
}
// Create, store and apply the new nextObj and fwdObj
ObjectiveContext context = new DefaultObjectiveContext(
(objective) -> log.debug("Successfully add {} on {}/{}, vlan {}",
mcastIp, deviceId, port.toLong(), assignedVlan),
(objective, error) ->
log.warn("Failed to add {} on {}/{}, vlan {}: {}",
mcastIp, deviceId, port.toLong(), assignedVlan, error));
NextObjective newNextObj =
nextObjBuilder(mcastIp, assignedVlan, portBuilder.build()).add();
ForwardingObjective fwdObj =
fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add();
mcastNextObjStore.put(mcastNextObjectiveStoreKey, newNextObj);
fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add(context);
mcastNextObjStore.put(mcastStoreKey, newNextObj);
srManager.flowObjectiveService.next(deviceId, newNextObj);
srManager.flowObjectiveService.forward(deviceId, fwdObj);
// TODO add objective callback
}
/**
......@@ -294,19 +419,17 @@ public class McastHandler {
*/
private boolean removePortFromDevice(DeviceId deviceId, PortNumber port,
IpAddress mcastIp, VlanId assignedVlan) {
log.info("Remove port {} from {}. mcastIp={}, assignedVlan={}",
port, deviceId, mcastIp, assignedVlan);
McastNextObjectiveStoreKey mcastNextObjectiveStoreKey =
new McastNextObjectiveStoreKey(mcastIp, deviceId);
McastStoreKey mcastStoreKey =
new McastStoreKey(mcastIp, deviceId);
// This device is not serving this multicast group
if (!mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) {
if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
log.warn("{} is not serving {} on port {}. Abort.", deviceId, mcastIp, port);
return false;
}
NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value();
NextObjective nextObj = mcastNextObjStore.get(mcastStoreKey).value();
Set<PortNumber> existingPorts = getPorts(nextObj.next());
// This device does not serve this multicast group
// This port does not serve this multicast group
if (!existingPorts.contains(port)) {
log.warn("{} is not serving {} on port {}. Abort.", deviceId, mcastIp, port);
return false;
......@@ -322,22 +445,91 @@ public class McastHandler {
// NOTE: Rely on GroupStore garbage collection rather than explicitly
// remove L3MG since there might be other flows/groups refer to
// the same L2IG
fwdObj = fwdObjBuilder(mcastIp, assignedVlan, nextObj.id()).remove();
mcastNextObjStore.remove(mcastNextObjectiveStoreKey);
ObjectiveContext context = new DefaultObjectiveContext(
(objective) -> log.debug("Successfully remove {} on {}/{}, vlan {}",
mcastIp, deviceId, port.toLong(), assignedVlan),
(objective, error) ->
log.warn("Failed to remove {} on {}/{}, vlan {}: {}",
mcastIp, deviceId, port.toLong(), assignedVlan, error));
fwdObj = fwdObjBuilder(mcastIp, assignedVlan, nextObj.id()).remove(context);
mcastNextObjStore.remove(mcastStoreKey);
srManager.flowObjectiveService.forward(deviceId, fwdObj);
} else {
// If this is not the last sink, update flows and groups
ObjectiveContext context = new DefaultObjectiveContext(
(objective) -> log.debug("Successfully update {} on {}/{}, vlan {}",
mcastIp, deviceId, port.toLong(), assignedVlan),
(objective, error) ->
log.warn("Failed to update {} on {}/{}, vlan {}: {}",
mcastIp, deviceId, port.toLong(), assignedVlan, error));
newNextObj = nextObjBuilder(mcastIp, assignedVlan, existingPorts).add();
fwdObj = fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add();
mcastNextObjStore.put(mcastNextObjectiveStoreKey, newNextObj);
mcastNextObjStore.put(mcastStoreKey, newNextObj);
srManager.flowObjectiveService.next(deviceId, newNextObj);
srManager.flowObjectiveService.forward(deviceId, fwdObj);
}
// TODO add objective callback
return existingPorts.isEmpty();
}
/**
* Removes entire group on given device.
*
* @param deviceId device ID
* @param mcastIp multicast group to be removed
* @param assignedVlan assigned VLAN ID
*/
private void removeGroupFromDevice(DeviceId deviceId, IpAddress mcastIp,
VlanId assignedVlan) {
McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, deviceId);
// This device is not serving this multicast group
if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
log.warn("{} is not serving {}. Abort.", deviceId, mcastIp);
return;
}
NextObjective nextObj = mcastNextObjStore.get(mcastStoreKey).value();
// NOTE: Rely on GroupStore garbage collection rather than explicitly
// remove L3MG since there might be other flows/groups refer to
// the same L2IG
ObjectiveContext context = new DefaultObjectiveContext(
(objective) -> log.debug("Successfully remove {} on {}, vlan {}",
mcastIp, deviceId, assignedVlan),
(objective, error) ->
log.warn("Failed to remove {} on {}, vlan {}: {}",
mcastIp, deviceId, assignedVlan, error));
ForwardingObjective fwdObj = fwdObjBuilder(mcastIp, assignedVlan, nextObj.id()).remove(context);
srManager.flowObjectiveService.forward(deviceId, fwdObj);
mcastNextObjStore.remove(mcastStoreKey);
mcastRoleStore.remove(mcastStoreKey);
}
/**
* Remove all groups on given device.
*
* @param deviceId device ID
*/
public void removeDevice(DeviceId deviceId) {
Iterator<Map.Entry<McastStoreKey, Versioned<NextObjective>>> itNextObj =
mcastNextObjStore.entrySet().iterator();
while (itNextObj.hasNext()) {
Map.Entry<McastStoreKey, Versioned<NextObjective>> entry = itNextObj.next();
if (entry.getKey().deviceId().equals(deviceId)) {
removeGroupFromDevice(entry.getKey().deviceId(), entry.getKey().mcastIp(), assignedVlan());
itNextObj.remove();
}
}
Iterator<Map.Entry<McastStoreKey, Versioned<McastRole>>> itRole =
mcastRoleStore.entrySet().iterator();
while (itRole.hasNext()) {
Map.Entry<McastStoreKey, Versioned<McastRole>> entry = itRole.next();
if (entry.getKey().deviceId().equals(deviceId)) {
itRole.remove();
}
}
}
/**
* Creates a next objective builder for multicast.
*
......@@ -456,16 +648,15 @@ public class McastHandler {
private Optional<Path> getPath(DeviceId src, DeviceId dst, IpAddress mcastIp) {
List<Path> allPaths = Lists.newArrayList(
topologyService.getPaths(topologyService.currentTopology(), src, dst));
log.debug("{} path(s) found from {} to {}", allPaths.size(), src, dst);
if (allPaths.isEmpty()) {
log.warn("Fail to find a path from {} to {}. Abort.", src, dst);
return Optional.empty();
}
// If one of the available path is used before, use the same path
McastNextObjectiveStoreKey mcastNextObjectiveStoreKey =
new McastNextObjectiveStoreKey(mcastIp, src);
if (mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) {
NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value();
McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, src);
if (mcastNextObjStore.containsKey(mcastStoreKey)) {
NextObjective nextObj = mcastNextObjStore.get(mcastStoreKey).value();
Set<PortNumber> existingPorts = getPorts(nextObj.next());
for (Path path : allPaths) {
PortNumber srcPort = path.links().get(0).src().port();
......@@ -480,6 +671,37 @@ public class McastHandler {
}
/**
* Gets device(s) of given role in given multicast group.
*
* @param mcastIp multicast IP
* @param role multicast role
* @return set of device ID or empty set if not found
*/
private Set<DeviceId> getDevice(IpAddress mcastIp, McastRole role) {
return mcastRoleStore.entrySet().stream()
.filter(entry -> entry.getKey().mcastIp().equals(mcastIp) &&
entry.getValue().value() == role)
.map(Map.Entry::getKey).map(McastStoreKey::deviceId)
.collect(Collectors.toSet());
}
/**
* Gets groups which is affected by the link down event.
*
* @param link link going down
* @return a set of multicast IpAddress
*/
private Set<IpAddress> getAffectedGroups(Link link) {
DeviceId deviceId = link.src().deviceId();
PortNumber port = link.src().port();
return mcastNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(deviceId) &&
getPorts(entry.getValue().value().next()).contains(port))
.map(Map.Entry::getKey).map(McastStoreKey::mcastIp)
.collect(Collectors.toSet());
}
/**
* Gets egress VLAN from McastConfig.
*
* @return egress VLAN or VlanId.NONE if not configured
......@@ -500,4 +722,34 @@ public class McastHandler {
VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET) :
egressVlan();
}
/**
* Gets the spine-facing port on ingress device of given multicast group.
*
* @param mcastIp multicast IP
* @return spine-facing port on ingress device
*/
private PortNumber ingressTransitPort(IpAddress mcastIp) {
DeviceId ingressDevice = getDevice(mcastIp, McastRole.INGRESS)
.stream().findAny().orElse(null);
if (ingressDevice != null) {
NextObjective nextObj = mcastNextObjStore
.get(new McastStoreKey(mcastIp, ingressDevice)).value();
Set<PortNumber> ports = getPorts(nextObj.next());
for (PortNumber port : ports) {
// Spine-facing port should have no subnet and no xconnect
if (srManager.deviceConfiguration != null &&
srManager.deviceConfiguration.getPortSubnet(ingressDevice, port) == null &&
srManager.deviceConfiguration.getXConnects().values().stream()
.allMatch(connectPoints ->
connectPoints.stream().noneMatch(connectPoint ->
connectPoint.port().equals(port))
)) {
return port;
}
}
}
return null;
}
}
......
......@@ -762,6 +762,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(null);
//log.trace("processLinkAdded: re-starting route population process");
//defaultRoutingHandler.startPopulationProcess();
mcastHandler.init();
}
private void processLinkRemoved(Link link) {
......@@ -775,6 +777,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link);
//log.trace("processLinkRemoved: re-starting route population process");
//defaultRoutingHandler.startPopulationProcess();
mcastHandler.processLinkDown(link);
}
private void processDeviceAdded(Device device) {
......@@ -784,17 +788,22 @@ public class SegmentRoutingManager implements SegmentRoutingService {
+ "processed after config completes.", device.id());
return;
}
processDeviceAddedInternal(device.id());
}
private void processDeviceAddedInternal(DeviceId deviceId) {
// Irrespective of whether the local is a MASTER or not for this device,
// we need to create a SR-group-handler instance. This is because in a
// multi-instance setup, any instance can initiate forwarding/next-objectives
// for any switch (even if this instance is a SLAVE or not even connected
// to the switch). To handle this, a default-group-handler instance is necessary
// per switch.
if (groupHandlerMap.get(device.id()) == null) {
log.debug("Current groupHandlerMap devs: {}", groupHandlerMap.keySet());
if (groupHandlerMap.get(deviceId) == null) {
DefaultGroupHandler groupHandler;
try {
groupHandler = DefaultGroupHandler.
createGroupHandler(device.id(),
createGroupHandler(deviceId,
appId,
deviceConfiguration,
linkService,
......@@ -804,23 +813,25 @@ public class SegmentRoutingManager implements SegmentRoutingService {
log.warn(e.getMessage() + " Aborting processDeviceAdded.");
return;
}
groupHandlerMap.put(device.id(), groupHandler);
log.debug("updating groupHandlerMap with new config for device: {}",
deviceId);
groupHandlerMap.put(deviceId, groupHandler);
// Also, in some cases, drivers may need extra
// information to process rules (eg. Router IP/MAC); and so, we send
// port addressing rules to the driver as well irrespective of whether
// this instance is the master or not.
defaultRoutingHandler.populatePortAddressingRules(device.id());
defaultRoutingHandler.populatePortAddressingRules(deviceId);
}
if (mastershipService.isLocalMaster(device.id())) {
hostHandler.readInitialHosts(device.id());
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
if (mastershipService.isLocalMaster(deviceId)) {
hostHandler.readInitialHosts(deviceId);
DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
groupHandler.createGroupsForXConnect(device.id());
routingRulePopulator.populateXConnectBroadcastRule(device.id());
routingRulePopulator.populateSubnetBroadcastRule(deviceId);
groupHandler.createGroupsForXConnect(deviceId);
routingRulePopulator.populateXConnectBroadcastRule(deviceId);
}
netcfgHandler.initVRouters(device.id());
netcfgHandler.initVRouters(deviceId);
}
private void processDeviceRemoved(Device device) {
......@@ -829,34 +840,29 @@ public class SegmentRoutingManager implements SegmentRoutingService {
.forEach(entry -> {
nsNextObjStore.remove(entry.getKey());
});
subnetNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> {
subnetNextObjStore.remove(entry.getKey());
});
portNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> {
portNextObjStore.remove(entry.getKey());
});
xConnectNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> {
xConnectNextObjStore.remove(entry.getKey());
});
subnetVidStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> {
subnetVidStore.remove(entry.getKey());
});
groupHandlerMap.remove(device.id());
defaultRoutingHandler.purgeEcmpGraph(device.id());
mcastHandler.removeDevice(device.id());
}
private void processPortRemoved(Device device, Port port) {
......@@ -900,48 +906,11 @@ public class SegmentRoutingManager implements SegmentRoutingService {
tunnelHandler, policyStore);
for (Device device : deviceService.getDevices()) {
// Irrespective of whether the local is a MASTER or not for this device,
// we need to create a SR-group-handler instance. This is because in a
// multi-instance setup, any instance can initiate forwarding/next-objectives
// for any switch (even if this instance is a SLAVE or not even connected
// to the switch). To handle this, a default-group-handler instance is necessary
// per switch.
log.debug("Current groupHandlerMap devs: {}", groupHandlerMap.keySet());
if (groupHandlerMap.get(device.id()) == null) {
DefaultGroupHandler groupHandler;
try {
groupHandler = DefaultGroupHandler.
createGroupHandler(device.id(),
appId,
deviceConfiguration,
linkService,
flowObjectiveService,
segmentRoutingManager);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting configureNetwork.");
return;
}
log.debug("updating groupHandlerMap with new config for "
+ "device: {}", device.id());
groupHandlerMap.put(device.id(), groupHandler);
// Also, in some cases, drivers may need extra
// information to process rules (eg. Router IP/MAC); and so, we send
// port addressing rules to the driver as well, irrespective of whether
// this instance is the master or not.
defaultRoutingHandler.populatePortAddressingRules(device.id());
}
if (mastershipService.isLocalMaster(device.id())) {
hostHandler.readInitialHosts(device.id());
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
groupHandler.createGroupsForXConnect(device.id());
routingRulePopulator.populateXConnectBroadcastRule(device.id());
}
processDeviceAddedInternal(device.id());
}
defaultRoutingHandler.startPopulationProcess();
mcastHandler.init();
}
@Override
......
......@@ -26,7 +26,7 @@ import java.util.Objects;
/**
* Key of multicast next objective store.
*/
public class McastNextObjectiveStoreKey {
public class McastStoreKey {
private final IpAddress mcastIp;
private final DeviceId deviceId;
......@@ -36,7 +36,7 @@ public class McastNextObjectiveStoreKey {
* @param mcastIp multicast group IP address
* @param deviceId device ID
*/
public McastNextObjectiveStoreKey(IpAddress mcastIp, DeviceId deviceId) {
public McastStoreKey(IpAddress mcastIp, DeviceId deviceId) {
checkNotNull(mcastIp, "mcastIp cannot be null");
checkNotNull(deviceId, "deviceId cannot be null");
checkArgument(mcastIp.isMulticast(), "mcastIp must be a multicast address");
......@@ -67,11 +67,11 @@ public class McastNextObjectiveStoreKey {
if (this == o) {
return true;
}
if (!(o instanceof McastNextObjectiveStoreKey)) {
if (!(o instanceof McastStoreKey)) {
return false;
}
McastNextObjectiveStoreKey that =
(McastNextObjectiveStoreKey) o;
McastStoreKey that =
(McastStoreKey) o;
return (Objects.equals(this.mcastIp, that.mcastIp) &&
Objects.equals(this.deviceId, that.deviceId));
}
......
......@@ -5,7 +5,7 @@
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<meta name="CocoaVersion" content="1404.34">
<meta name="CocoaVersion" content="1404.46">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #0433ff; -webkit-text-stroke: #0433ff}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #ff9300; -webkit-text-stroke: #ff9300}
......@@ -14,6 +14,7 @@
p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #00c7fc; -webkit-text-stroke: #00c7fc}
p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #ff2600; -webkit-text-stroke: #ff2600}
p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #000000; -webkit-text-stroke: #000000}
p.p8 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #00c7fc; -webkit-text-stroke: #000000}
span.s1 {font-kerning: none}
span.s2 {font-kerning: none; color: #0433ff; -webkit-text-stroke: 0px #0433ff}
span.s3 {font-kerning: none; color: #000000; -webkit-text-stroke: 0px #000000}
......@@ -22,7 +23,8 @@
span.s6 {font-kerning: none; color: #77bb41; -webkit-text-stroke: 0px #77bb41}
span.s7 {font-kerning: none; color: #00c7fc; -webkit-text-stroke: 0px #00c7fc}
span.s8 {font-kerning: none; color: #ff2600; -webkit-text-stroke: 0px #ff2600}
span.s9 {font-kerning: none; color: #669c35; -webkit-text-stroke: 0px #669c35}
span.s9 {font-kerning: none; color: #000000}
span.s10 {font-kerning: none; color: #669c35; -webkit-text-stroke: 0px #669c35}
span.Apple-tab-span {white-space:pre}
</style>
</head>
......@@ -31,7 +33,7 @@
<p class="p2"><span class="s1">Orange: vSG LAN connectivity (cross-connect)</span></p>
<p class="p3"><span class="s1">Magenta: vSG WAN connectivity (default route)</span></p>
<p class="p4"><span class="s1">Green: vRouter integration</span></p>
<p class="p5"><span class="s1">Light Blue: PIM integration</span></p>
<p class="p5"><span class="s1">Light Blue: Multicast</span></p>
<p class="p6"><span class="s1">Red: Link validation</span></p>
<p class="p7"><span class="s1"><br>
</span></p>
......@@ -221,7 +223,11 @@
<p class="p7"><span class="s1"><span class="Apple-converted-space">        </span>"org.onosproject.core" : {</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>"core" : {</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"linkDiscoveryMode" : "STRICT" </span><span class="s8">// enable strict link validation</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>} <span class="Apple-converted-space">   </span></span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>},</span></p>
<p class="p8"><span class="s9"><span class="Apple-tab-span"> </span><span class="Apple-tab-span"> </span><span class="Apple-converted-space">  </span>"multicast": { </span><span class="s1">// The VLAN we expect to see on the multicast ingress and egress</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"ingressVlan": "None",</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"egressVlan": "None"</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>}<span class="Apple-converted-space"> </span></span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">        </span>},</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">        </span>"org.onosproject.segmentrouting" : {</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>"segmentrouting" : {</span></p>
......@@ -243,7 +249,7 @@
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"controlPlaneConnectPoint" : "of:0000000000000002/31", </span><span class="s6">// location of Quagga</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"ospfEnabled" : "true", </span><span class="s6">// enable OSPF</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"pimEnabled" : "true", </span><span class="s7">// enable PIM</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"interfaces" : [ "external-quagga" ] </span><span class="s9">// </span><span class="s6">VR only handles peers on these ports</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"interfaces" : [ "external-quagga" ] </span><span class="s10">// </span><span class="s6">VR only handles peers on these ports</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>}</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">        </span>}</span></p>
<p class="p7"><span class="s1"><span class="Apple-converted-space">    </span>}</span></p>
......
......@@ -228,6 +228,10 @@
"org.onosproject.core" : {
"core" : {
"linkDiscoveryMode" : "STRICT"
},
"multicast": {
"ingressVlan": "None",
"egressVlan": "None"
}
},
"org.onosproject.segmentrouting" : {
......