Charles Chan

CORD-77 Dynamic Access Agent Config

This commit depends on https://gerrit.opencord.org/#/c/56/

Change-Id: I6084621c36046ae8b6262cab52c49825d3e0d0d1
......@@ -2,12 +2,18 @@ COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:org.apache.karaf.shell.console',
'//lib:javax.ws.rs-api',
'//lib:cord-config',
'//cli:onos-cli',
'//core/store/serializers:onos-core-serializers',
'//incubator/api:onos-incubator-api',
'//utils/rest:onlab-rest',
]
BUNDLES = [
'//apps/segmentrouting:onos-apps-segmentrouting',
'//lib:cord-config'
]
TEST_DEPS = [
'//lib:TEST_ADAPTERS',
]
......@@ -21,5 +27,6 @@ onos_app (
title = 'Segment Routing App',
category = 'Traffic Steering',
url = 'http://onosproject.org',
included_bundles = BUNDLES,
description = 'Segment routing application.',
)
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present 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.
-->
<app name="org.onosproject.segmentrouting" origin="ON.Lab" version="${project.version}"
title="Segment Routing App" category="Traffic Steering" url="http://onosproject.org"
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}" apps="">
<description>${project.description}</description>
<artifact>mvn:${project.groupId}/${project.artifactId}/${project.version}</artifact>
<!-- TODO: Replace this with variable -->
<artifact>mvn:org.opencord/cord-config/1.0-SNAPSHOT</artifact>
</app>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2016-present 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.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<feature>onos-api</feature>
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
<!-- TODO: Replace this with variable -->
<bundle>mvn:org.opencord/cord-config/1.0-SNAPSHOT</bundle>
</feature>
</features>
......@@ -30,11 +30,6 @@
<description>Segment routing application</description>
<properties>
<onos.app.name>org.onosproject.segmentrouting</onos.app.name>
<onos.app.title>Segment Routing App</onos.app.title>
<onos.app.category>Traffic Steering</onos.app.category>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.readme>Segment routing application.</onos.app.readme>
<web.context>/onos/segmentrouting</web.context>
<api.version>1.0.0</api.version>
<api.title>ONOS Segment Routing REST API</api.title>
......@@ -70,6 +65,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.opencord</groupId>
<artifactId>cord-config</artifactId>
<!-- TODO: Replace this with variable -->
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
......
/*
* Copyright 2016-present 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;
import com.google.common.collect.ImmutableSet;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultHost;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.provider.ProviderId;
import org.opencord.cordconfig.CordConfigEvent;
import org.opencord.cordconfig.access.AccessAgentData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
/**
* Handles access agent config event which is required for CORD integration.
*/
public class CordConfigHandler {
private static Logger log = LoggerFactory.getLogger(CordConfigHandler.class);
private final SegmentRoutingManager srManager;
/**
* Constructs the CordConfigHandler.
*
* @param srManager Segment Routing manager
*/
public CordConfigHandler(SegmentRoutingManager srManager) {
this.srManager = srManager;
}
/**
* Read initial access agent config for given device.
*
* @param deviceId ID of the device to be initialized
*/
public void init(DeviceId deviceId) {
// Try to read access agent config
Optional<AccessAgentData> accessAgent =
srManager.cordConfigService.getAccessAgent(deviceId);
if (!accessAgent.isPresent()) {
log.debug("No access agent config on {}. Skip.", deviceId);
return;
}
processAccessAgentAdded(accessAgent.get());
}
// TODO javadoc
protected void processAccessAgentAddedEvent(CordConfigEvent event) {
log.debug("processAccessAgentAdded: {}, {}", event.subject(), event.prevSubject());
processAccessAgentAdded((AccessAgentData) event.subject());
}
protected void processAccessAgentUpdatedEvent(CordConfigEvent event) {
log.debug("processAccessAgentUpdated: {}, {}", event.subject(), event.prevSubject());
processAccessAgentRemoved((AccessAgentData) event.prevSubject());
processAccessAgentAdded((AccessAgentData) event.subject());
}
protected void processAccessAgentRemovedEvent(CordConfigEvent event) {
log.debug("processAccessAgentRemoved: {}, {}", event.subject(), event.prevSubject());
processAccessAgentRemoved((AccessAgentData) event.prevSubject());
}
protected void processAccessAgentAdded(AccessAgentData accessAgentData) {
if (!srManager.mastershipService.isLocalMaster(accessAgentData.deviceId())) {
log.debug("Not the master of {}. Abort.", accessAgentData.deviceId());
return;
}
// Do not proceed if vtn location is missing
if (!accessAgentData.getVtnLocation().isPresent()) {
log.warn("accessAgentData does not contain vtn location. Abort.");
return;
}
MacAddress agentMac = accessAgentData.getAgentMac();
ConnectPoint agentLocation = accessAgentData.getVtnLocation().get();
// Do not proceed if agent port doesn't have subnet configured
Ip4Prefix agentSubnet = srManager.deviceConfiguration
.getPortSubnet(agentLocation.deviceId(), agentLocation.port());
if (agentSubnet == null) {
log.warn("Agent port does not have subnet configuration. Abort.");
return;
}
// Add host information for agent
log.info("push host info for agent {}", agentMac);
srManager.hostHandler.processHostAdded(createHost(agentMac, agentLocation));
accessAgentData.getOltMacInfo().forEach((connectPoint, macAddress) -> {
// Do not proceed if olt port has subnet configured
Ip4Prefix oltSubnet = srManager.deviceConfiguration
.getPortSubnet(connectPoint.deviceId(), connectPoint.port());
if (oltSubnet != null) {
log.warn("OLT port has subnet configuration. Abort.");
return;
}
// Add olt to the subnet of agent
log.info("push subnet for olt {}", agentSubnet);
srManager.deviceConfiguration.addSubnet(connectPoint, agentSubnet);
srManager.routingRulePopulator.populateRouterMacVlanFilters(connectPoint.deviceId());
// Add host information for olt
log.info("push host info for olt {}", macAddress);
srManager.hostHandler.processHostAdded(createHost(macAddress, connectPoint));
});
}
protected void processAccessAgentRemoved(AccessAgentData accessAgentData) {
if (!srManager.mastershipService.isLocalMaster(accessAgentData.deviceId())) {
log.debug("Not the master of {}. Abort.", accessAgentData.deviceId());
return;
}
// Do not proceed if vtn location is missing
if (!accessAgentData.getVtnLocation().isPresent()) {
log.warn("accessAgentData does not contain vtn location. Abort.");
return;
}
MacAddress agentMac = accessAgentData.getAgentMac();
ConnectPoint agentLocation = accessAgentData.getVtnLocation().get();
// Do not proceed if olt port doesn't have subnet configured
Ip4Prefix agentSubnet = srManager.deviceConfiguration
.getPortSubnet(agentLocation.deviceId(), agentLocation.port());
if (agentSubnet == null) {
log.warn("Agent port does not have subnet configuration. Abort.");
return;
}
// Remove host information for agent
log.info("delete host info for agent {}", agentMac);
srManager.hostHandler.processHostRemoved(createHost(agentMac, agentLocation));
accessAgentData.getOltMacInfo().forEach((connectPoint, macAddress) -> {
// Do not proceed if agent port doesn't have subnet configured
Ip4Prefix oltSubnet = srManager.deviceConfiguration
.getPortSubnet(connectPoint.deviceId(), connectPoint.port());
if (oltSubnet == null) {
log.warn("OLT port does not have subnet configuration. Abort.");
return;
}
// Remove host information for olt
log.info("delete host info for olt {}", macAddress);
srManager.hostHandler.processHostRemoved(createHost(macAddress, connectPoint));
// Remove olt to the subnet of agent
log.info("delete subnet for olt {}", agentSubnet);
srManager.deviceConfiguration.removeSubnet(connectPoint, agentSubnet);
srManager.routingRulePopulator.populateRouterMacVlanFilters(connectPoint.deviceId());
});
}
private Host createHost(MacAddress macAddress, ConnectPoint location) {
return new DefaultHost(
new ProviderId("host", "org.onosproject.segmentrouting"),
HostId.hostId(macAddress),
macAddress,
VlanId.NONE,
new HostLocation(location, System.currentTimeMillis()),
ImmutableSet.of());
}
}
......@@ -71,15 +71,15 @@ public class HostHandler {
if (!deviceId.equals(devId)) {
return;
}
processHostAddedEventInternal(host);
processHostAdded(host);
});
}
protected void processHostAddedEvent(HostEvent event) {
processHostAddedEventInternal(event.subject());
processHostAdded(event.subject());
}
private void processHostAddedEventInternal(Host host) {
protected void processHostAdded(Host host) {
MacAddress mac = host.mac();
VlanId vlanId = host.vlan();
HostLocation location = host.location();
......@@ -116,15 +116,19 @@ public class HostHandler {
}
protected void processHostRemoveEvent(HostEvent event) {
MacAddress mac = event.subject().mac();
VlanId vlanId = event.subject().vlan();
HostLocation location = event.subject().location();
processHostRemoved(event.subject());
}
protected void processHostRemoved(Host host) {
MacAddress mac = host.mac();
VlanId vlanId = host.vlan();
HostLocation location = host.location();
DeviceId deviceId = location.deviceId();
PortNumber port = location.port();
Set<IpAddress> ips = event.subject().ipAddresses();
Set<IpAddress> ips = host.ipAddresses();
log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
if (accepted(event.subject())) {
if (accepted(host)) {
// Revoke bridging table entry
ForwardingObjective.Builder fob =
hostFwdObjBuilder(deviceId, mac, vlanId, port);
......@@ -133,9 +137,9 @@ public class HostHandler {
return;
}
ObjectiveContext context = new DefaultObjectiveContext(
(objective) -> log.debug("Host rule for {} revoked", event.subject()),
(objective) -> log.debug("Host rule for {} revoked", host),
(objective, error) ->
log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
log.warn("Failed to revoke host rule for {}: {}", host, error));
flowObjectiveService.forward(deviceId, fob.remove(context));
// Revoke IP table entry
......
......@@ -82,6 +82,9 @@ import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.EventuallyConsistentMapBuilder;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.WallClockTimestamp;
import org.opencord.cordconfig.CordConfigEvent;
import org.opencord.cordconfig.CordConfigListener;
import org.opencord.cordconfig.CordConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -147,6 +150,9 @@ public class SegmentRoutingManager implements SegmentRoutingService {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CordConfigService cordConfigService;
protected ArpHandler arpHandler = null;
protected IcmpHandler icmpHandler = null;
protected IpHandler ipHandler = null;
......@@ -163,11 +169,13 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private AppConfigHandler appCfgHandler = null;
protected XConnectHandler xConnectHandler = null;
private McastHandler mcastHandler = null;
private HostHandler hostHandler = null;
protected HostHandler hostHandler = null;
private CordConfigHandler cordConfigHandler = null;
private InternalEventHandler eventHandler = new InternalEventHandler();
private final InternalHostListener hostListener = new InternalHostListener();
private final InternalConfigListener cfgListener = new InternalConfigListener(this);
private final InternalMcastListener mcastListener = new InternalMcastListener();
private final InternalCordConfigListener cordConfigListener = new InternalCordConfigListener();
private ScheduledExecutorService executorService = Executors
.newScheduledThreadPool(1);
......@@ -324,6 +332,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
xConnectHandler = new XConnectHandler(this);
mcastHandler = new McastHandler(this);
hostHandler = new HostHandler(this);
cordConfigHandler = new CordConfigHandler(this);
cfgService.addListener(cfgListener);
cfgService.registerConfigFactory(deviceConfigFactory);
......@@ -335,6 +344,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
linkService.addListener(linkListener);
deviceService.addListener(deviceListener);
multicastRouteService.addListener(mcastListener);
cordConfigService.addListener(cordConfigListener);
// Request ARP packet-in
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
......@@ -379,6 +389,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
linkService.removeListener(linkListener);
deviceService.removeListener(deviceListener);
multicastRouteService.removeListener(mcastListener);
cordConfigService.removeListener(cordConfigListener);
processor = null;
linkListener = null;
......@@ -394,7 +405,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
log.info("Stopped");
}
@Override
public List<Tunnel> getTunnels() {
return tunnelHandler.getTunnels();
......@@ -818,6 +828,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
if (mastershipService.isLocalMaster(deviceId)) {
hostHandler.readInitialHosts(deviceId);
xConnectHandler.init(deviceId);
cordConfigHandler.init(deviceId);
DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(deviceId);
......@@ -1000,4 +1011,26 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
}
}
private class InternalCordConfigListener implements CordConfigListener {
@Override
public void event(CordConfigEvent event) {
switch (event.type()) {
case ACCESS_AGENT_ADDED:
cordConfigHandler.processAccessAgentAddedEvent(event);
break;
case ACCESS_AGENT_UPDATED:
cordConfigHandler.processAccessAgentUpdatedEvent(event);
break;
case ACCESS_AGENT_REMOVED:
cordConfigHandler.processAccessAgentRemovedEvent(event);
break;
case ACCESS_DEVICE_ADDED:
case ACCESS_DEVICE_UPDATED:
case ACCESS_DEVICE_REMOVED:
default:
break;
}
}
}
}
......
# ***** This file was auto-generated at Wed Jun 01 17:23:09 PDT 2016. Do not edit this file manually. *****
# ***** This file was auto-generated at Wed Jun 15 12:09:22 PDT 2016. Do not edit this file manually. *****
osgi_feature_group(
name = 'COMPILE',
visibility = ['PUBLIC'],
......@@ -1202,6 +1202,15 @@ remote_jar (
)
remote_jar (
name = 'cord-config',
out = 'cord-config-1.0-20160615.190726-9.jar',
url = 'https://oss.sonatype.org/content/repositories/snapshots/org/opencord/cord-config/1.0-SNAPSHOT/cord-config-1.0-20160615.190726-9.jar',
sha1 = '790bc0cf8ffe3f872370dd197cf225e76d93002d',
maven_coords = 'org.opencord:cord-config:1.0-SNAPSHOT',
visibility = [ 'PUBLIC' ],
)
remote_jar (
name = 'openstack4j-core',
out = 'openstack4j-core-2.11.jar',
url = 'mvn:org.pacesys:openstack4j-core:jar:2.11',
......
......@@ -221,6 +221,10 @@
"uri": "mvn:com.btisystems.mibbler.mibs:rfc:1.0-SNAPSHOT",
"repo": "https://oss.sonatype.org/content/repositories/snapshots"
},
"cord-config": {
"uri": "mvn:org.opencord:cord-config:1.0-SNAPSHOT",
"repo": "https://oss.sonatype.org/content/repositories/snapshots"
},
// Openstack4j related jars
"openstack4j-core": "mvn:org.pacesys:openstack4j-core:2.11",
"openstack4j-http-connector": "mvn:org.pacesys.openstack4j.connectors:openstack4j-http-connector:2.11",
......