Hyunsun Moon
Committed by Gerrit Code Review

Refactored OpenstackSwitching app

[DONE]
- Restructured to activate or deactivate switching and routing app separately
- Fixed to add or remove host when port is detected or vanished
- Use openstack node service to get integration bridges and data IP

[TODO]
- Remove use of OpenstackPortInfo
- Support installing flow rules for exising VMs
- Call security group update method when port update triggered from OpenStack

Change-Id: Ic0b2ac3f7ab07f0e20c97c6edfdd1928b9767baf
Showing 39 changed files with 483 additions and 466 deletions
......@@ -117,8 +117,8 @@ APPS = [
'//apps/mlb:onos-apps-mlb-oar',
'//apps/openstackinterface:onos-apps-openstackinterface-oar',
'//apps/openstacknetworking:onos-apps-openstacknetworking-oar',
'//apps/openstacknetworking/openstackrouting:onos-apps-openstacknetworking-openstackrouting-oar',
'//apps/openstacknetworking/openstackswitching:onos-apps-openstacknetworking-openstackswitching-oar',
'//apps/openstacknetworking/routing:onos-apps-openstacknetworking-routing-oar',
'//apps/openstacknetworking/switching:onos-apps-openstacknetworking-switching-oar',
'//apps/mobility:onos-apps-mobility-oar',
'//apps/optical:onos-apps-optical-oar',
'//apps/newoptical:onos-apps-newoptical-oar',
......
......@@ -8,5 +8,5 @@ onos_app (
category = 'Utility',
url = 'http://onosproject.org',
included_bundles = BUNDLES,
required_apps = [ 'org.onosproject.openstackinterface' ],
required_apps = [ 'org.onosproject.openstackrouting', 'org.onosproject.openstackswitching' ]
)
......
......@@ -19,14 +19,12 @@ import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.net.DeviceId;
import java.util.Collection;
import java.util.Collections;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Contains OpenstackPort Information.
*/
// TODO remove this
public final class OpenstackPortInfo {
private final Ip4Address hostIp;
private final MacAddress hostMac;
......@@ -34,7 +32,6 @@ public final class OpenstackPortInfo {
private final long vni;
private final Ip4Address gatewayIP;
private final String networkId;
private final Collection<String> securityGroups;
/**
* Returns OpenstackPortInfo reference.
......@@ -45,17 +42,15 @@ public final class OpenstackPortInfo {
* @param vni tunnel ID
* @param gatewayIP gateway IP address
* @param networkId network identifier
* @param securityGroups security group list
*/
public OpenstackPortInfo(Ip4Address hostIp, MacAddress hostMac, DeviceId deviceId, long vni,
Ip4Address gatewayIP, String networkId, Collection<String> securityGroups) {
Ip4Address gatewayIP, String networkId) {
this.hostIp = hostIp;
this.hostMac = hostMac;
this.deviceId = deviceId;
this.vni = vni;
this.gatewayIP = gatewayIP;
this.networkId = networkId;
this.securityGroups = securityGroups;
}
/**
......@@ -113,15 +108,6 @@ public final class OpenstackPortInfo {
}
/**
* Returns Security Group ID list.
*
* @return list of Security Group ID
*/
public Collection<String> securityGroups() {
return Collections.unmodifiableCollection(securityGroups);
}
/**
* Returns the builder of the OpenstackPortInfo.
*
* @return OpenstackPortInfo builder reference
......@@ -140,7 +126,6 @@ public final class OpenstackPortInfo {
private DeviceId deviceId;
private long vni;
private Ip4Address gatewayIP;
private Collection<String> securityGroups;
private String networkId;
/**
......@@ -210,23 +195,12 @@ public final class OpenstackPortInfo {
}
/**
* Sets the security group ID list.
*
* @param securityGroups security group ID list
* @return Builder reference
*/
public Builder setSecurityGroups(Collection<String> securityGroups) {
this.securityGroups = securityGroups;
return this;
}
/**
* Builds the OpenstackPortInfo reference.
*
* @return OpenstackPortInfo reference
*/
public OpenstackPortInfo build() {
return new OpenstackPortInfo(hostIp, hostMac, deviceId, vni, gatewayIP, networkId, securityGroups);
return new OpenstackPortInfo(hostIp, hostMac, deviceId, vni, gatewayIP, networkId);
}
}
}
......
......@@ -15,57 +15,19 @@
*/
package org.onosproject.openstacknetworking;
import org.onosproject.openstackinterface.OpenstackNetwork;
import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstackinterface.OpenstackSubnet;
import java.util.Map;
/**
* Handles port management REST API from Openstack for VMs.
*/
// TODO remove this
public interface OpenstackSwitchingService {
/**
* Store the port information created by Openstack.
*
* @param openstackPort port information
*/
void createPorts(OpenstackPort openstackPort);
/**
* Removes flow rules corresponding to the port removed by Openstack.
*
* @param uuid UUID
*/
void removePort(String uuid);
/**
* Updates flow rules corresponding to the port information updated by Openstack.
*
* @param openstackPort OpenStack port
*/
void updatePort(OpenstackPort openstackPort);
/**
* Stores the network information created by openstack.
*
* @param openstackNetwork network information
*/
void createNetwork(OpenstackNetwork openstackNetwork);
/**
* Stores the subnet information created by openstack.
*
* @param openstackSubnet subnet information
*/
void createSubnet(OpenstackSubnet openstackSubnet);
/**
* Retruns OpenstackPortInfo map.
*
* @return OpenstackPortInfo map
*/
// TODO remove this
Map<String, OpenstackPortInfo> openstackPortInfo();
}
......
<?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.openstacknetworking" origin="ON.Lab" version="${project.version}"
category="Utility" url="http://onosproject.org" title="OpenStack Networking App"
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}"
apps="org.onosproject.openstackswitching,org.onosproject.openstackrouting">
<description>${project.description}</description>
<artifact>mvn:${project.groupId}/onos-app-openstacknetworking-api/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-app-openstacknetworking-web/${project.version}</artifact>
</app>
<?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.
-->
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-openstacknetworking</artifactId>
<version>1.7.0-SNAPSHOT</version>
</parent>
<artifactId>onos-app-openstacknetworking-app</artifactId>
<packaging>pom</packaging>
<properties>
<onos.app.readme>Openstack Networking Application.</onos.app.readme>
</properties>
<description>SONA Openstack Networking main Application</description>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-openstackswitching</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-openstackrouting</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-openstacknetworking-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-openstacknetworking-web</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
<?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.openstackrouting" origin="ON.Lab" version="${project.version}"
category="default" url="http://onosproject.org"
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}" >
<description>${project.description}</description>
<artifact>mvn:${project.groupId}/onos-app-openstackrouting/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-app-openstacknetworking-api/${project.version}</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}/onos-app-openstackrouting/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-openstacknetworking-api/${project.version}</bundle>
</feature>
</features>
<?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.openstackswitching" origin="ON.Lab" version="${project.version}"
category="default" url="http://onosproject.org"
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}"
apps="org.onosproject.dhcp">
<description>${project.description}</description>
<artifact>mvn:${project.groupId}/onos-app-openstackswitching/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-app-openstacknetworking-api/${project.version}</artifact>
</app>
/*
* 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.openstacknetworking.switching;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.PacketService;
import org.onosproject.openstackinterface.OpenstackInterfaceService;
import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstacknetworking.OpenstackPortInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.Collection;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handles ARP packet from VMs.
*/
public class OpenstackArpHandler {
private static Logger log = LoggerFactory
.getLogger(OpenstackArpHandler.class);
private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
private PacketService packetService;
private OpenstackInterfaceService openstackService;
private HostService hostService;
/**
* Returns OpenstackArpHandler reference.
*
* @param openstackService OpenstackNetworkingService reference
* @param packetService PacketService reference
* @param hostService host service
*/
public OpenstackArpHandler(OpenstackInterfaceService openstackService, PacketService packetService,
HostService hostService) {
this.openstackService = openstackService;
this.packetService = packetService;
this.hostService = hostService;
}
/**
* Processes ARP request packets.
* It checks if the target IP is owned by a known host first and then ask to
* OpenStack if it's not. This ARP proxy does not support overlapping IP.
*
* @param pkt ARP request packet
* @param openstackPortInfoCollection collection of port information
*/
public void processPacketIn(InboundPacket pkt, Collection<OpenstackPortInfo> openstackPortInfoCollection) {
Ethernet ethRequest = pkt.parsed();
ARP arp = (ARP) ethRequest.getPayload();
if (arp.getOpCode() != ARP.OP_REQUEST) {
return;
}
IpAddress sourceIp = Ip4Address.valueOf(arp.getSenderProtocolAddress());
MacAddress srcMac = MacAddress.valueOf(arp.getSenderHardwareAddress());
OpenstackPortInfo portInfo = openstackPortInfoCollection.stream()
.filter(p -> p.ip().equals(sourceIp) && p.mac().equals(srcMac)).findFirst().orElse(null);
IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
MacAddress dstMac;
if (targetIp.equals(portInfo == null ? null : portInfo.gatewayIP())) {
dstMac = GATEWAY_MAC;
} else {
dstMac = getMacFromHostService(targetIp);
if (dstMac == null) {
dstMac = getMacFromOpenstack(targetIp);
}
}
if (dstMac == null) {
log.debug("Failed to find MAC address for {}", targetIp.toString());
return;
}
Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
dstMac,
ethRequest);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(pkt.receivedFrom().port())
.build();
packetService.emit(new DefaultOutboundPacket(
pkt.receivedFrom().deviceId(),
treatment,
ByteBuffer.wrap(ethReply.serialize())));
}
/**
* Returns MAC address of a host with a given target IP address by asking to
* OpenStack. It does not support overlapping IP.
*
* @param targetIp target ip address
* @return mac address, or null if it fails to fetch the mac
*/
private MacAddress getMacFromOpenstack(IpAddress targetIp) {
checkNotNull(targetIp);
OpenstackPort openstackPort = openstackService.ports()
.stream()
.filter(port -> port.fixedIps().containsValue(targetIp))
.findFirst()
.orElse(null);
if (openstackPort != null) {
log.debug("Found MAC from OpenStack for {}", targetIp.toString());
return openstackPort.macAddress();
} else {
return null;
}
}
/**
* Returns MAC address of a host with a given target IP address by asking to
* host service. It does not support overlapping IP.
*
* @param targetIp target ip
* @return mac address, or null if it fails to find the mac
*/
private MacAddress getMacFromHostService(IpAddress targetIp) {
checkNotNull(targetIp);
Host host = hostService.getHostsByIp(targetIp)
.stream()
.findFirst()
.orElse(null);
if (host != null) {
log.debug("Found MAC from host service for {}", targetIp.toString());
return host.mac();
} else {
return null;
}
}
}
......@@ -29,11 +29,10 @@
<packaging>pom</packaging>
<modules>
<module>app</module>
<module>web</module>
<module>api</module>
<module>openstackswitching</module>
<module>openstackrouting</module>
<module>web</module>
<module>switching</module>
<module>routing</module>
</modules>
<description>SONA Openstack Networking Application</description>
......
......@@ -4,12 +4,13 @@ COMPILE_DEPS = [
'//apps/openstackinterface/api:onos-apps-openstackinterface-api',
'//apps/openstacknetworking/api:onos-apps-openstacknetworking-api',
'//apps/scalablegateway:onos-apps-scalablegateway',
'//apps/openstacknode:onos-apps-openstacknode',
]
BUNDLES = [
'//apps/openstackinterface/api:onos-apps-openstackinterface-api',
'//apps/openstacknetworking/api:onos-apps-openstacknetworking-api',
'//apps/openstacknetworking/openstackrouting:onos-apps-openstacknetworking-openstackrouting',
'//apps/openstacknetworking/web:onos-apps-openstacknetworking-web',
'//apps/openstacknetworking/routing:onos-apps-openstacknetworking-routing',
]
osgi_jar_with_tests (
......@@ -17,9 +18,11 @@ osgi_jar_with_tests (
)
onos_app (
app_name = 'org.onosproject.openstackrouting',
title = 'OpenStack Routing App',
category = 'Utility',
url = 'http://onosproject.org',
description = 'OpenStack routing application.',
included_bundles = BUNDLES,
required_apps = [ 'org.onosproject.openstackinterface', 'org.onosproject.openstacknode', 'org.onosproject.scalablegateway' ]
)
......
......@@ -20,5 +20,6 @@
<feature>onos-api</feature>
<bundle>mvn:${project.groupId}/onos-app-openstacknetworking-api/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-openstacknetworking-web/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-openstackrouting/${project.version}</bundle>
</feature>
</features>
......
......@@ -28,6 +28,17 @@
<artifactId>onos-app-openstackrouting</artifactId>
<packaging>bundle</packaging>
<properties>
<onos.app.name>org.onosproject.openstackrouting</onos.app.name>
<onos.app.title>Openstack Switching App</onos.app.title>
<onos.app.category>Traffic Steering</onos.app.category>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.requires>
org.onosproject.openstackinterface,
org.onosproject.openstacknode
</onos.app.requires>
</properties>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
......
......@@ -3,14 +3,14 @@ COMPILE_DEPS = [
'//core/store/serializers:onos-core-serializers',
'//apps/openstackinterface/api:onos-apps-openstackinterface-api',
'//apps/openstacknetworking/api:onos-apps-openstacknetworking-api',
'//apps/openstacknode:onos-apps-openstacknode',
'//apps/dhcp/api:onos-apps-dhcp-api',
]
BUNDLES = [
'//apps/openstackinterface/api:onos-apps-openstackinterface-api',
'//apps/openstacknetworking/api:onos-apps-openstacknetworking-api',
'//apps/openstacknetworking/openstackswitching:onos-apps-openstacknetworking-openstackswitching',
'//apps/dhcp/api:onos-apps-dhcp-api',
'//apps/openstacknetworking/web:onos-apps-openstacknetworking-web',
'//apps/openstacknetworking/switching:onos-apps-openstacknetworking-switching',
]
osgi_jar_with_tests (
......@@ -18,9 +18,11 @@ osgi_jar_with_tests (
)
onos_app (
app_name = 'org.onosproject.openstackswitching',
title = 'OpenStack Switching App',
category = 'Utility',
url = 'http://onosproject.org',
description = 'OpenStack Switching application.',
included_bundles = BUNDLES,
required_apps = [ 'org.onosproject.openstackinterface', 'org.onosproject.openstacknode', 'org.onosproject.dhcp' ]
)
......
......@@ -19,6 +19,7 @@
description="${project.description}">
<feature>onos-api</feature>
<bundle>mvn:${project.groupId}/onos-app-openstacknetworking-api/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-openstacknetworking-web/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-openstackswitching/${project.version}</bundle>
</feature>
</features>
......
......@@ -28,8 +28,24 @@
<artifactId>onos-app-openstackswitching</artifactId>
<packaging>bundle</packaging>
<properties>
<onos.app.name>org.onosproject.openstackswitching</onos.app.name>
<onos.app.title>Openstack Switching App</onos.app.title>
<onos.app.category>Traffic Steering</onos.app.category>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.requires>
org.onosproject.dhcp,
org.onosproject.openstackinterface,
org.onosproject.openstacknode
</onos.app.requires>
</properties>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-openstacknetworking-api</artifactId>
<version>${project.version}</version>
......@@ -41,6 +57,11 @@
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-openstacknode</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-dhcp</artifactId>
<version>${project.version}</version>
</dependency>
......
/*
* 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.openstacknetworking.switching;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.Ip4Address;
import org.onlab.util.Tools;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Host;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.slf4j.Logger;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.openstacknetworking.switching.Constants.*;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provides abstract virtual machine handler.
*/
public abstract class AbstractVmHandler {
protected final Logger log = getLogger(getClass());
protected final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler"));
protected CoreService coreService;
protected MastershipService mastershipService;
protected HostService hostService;
protected ApplicationId appId;
protected HostListener hostListener = new InternalHostListener();
protected void activate() {
ServiceDirectory services = new DefaultServiceDirectory();
coreService = services.get(CoreService.class);
mastershipService = services.get(MastershipService.class);
hostService = services.get(HostService.class);
appId = coreService.registerApplication(Constants.APP_ID);
hostService.addListener(hostListener);
log.info("Started");
}
protected void deactivate() {
hostService.removeListener(hostListener);
eventExecutor.shutdown();
log.info("Stopped");
}
abstract void hostDetected(Host host);
abstract void hostRemoved(Host host);
protected boolean isValidHost(Host host) {
return !host.ipAddresses().isEmpty() &&
host.annotations().value(VXLAN_ID) != null &&
host.annotations().value(NETWORK_ID) != null &&
host.annotations().value(TENANT_ID) != null &&
host.annotations().value(PORT_ID) != null;
}
protected Set<Host> getVmsInDifferentCnode(Host host) {
return Tools.stream(hostService.getHosts())
.filter(h -> !h.location().deviceId().equals(host.location().deviceId()))
.filter(this::isValidHost)
.filter(h -> Objects.equals(getVni(h), getVni(host)))
.collect(Collectors.toSet());
}
protected Optional<Host> getVmByPortId(String portId) {
return Tools.stream(hostService.getHosts())
.filter(this::isValidHost)
.filter(host -> host.annotations().value(PORT_ID).equals(portId))
.findFirst();
}
protected Ip4Address getIp(Host host) {
return host.ipAddresses().stream().findFirst().get().getIp4Address();
}
protected String getVni(Host host) {
return host.annotations().value(VXLAN_ID);
}
protected String getTenantId(Host host) {
return host.annotations().value(TENANT_ID);
}
private class InternalHostListener implements HostListener {
@Override
public void event(HostEvent event) {
Host host = event.subject();
if (!mastershipService.isLocalMaster(host.location().deviceId())) {
// do not allow to proceed without mastership
return;
}
if (!isValidHost(host)) {
log.debug("Invalid host event, ignore it {}", host);
return;
}
switch (event.type()) {
case HOST_UPDATED:
case HOST_ADDED:
eventExecutor.execute(() -> hostDetected(host));
break;
case HOST_REMOVED:
eventExecutor.execute(() -> hostRemoved(host));
break;
default:
break;
}
}
}
}
/*
* 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.openstacknetworking.switching;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.IpPrefix;
/**
* Provides constants used in OpenStack node services.
*/
public final class Constants {
private Constants() {
}
public static final String APP_ID = "org.onosproject.openstackswitching";
public static final String PORTNAME_PREFIX_VM = "tap";
public static final String PORTNAME_PREFIX_ROUTER = "qr-";
public static final String PORTNAME_PREFIX_TUNNEL = "vxlan";
// TODO remove this
public static final String ROUTER_INTERFACE = "network:router_interface";
public static final String DEVICE_OWNER_GATEWAY = "network:router_gateway";
public static final Ip4Address DNS_SERVER_IP = Ip4Address.valueOf("8.8.8.8");
public static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
public static final int DHCP_INFINITE_LEASE = -1;
public static final String NETWORK_ID = "networkId";
public static final String PORT_ID = "portId";
public static final String VXLAN_ID = "vxlanId";
public static final String TENANT_ID = "tenantId";
public static final String GATEWAY_IP = "gatewayIp";
public static final String CREATE_TIME = "createTime";
public static final int SWITCHING_RULE_PRIORITY = 30000;
public static final int TUNNELTAG_RULE_PRIORITY = 30000;
public static final int ACL_RULE_PRIORITY = 30000;
}
\ No newline at end of file
/*
* 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.openstacknetworking.switching;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.util.Tools;
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.openstackinterface.OpenstackInterfaceService;
import org.onosproject.openstackinterface.OpenstackNetwork;
import org.onosproject.openstackinterface.OpenstackPort;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.Dictionary;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.openstacknetworking.switching.Constants.*;
/**
* Handles ARP packet from VMs.
*/
@Component(immediate = true)
public final class OpenstackArpHandler extends AbstractVmHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String GATEWAY_MAC = "gatewayMac";
private static final String DEFAULT_GATEWAY_MAC = "1f:1f:1f:1f:1f:1f";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackInterfaceService openstackService;
@Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC,
label = "Fake MAC address for virtual network subnet gateway")
private String gatewayMac = DEFAULT_GATEWAY_MAC;
private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
private final Set<Ip4Address> gateways = Sets.newConcurrentHashSet();
@Activate
protected void activate() {
packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
super.activate();
}
@Deactivate
protected void deactivate() {
packetService.removeProcessor(packetProcessor);
super.deactivate();
}
@Modified
protected void modified(ComponentContext context) {
Dictionary<?, ?> properties = context.getProperties();
String updatedMac;
updatedMac = Tools.get(properties, GATEWAY_MAC);
if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(gatewayMac)) {
gatewayMac = updatedMac;
}
log.info("Modified");
}
/**
* Processes ARP request packets.
* It checks if the target IP is owned by a known host first and then ask to
* OpenStack if it's not. This ARP proxy does not support overlapping IP.
*
* @param context packet context
* @param ethPacket ethernet packet
*/
private void processPacketIn(PacketContext context, Ethernet ethPacket) {
ARP arpPacket = (ARP) ethPacket.getPayload();
if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
return;
}
Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
getMacFromHostService(targetIp);
if (replyMac.equals(MacAddress.NONE)) {
replyMac = getMacFromOpenstack(targetIp);
}
if (replyMac == MacAddress.NONE) {
log.debug("Failed to find MAC address for {}", targetIp.toString());
return;
}
Ethernet ethReply = ARP.buildArpReply(
targetIp.getIp4Address(),
replyMac,
ethPacket);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(context.inPacket().receivedFrom().port())
.build();
packetService.emit(new DefaultOutboundPacket(
context.inPacket().receivedFrom().deviceId(),
treatment,
ByteBuffer.wrap(ethReply.serialize())));
}
/**
* Returns MAC address of a host with a given target IP address by asking to
* OpenStack. It does not support overlapping IP.
*
* @param targetIp target ip address
* @return mac address, or null if it fails to fetch the mac
*/
private MacAddress getMacFromOpenstack(IpAddress targetIp) {
checkNotNull(targetIp);
OpenstackPort openstackPort = openstackService.ports()
.stream()
.filter(port -> port.fixedIps().containsValue(targetIp.getIp4Address()))
.findFirst()
.orElse(null);
if (openstackPort != null) {
log.debug("Found MAC from OpenStack for {}", targetIp.toString());
return openstackPort.macAddress();
} else {
return MacAddress.NONE;
}
}
/**
* Returns MAC address of a host with a given target IP address by asking to
* host service. It does not support overlapping IP.
*
* @param targetIp target ip
* @return mac address, or null if it fails to find the mac
*/
private MacAddress getMacFromHostService(IpAddress targetIp) {
checkNotNull(targetIp);
Host host = hostService.getHostsByIp(targetIp)
.stream()
.findFirst()
.orElse(null);
if (host != null) {
log.debug("Found MAC from host service for {}", targetIp.toString());
return host.mac();
} else {
return MacAddress.NONE;
}
}
@Override
protected void hostDetected(Host host) {
OpenstackNetwork osNet = openstackService.network(host.annotations().value(NETWORK_ID));
if (osNet == null) {
log.warn("Failed to get OpenStack network for {}", host);
return;
}
osNet.subnets().stream()
.forEach(subnet -> gateways.add(Ip4Address.valueOf(subnet.gatewayIp())));
}
@Override
protected void hostRemoved(Host host) {
// TODO remove subnet gateway from gateways if no hosts exists on that subnet
}
private class InternalPacketProcessor implements PacketProcessor {
@Override
public void process(PacketContext context) {
if (context.isHandled()) {
return;
}
Ethernet ethPacket = context.inPacket().parsed();
if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
return;
}
processPacketIn(context, ethPacket);
}
}
}
......@@ -15,16 +15,7 @@
*/
package org.onosproject.openstacknetworking.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstackinterface.web.OpenstackPortCodec;
import org.onosproject.openstacknetworking.OpenstackPortInfo;
import org.onosproject.openstacknetworking.OpenstackRoutingService;
import org.onosproject.openstacknetworking.OpenstackSwitchingService;
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
......@@ -43,47 +34,16 @@ import java.io.InputStream;
@Path("ports")
public class OpenstackPortWebResource extends AbstractWebResource {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String PORTNAME_PREFIX_VM = "tap";
private static final OpenstackPortCodec PORT_CODEC = new OpenstackPortCodec();
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createPorts(InputStream input) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectNode portNode = (ObjectNode) mapper.readTree(input);
OpenstackPort openstackPort = PORT_CODEC.decode(portNode, this);
OpenstackSwitchingService switchingService =
getService(OpenstackSwitchingService.class);
switchingService.createPorts(openstackPort);
log.debug("REST API ports is called with {}", portNode.toString());
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Creates Port failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
}
return Response.status(Response.Status.OK).build();
}
@Path("{portUUID}")
@DELETE
public Response deletePorts(@PathParam("portUUID") String id) {
OpenstackSwitchingService switchingService =
getService(OpenstackSwitchingService.class);
OpenstackPortInfo portInfo = switchingService.openstackPortInfo()
.get(PORTNAME_PREFIX_VM.concat(id.substring(0, 11)));
OpenstackRoutingService routingService =
getService(OpenstackRoutingService.class);
routingService.checkDisassociatedFloatingIp(id, portInfo);
switchingService.removePort(id);
return Response.noContent().build();
}
......@@ -92,23 +52,7 @@ public class OpenstackPortWebResource extends AbstractWebResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updatePorts(InputStream input) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectNode portNode = (ObjectNode) mapper.readTree(input);
OpenstackPort openstackPort = PORT_CODEC.decode(portNode, this);
OpenstackSwitchingService switchingService =
getService(OpenstackSwitchingService.class);
switchingService.updatePort(openstackPort);
log.debug("REST API update port is called with {}", portNode.toString());
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Update Port failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
}
// TODO call security group update here
return Response.status(Response.Status.OK).build();
}
}
......
......@@ -16,5 +16,5 @@ onos_app (
category = 'Utility',
url = 'http://onosproject.org',
description = 'SONA Openstack Node Bootstrap Application.',
required_app = [ 'org.onosproject.ovsdb-base' ],
required_apps = [ 'org.onosproject.ovsdb-base', 'org.onosproject.drivers.ovsdb' ]
)
......
......@@ -38,7 +38,8 @@
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.readme>SONA Openstack Node Bootstrap Application</onos.app.readme>
<onos.app.requires>
org.onosproject.ovsdb-base
org.onosproject.ovsdb-base,
org.onosproject.drivers.ovsdb
</onos.app.requires>
</properties>
......
......@@ -242,7 +242,7 @@ public final class OpenstackNodeManager implements OpenstackNodeService {
deviceService.removeListener(deviceListener);
nodeStore.removeListener(nodeStoreListener);
componentConfigService.unregisterProperties(getClass(), true);
componentConfigService.unregisterProperties(getClass(), false);
configRegistry.unregisterConfigFactory(configFactory);
leadershipService.withdraw(appId.name());
......
......@@ -54,8 +54,8 @@ APPS = [
'//apps/mlb:onos-apps-mlb-oar',
'//apps/openstackinterface:onos-apps-openstackinterface-oar',
'//apps/openstacknetworking:onos-apps-openstacknetworking-oar',
'//apps/openstacknetworking/openstackrouting:onos-apps-openstacknetworking-openstackrouting-oar',
'//apps/openstacknetworking/openstackswitching:onos-apps-openstacknetworking-openstackswitching-oar',
'//apps/openstacknetworking/routing:onos-apps-openstacknetworking-routing-oar',
'//apps/openstacknetworking/switching:onos-apps-openstacknetworking-switching-oar',
'//apps/mobility:onos-apps-mobility-oar',
'//apps/optical:onos-apps-optical-oar',
'//apps/pathpainter:onos-apps-pathpainter-oar',
......