ONOS-5182 Refactored SONA to cache network states
Change-Id: Ib316fa5fa5d36e9da370a1578ac55de4a8dd9b04
diff --git a/apps/openstacknetworking/BUCK b/apps/openstacknetworking/BUCK
index e44142f..519f134 100644
--- a/apps/openstacknetworking/BUCK
+++ b/apps/openstacknetworking/BUCK
@@ -6,10 +6,8 @@
'//utils/rest:onlab-rest',
'//cli:onos-cli',
'//apps/openstacknode:onos-apps-openstacknode',
- '//apps/openstackinterface/api:onos-apps-openstackinterface-api',
- '//apps/openstackinterface/app:onos-apps-openstackinterface-app',
'//apps/scalablegateway:onos-apps-scalablegateway',
- '//apps/dhcp/api:onos-apps-dhcp-api',
+ '//lib:openstack4j-core',
]
osgi_jar_with_tests (
@@ -28,9 +26,7 @@
url = 'http://onosproject.org',
description = 'OpenStack Networking application.',
required_apps = [
- 'org.onosproject.dhcp',
'org.onosproject.openstacknode',
- 'org.onosproject.openstackinterface',
'org.onosproject.scalablegateway'
]
)
diff --git a/apps/openstacknetworking/pom.xml b/apps/openstacknetworking/pom.xml
index e9569cc..afbe2ea 100644
--- a/apps/openstacknetworking/pom.xml
+++ b/apps/openstacknetworking/pom.xml
@@ -35,9 +35,7 @@
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.readme>OpenStack networking application</onos.app.readme>
<onos.app.requires>
- org.onosproject.dhcp,
org.onosproject.openstacknode,
- org.onosproject.openstacinterface,
org.onosproject.scalablegateway
</onos.app.requires>
<web.context>/onos/openstacknetworking</web.context>
@@ -66,6 +64,14 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-rest</artifactId>
<version>${project.version}</version>
@@ -98,26 +104,6 @@
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-app-dhcp-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-app-dhcp</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-app-openstackinterface-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-app-openstackinterface-app</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.onosproject</groupId>
<artifactId>onos-app-openstacknode</artifactId>
<version>${project.version}</version>
</dependency>
@@ -164,6 +150,21 @@
<groupId>org.onosproject</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.pacesys</groupId>
+ <artifactId>openstack4j-core</artifactId>
+ <version>2.11</version>
+ </dependency>
+ <dependency>
+ <groupId>org.pacesys.openstack4j.connectors</groupId>
+ <artifactId>openstack4j-http-connector</artifactId>
+ <version>2.11</version>
+ </dependency>
+ <dependency>
+ <groupId>org.pacesys.openstack4j.connectors</groupId>
+ <artifactId>openstack4j-httpclient</artifactId>
+ <version>2.11</version>
+ </dependency>
</dependencies>
<build>
@@ -206,6 +207,17 @@
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Web-ContextPath>${web.context}</Web-ContextPath>
+ <Import-Package>
+ !org.apache.http.*,
+ !com.fasterxml.jackson.dataformat.*,
+ !javax.annotation,
+ *,org.glassfish.jersey.servlet
+ </Import-Package>
+ <Embed-Dependency>
+ openstack4j-core,
+ openstack4j-http-connector,
+ openstack4j-httpclient
+ </Embed-Dependency>
</instructions>
</configuration>
</plugin>
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index 4ba121a..00e91b0 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -15,9 +15,6 @@
*/
package org.onosproject.openstacknetworking.api;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
/**
@@ -28,42 +25,18 @@
private Constants() {
}
- public static final String SWITCHING_APP_ID = "org.onosproject.openstackswitching";
- public static final String ROUTING_APP_ID = "org.onosproject.openstackrouting";
-
- public static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
- public static final String DEVICE_OWNER_ROUTER_GATEWAY = "network:router_gateway";
- public static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
-
- public static final String PORT_NAME_PREFIX_VM = "tap";
- public static final String PORT_NAME_PREFIX_TUNNEL = "vxlan";
+ public static final String OPENSTACK_NETWORKING_APP_ID = "org.onosproject.openstacknetworking";
public static final String DEFAULT_GATEWAY_MAC_STR = "fe:00:00:00:00:02";
public static final MacAddress DEFAULT_GATEWAY_MAC = MacAddress.valueOf(DEFAULT_GATEWAY_MAC_STR);
- // TODO make this configurable
public static final MacAddress DEFAULT_EXTERNAL_ROUTER_MAC = MacAddress.valueOf("fe:00:00:00:00:01");
- 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 SUBNET_ID = "subnetId";
- 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;
- public static final int EW_ROUTING_RULE_PRIORITY = 28000;
-
- public static final int GATEWAY_ICMP_PRIORITY = 43000;
- public static final int ROUTING_RULE_PRIORITY = 25000;
- public static final int FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY = 42000;
- public static final int FLOATING_RULE_PRIORITY = 41000;
- public static final int PNAT_RULE_PRIORITY = 26000;
- public static final int PNAT_TIMEOUT = 120;
+ public static final int PRIORITY_TUNNEL_TAG_RULE = 30000;
+ public static final int PRIORITY_FLOATING_INTERNAL = 42000;
+ public static final int PRIORITY_FLOATING_EXTERNAL = 41000;
+ public static final int PRIORITY_ICMP_RULE = 43000;
+ public static final int PRIORITY_INTERNAL_ROUTING_RULE = 28000;
+ public static final int PRIORITY_EXTERNAL_ROUTING_RULE = 25000;
+ public static final int PRIORITY_SNAT_RULE = 26000;
+ public static final int PRIORITY_SWITCHING_RULE = 30000;
}
\ No newline at end of file
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java
new file mode 100644
index 0000000..97a112b
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Representation of virtual instance port.
+ */
+public interface InstancePort {
+
+ /**
+ * Returns the OpenStack network ID of the instance port.
+ *
+ * @return openstack network id
+ */
+ String networkId();
+
+ /**
+ * Returns the OpenStack port ID of a given host.
+ *
+ * @return openstack port id
+ */
+ String portId();
+
+ /**
+ * Returns the MAC address of the instance port.
+ *
+ * @return mac address
+ */
+ MacAddress macAddress();
+
+ /**
+ * Returns the IP address of the instance port.
+ *
+ * @return ip address
+ */
+ IpAddress ipAddress();
+
+ /**
+ * Returns the device ID of the instance port.
+ *
+ * @return device id
+ */
+ DeviceId deviceId();
+
+ /**
+ * Returns the port number of the instance port.
+ *
+ * @return port number
+ */
+ PortNumber portNumber();
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java
new file mode 100644
index 0000000..945772e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes virtual instance port event.
+ */
+public class InstancePortEvent extends AbstractEvent<InstancePortEvent.Type, InstancePort> {
+
+ public enum Type {
+
+ /**
+ * Signifies that a new instance port is detected.
+ */
+ OPENSTACK_INSTANCE_PORT_DETECTED,
+
+ /**
+ * Signifies that the instance port is updated.
+ */
+ OPENSTACK_INSTANCE_PORT_UPDATED,
+
+ /**
+ * Signifies that the instance port is disabled.
+ */
+ OPENSTACK_INSTANCE_PORT_VANISHED
+ }
+
+ /**
+ * Creates an event of a given type for the specified instance port.
+ *
+ * @param type instance port event type
+ * @param instPort instance port
+ */
+ public InstancePortEvent(Type type, InstancePort instPort) {
+ super(type, instPort);
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortListener.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortListener.java
new file mode 100644
index 0000000..708a9dd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for instance port event.
+ */
+public interface InstancePortListener extends EventListener<InstancePortEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
new file mode 100644
index 0000000..91c8183
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.event.ListenerService;
+
+import java.util.Set;
+
+/**
+ * Service for interacting with the inventory of instance ports.
+ */
+public interface InstancePortService
+ extends ListenerService<InstancePortEvent, InstancePortListener> {
+
+ /**
+ * Returns instance port with the given MAC address.
+ *
+ * @param macAddress mac address
+ * @return instance port; null if not found
+ */
+ InstancePort instancePort(MacAddress macAddress);
+
+ /**
+ * Returns instance port with the given IP address in the given OpenStack network.
+ *
+ * @param ipAddress ip address
+ * @param osNetId openstack network id
+ * @return instance port; null if not found
+ */
+ InstancePort instancePort(IpAddress ipAddress, String osNetId);
+
+ /**
+ * Returns instance port with the given openstack port ID.
+ *
+ * @param osPortId openstack port id
+ * @return instance port; null if not found
+ */
+ InstancePort instancePort(String osPortId);
+
+ /**
+ * Returns all instance ports.
+ *
+ * @return set of instance ports; empty list if no port exists
+ */
+ Set<InstancePort> instancePorts();
+
+ /**
+ * Returns instance ports in the given OpenStack network.
+ *
+ * @param osNetId openstack network
+ * @return set of instance ports; empty list if no port exists
+ */
+ Set<InstancePort> instancePorts(String osNetId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java
deleted file mode 100644
index 9988a4e..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.api;
-
-import org.onosproject.net.Host;
-
-import org.onosproject.openstackinterface.OpenstackFloatingIP;
-
-/**
- * Handles floating IP update requests from OpenStack.
- */
-public interface OpenstackFloatingIpService {
-
- enum Action {
- ASSOCIATE,
- DISSASSOCIATE
- }
-
- /**
- * Handles floating IP create request from OpenStack.
- *
- * @param floatingIp floating IP information
- */
- void createFloatingIp(OpenstackFloatingIP floatingIp);
-
- /**
- * Handles floating IP update request from OpenStack.
- *
- * @param floatingIp floating IP information
- */
- void updateFloatingIp(OpenstackFloatingIP floatingIp);
-
- /**
- * Handles floating IP remove request from OpenStack.
- *
- * @param floatingIpId floating ip identifier
- */
- void deleteFloatingIp(String floatingIpId);
-
- /**
- * Handles to purge data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void purgeVmFlow(Host host);
-
- /**
- * Handles to reinstall data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java
new file mode 100644
index 0000000..5ebb559
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+/**
+ * Service for administering the inventory of OpenStack network, subnet and port.
+ */
+public interface OpenstackNetworkAdminService {
+
+ /**
+ * Creates a network with the given information.
+ *
+ * @param network the new network
+ */
+ void createNetwork(Network network);
+
+ /**
+ * Updates the network with the given information.
+ *
+ * @param network the updated network
+ */
+ void updateNetwork(Network network);
+
+ /**
+ * Removes the network with the given network id.
+ *
+ * @param networkId network id
+ */
+ void removeNetwork(String networkId);
+
+ /**
+ * Creates a subnet with the given information.
+ *
+ * @param subnet the new subnet
+ */
+ void createSubnet(Subnet subnet);
+
+ /**
+ * Updates a subnet with the given information.
+ *
+ * @param subnet the updated subnet
+ */
+ void updateSubnet(Subnet subnet);
+
+ /**
+ * Removes the subnet with the given subnet id.
+ *
+ * @param subnetId subnet id
+ */
+ void removeSubnet(String subnetId);
+
+ /**
+ * Creates a port with the given information.
+ *
+ * @param port the new port
+ */
+ void createPort(Port port);
+
+ /**
+ * Updates the port with the given information.
+ *
+ * @param port the updated port
+ */
+ void updatePort(Port port);
+
+ /**
+ * Removes the port with the given port id.
+ *
+ * @param portId port id
+ */
+ void removePort(String portId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
new file mode 100644
index 0000000..c022c29
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.joda.time.LocalDateTime;
+import org.onosproject.event.AbstractEvent;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Describes OpenStack network service event.
+ */
+public class OpenstackNetworkEvent extends AbstractEvent<OpenstackNetworkEvent.Type, Network> {
+
+ private final Port port;
+ private final Subnet subnet;
+
+ public enum Type {
+ /**
+ * Signifies that a new OpenStack network is created.
+ */
+ OPENSTACK_NETWORK_CREATED,
+
+ /**
+ * Signifies that the OpenStack network is updated.
+ */
+ OPENSTACK_NETWORK_UPDATED,
+
+ /**
+ * Signifies that the OpenStack network is removed.
+ */
+ OPENSTACK_NETWORK_REMOVED,
+
+ /**
+ * Signifies that a new OpenStack subnet is created.
+ */
+ OPENSTACK_SUBNET_CREATED,
+
+ /**
+ * Signifies that the OpenStack subnet is updated.
+ */
+ OPENSTACK_SUBNET_UPDATED,
+
+ /**
+ * Signifies that the OpenStack subnet is removed.
+ */
+ OPENSTACK_SUBNET_REMOVED,
+
+ /**
+ * Signifies that a new OpenStack port is created.
+ */
+ OPENSTACK_PORT_CREATED,
+
+ /**
+ * Signifies that the OpenStack port is updated.
+ */
+ OPENSTACK_PORT_UPDATED,
+
+ /**
+ * Signifies that the OpenStack port is removed.
+ */
+ OPENSTACK_PORT_REMOVED
+ }
+
+ /**
+ * Creates an event of a given type for the specified network and the current time.
+ * @param type openstack network event type
+ * @param network openstack network
+ */
+ public OpenstackNetworkEvent(Type type, Network network) {
+ super(type, network);
+ this.port = null;
+ this.subnet = null;
+ }
+
+ /**
+ * Creates an event of a given type for the specified network, port and the
+ * current time.
+ *
+ * @param type openstack network event type
+ * @param network openstack network
+ * @param port openstack port
+ */
+ public OpenstackNetworkEvent(Type type, Network network, Port port) {
+ super(type, network);
+ this.port = port;
+ this.subnet = null;
+ }
+
+ /**
+ * Creates an event of a given type for the specified network, subnet and the
+ * current time.
+ *
+ * @param type openstack network event type
+ * @param network openstack network
+ * @param subnet openstack subnet
+ */
+ public OpenstackNetworkEvent(Type type, Network network, Subnet subnet) {
+ super(type, network);
+ this.port = null;
+ this.subnet = subnet;
+ }
+
+ /**
+ * Returns the port of the network event.
+ *
+ * @return openstack port; null if the event is not port specific
+ */
+ public Port port() {
+ return port;
+ }
+
+ /**
+ * Returns the subnet of the network event.
+ *
+ * @return openstack subnet; null if the event is not subnet specific
+ */
+ public Subnet subnet() {
+ return subnet;
+ }
+
+ @Override
+ public String toString() {
+ if (port == null && subnet == null) {
+ return super.toString();
+ }
+ return toStringHelper(this)
+ .add("time", new LocalDateTime(time()))
+ .add("type", type())
+ .add("network", subject())
+ .add("port", port)
+ .add("subnet", subnet)
+ .toString();
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkListener.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkListener.java
new file mode 100644
index 0000000..1e824da
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for OpenStack network event.
+ */
+public interface OpenstackNetworkListener extends EventListener<OpenstackNetworkEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
new file mode 100644
index 0000000..78ac42d
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.event.ListenerService;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+import java.util.Set;
+
+/**
+ * Service for interacting with the inventory of OpenStack network and port.
+ */
+public interface OpenstackNetworkService
+ extends ListenerService<OpenstackNetworkEvent, OpenstackNetworkListener> {
+ /**
+ * Returns the network with the supplied network ID.
+ *
+ * @param networkId network id
+ * @return openstack network
+ */
+ Network network(String networkId);
+
+ /**
+ * Returns all networks registered in the service.
+ *
+ * @return set of networks
+ */
+ Set<Network> networks();
+
+ /**
+ * Returns the subnet with the supplied subnet ID.
+ *
+ * @param subnetId subnet id
+ * @return subnet
+ */
+ Subnet subnet(String subnetId);
+
+ /**
+ * Returns all subnets registered in the service.
+ *
+ * @return set of subnet
+ */
+ Set<Subnet> subnets();
+
+ /**
+ * Returns all subnets associated with the supplied network.
+ *
+ * @param networkId network id
+ * @return set of subnet
+ */
+ Set<Subnet> subnets(String networkId);
+
+ /**
+ * Returns the OpenStack port with the supplied port ID.
+ *
+ * @param portId openstack port id
+ * @return openstack port
+ */
+ Port port(String portId);
+
+ /**
+ * Returns the OpenStack port with the supplied ONOS port.
+ *
+ * @param port onos port
+ * @return openstack port
+ */
+ Port port(org.onosproject.net.Port port);
+
+ /**
+ * Returns all OpenStack ports registered in the service.
+ *
+ * @return set of ports
+ */
+ Set<Port> ports();
+
+ /**
+ * Returns all OpenStack ports associated with the supplied network.
+ * @param networkId network id
+ * @return set of ports
+ */
+ Set<Port> ports(String networkId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
new file mode 100644
index 0000000..86f338fe
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.store.Store;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of OpenStack network states; not intended for direct use.
+ */
+public interface OpenstackNetworkStore
+ extends Store<OpenstackNetworkEvent, OpenstackNetworkStoreDelegate> {
+
+ /**
+ * Creates the new network.
+ *
+ * @param network openstack network
+ */
+ void createNetwork(Network network);
+
+ /**
+ * Updates the network.
+ *
+ * @param network openstack network
+ */
+ void updateNetwork(Network network);
+
+ /**
+ * Removes the network with the given network id.
+ *
+ * @param networkId network id
+ * @return removed openstack network; null if failed
+ */
+ Network removeNetwork(String networkId);
+
+ /**
+ * Returns the network with the given network id.
+ *
+ * @param networkId network id
+ * @return network; null if not found
+ */
+ Network network(String networkId);
+
+ /**
+ * Returns all networks.
+ *
+ * @return set of networks
+ */
+ Set<Network> networks();
+
+ /**
+ * Creates a subnet with the given information.
+ *
+ * @param subnet the new subnet
+ */
+ void createSubnet(Subnet subnet);
+
+ /**
+ * Updates a subnet with the given information.
+ *
+ * @param subnet the updated subnet
+ */
+ void updateSubnet(Subnet subnet);
+
+ /**
+ * Removes the subnet with the given subnet id.
+ *
+ * @param subnetId subnet id
+ * @return removed subnet; null if failed
+ */
+ Subnet removeSubnet(String subnetId);
+
+ /**
+ * Returns the subnet with the supplied subnet ID.
+ *
+ * @param subnetId subnet id
+ * @return subnet
+ */
+ Subnet subnet(String subnetId);
+
+ /**
+ * Returns all subnets registered in the service.
+ *
+ * @return set of subnet
+ */
+ Set<Subnet> subnets();
+
+ /**
+ * Creates the new port.
+ *
+ * @param port the new port
+ */
+ void createPort(Port port);
+
+ /**
+ * Updates the port.
+ *
+ * @param port port
+ */
+ void updatePort(Port port);
+
+ /**
+ * Removes the port.
+ *
+ * @param portId port id
+ * @return removed port; null if failed
+ */
+ Port removePort(String portId);
+
+ /**
+ * Returns the port with the given port id.
+ *
+ * @param portId port id
+ * @return port
+ */
+ Port port(String portId);
+
+ /**
+ * Returns all ports.
+ *
+ * @return set of ports
+ */
+ Set<Port> ports();
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStoreDelegate.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStoreDelegate.java
new file mode 100644
index 0000000..ba8ce56
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * OpenStack network store delegate abstraction.
+ */
+public interface OpenstackNetworkStoreDelegate extends StoreDelegate<OpenstackNetworkEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java
new file mode 100644
index 0000000..5870eb0
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+/**
+ * Service for administering the inventory of OpenStack router and floating IP.
+ */
+public interface OpenstackRouterAdminService {
+
+ /**
+ * Creates a router with the given information.
+ *
+ * @param osRouter the new router
+ */
+ void createRouter(Router osRouter);
+
+ /**
+ * Updates the router with the given information.
+ *
+ * @param osRouter updated router
+ */
+ void updateRouter(Router osRouter);
+
+ /**
+ * Removes the router with the given router ID.
+ *
+ * @param osRouterId router id
+ */
+ void removeRouter(String osRouterId);
+
+ /**
+ * Adds a new interface to the router.
+ *
+ * @param osRouterIface openstack router interface
+ */
+ void addRouterInterface(RouterInterface osRouterIface);
+
+ /**
+ * Updates the router interface.
+ *
+ * @param osRouterIface openstack router interface
+ */
+ void updateRouterInterface(RouterInterface osRouterIface);
+
+ /**
+ * Removes the router interface.
+ *
+ * @param osRouterIfaceId openstack router interface id
+ */
+ void removeRouterInterface(String osRouterIfaceId);
+
+ /**
+ * Creates a floating IP with the given information.
+ *
+ * @param floatingIP the new floating ip
+ */
+ void createFloatingIp(NetFloatingIP floatingIP);
+
+ /**
+ * Updates the floating IP with the given information.
+ *
+ * @param floatingIP the updated floating ip
+ */
+ void updateFloatingIp(NetFloatingIP floatingIP);
+
+ /**
+ * Removes the floating IP with the given floating IP ID.
+ *
+ * @param floatingIpId floating ip id
+ */
+ void removeFloatingIp(String floatingIpId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterEvent.java
new file mode 100644
index 0000000..4cf4d99
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterEvent.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.joda.time.LocalDateTime;
+import org.onosproject.event.AbstractEvent;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Describes OpenStack router service events.
+ */
+public class OpenstackRouterEvent extends AbstractEvent<OpenstackRouterEvent.Type, Router> {
+
+ private final ExternalGateway exGateway;
+ private final RouterInterface routerIface;
+ private final NetFloatingIP floatingIP;
+ private final String portId;
+
+ public enum Type {
+
+ /**
+ * Signifies that a new OpenStack router is created.
+ */
+ OPENSTACK_ROUTER_CREATED,
+
+ /**
+ * Signifies that the OpenStack router is updated.
+ */
+ OPENSTACK_ROUTER_UPDATED,
+
+ /**
+ * Signifies that the OpenStack router is removed.
+ */
+ OPENSTACK_ROUTER_REMOVED,
+
+ /**
+ * Signifies that the external gateway is added to the router.
+ */
+ OPENSTACK_ROUTER_GATEWAY_ADDED,
+
+ /**
+ * Signifies that the external gateway is removed from the router.
+ */
+ OPENSTACK_ROUTER_GATEWAY_REMOVED,
+
+ /**
+ * Signifies that the OpenStack router interface is added.
+ */
+ OPENSTACK_ROUTER_INTERFACE_ADDED,
+
+ /**
+ * Signifies that the OpenStack router interface is updated.
+ */
+ OPENSTACK_ROUTER_INTERFACE_UPDATED,
+
+ /**
+ * Signifies that the OpenStack router interface is removed.
+ */
+ OPENSTACK_ROUTER_INTERFACE_REMOVED,
+
+ /**
+ * Signifies that a new floating IP is created.
+ */
+ OPENSTACK_FLOATING_IP_CREATED,
+
+ /**
+ * Signifies that the floating IP is updated.
+ */
+ OPENSTACK_FLOATING_IP_UPDATED,
+
+ /**
+ * Signifies that the floating IP is removed.
+ */
+ OPENSTACK_FLOATING_IP_REMOVED,
+
+ /**
+ * Signifies that the floating IP is associated to a fixed IP.
+ */
+ OPENSTACK_FLOATING_IP_ASSOCIATED,
+
+ /**
+ * Signifies that the floating IP disassociated from the fixed IP.
+ */
+ OPENSTACK_FLOATING_IP_DISASSOCIATED
+ }
+
+ /**
+ * Creates an event of a given type for the specified router and the current time.
+ *
+ * @param type openstack router event type
+ * @param osRouter openstack router
+ */
+ public OpenstackRouterEvent(Type type, Router osRouter) {
+ super(type, osRouter);
+ this.exGateway = null;
+ this.routerIface = null;
+ this.floatingIP = null;
+ this.portId = null;
+ }
+
+ /**
+ * Creates an event of a given type for the specified router, external gateway and
+ * the current time.
+ *
+ * @param type openstack router event type
+ * @param osRouter openstack router
+ * @param exGateway openstack router external gateway
+ */
+ public OpenstackRouterEvent(Type type, Router osRouter, ExternalGateway exGateway) {
+ super(type, osRouter);
+ this.exGateway = exGateway;
+ this.routerIface = null;
+ this.floatingIP = null;
+ this.portId = null;
+ }
+
+ /**
+ * Creates an event of a given type for the specified router, floating IP and
+ * the current time.
+ *
+ * @param type openstack router event type
+ * @param osRouter openstack router
+ * @param osRouterIface openstack router interface
+ */
+ public OpenstackRouterEvent(Type type, Router osRouter, RouterInterface osRouterIface) {
+ super(type, osRouter);
+ this.exGateway = null;
+ this.routerIface = osRouterIface;
+ this.floatingIP = null;
+ this.portId = null;
+ }
+
+ /**
+ * Creates an event of a given type for the specified router, floating IP and
+ * the current time.
+ *
+ * @param type openstack router event type
+ * @param router openstack router
+ * @param floatingIP openstack floating ip
+ */
+ public OpenstackRouterEvent(Type type, Router router, NetFloatingIP floatingIP) {
+ super(type, router);
+ this.exGateway = null;
+ this.routerIface = null;
+ this.floatingIP = floatingIP;
+ this.portId = null;
+ }
+
+ /**
+ * Creates an event of a given type for the specified router, floating IP,
+ * associated OpenStack port ID and the current time.
+ *
+ * @param type openstack router event type
+ * @param router openstack router
+ * @param floatingIP openstack floating ip
+ * @param portId associated openstack port id
+ */
+ public OpenstackRouterEvent(Type type, Router router, NetFloatingIP floatingIP,
+ String portId) {
+ super(type, router);
+ this.exGateway = null;
+ this.routerIface = null;
+ this.floatingIP = floatingIP;
+ this.portId = portId;
+ }
+
+ /**
+ * Returns the router external gateway object of the router event.
+ *
+ * @return openstack router external gateway; null if the event is not
+ * relevant to the router external gateway
+ */
+ public ExternalGateway externalGateway() {
+ return exGateway;
+ }
+
+ /**
+ * Returns the router interface object of the router event.
+ *
+ * @return openstack router interface; null if the event is not relevant to
+ * the router interface
+ */
+ public RouterInterface routerIface() {
+ return routerIface;
+ }
+
+ /**
+ * Returns the floating IP of the router event.
+ *
+ * @return openstack floating ip; null if the event is not relevant to
+ * the floating ip
+ */
+ public NetFloatingIP floatingIp() {
+ return floatingIP;
+ }
+
+ /**
+ * Returns the associated port ID of the floating IP.
+ *
+ * @return openstack port id; null if the event is not relevant to the
+ * floating ip
+ */
+ public String portId() {
+ return portId;
+ }
+
+ @Override
+ public String toString() {
+ if (floatingIP == null) {
+ return super.toString();
+ }
+ return toStringHelper(this)
+ .add("time", new LocalDateTime(time()))
+ .add("type", type())
+ .add("router", subject())
+ .add("externalGateway", exGateway)
+ .add("routerIface", routerIface)
+ .add("floatingIp", floatingIP)
+ .add("portId", portId)
+ .toString();
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterListener.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterListener.java
new file mode 100644
index 0000000..2442d79
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for OpenStack router event.
+ */
+public interface OpenstackRouterListener extends EventListener<OpenstackRouterEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java
new file mode 100644
index 0000000..75c1378
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java
@@ -0,0 +1,90 @@
+/*
+ * 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.api;
+
+import org.onosproject.event.ListenerService;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+import java.util.Set;
+
+/**
+ * Handles router update requests from OpenStack.
+ */
+public interface OpenstackRouterService
+ extends ListenerService<OpenstackRouterEvent, OpenstackRouterListener> {
+
+ /**
+ * Returns the router with the supplied router ID.
+ *
+ * @param osRouterId openstack router id
+ * @return openstack router
+ */
+ Router router(String osRouterId);
+
+ /**
+ * Returns all routers.
+ *
+ * @return set of openstack routers
+ */
+ Set<Router> routers();
+
+ /**
+ * Returns the router interface with the given ID.
+ *
+ * @param osRouterIfaceId openstack router interface port id
+ * @return openstack router interface
+ */
+ RouterInterface routerInterface(String osRouterIfaceId);
+
+ /**
+ * Returns all router interfaces.
+ *
+ * @return set of openstack router interfaces
+ */
+ Set<RouterInterface> routerInterfaces();
+
+ /**
+ * Returns all router interfaces with the router ID.
+ *
+ * @param osRouterId openstack router id
+ * @return set of router interfaces
+ */
+ Set<RouterInterface> routerInterfaces(String osRouterId);
+
+ /**
+ * Returns the floating IP with the supplied floating IP ID.
+ * @param floatingIpId floating ip id
+ * @return openstack floating ip
+ */
+ NetFloatingIP floatingIp(String floatingIpId);
+
+ /**
+ * Returns all floating IPs.
+ *
+ * @return set of openstack floating ips
+ */
+ Set<NetFloatingIP> floatingIps();
+
+ /**
+ * Returns all floating IPs associated with the router ID.
+ *
+ * @param routerId router id
+ * @return set of openstack floating ips
+ */
+ Set<NetFloatingIP> floatingIps(String routerId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java
new file mode 100644
index 0000000..b0744a0
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.store.Store;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of OpenStack router states; not intended for direct use.
+ */
+public interface OpenstackRouterStore
+ extends Store<OpenstackRouterEvent, OpenstackRouterStoreDelegate> {
+
+ /**
+ * Creates a new router.
+ *
+ * @param osRouter openstack router
+ */
+ void createRouter(Router osRouter);
+
+ /**
+ * Updates the router.
+ *
+ * @param osRouter updated openstack router
+ */
+ void updateRouter(Router osRouter);
+
+ /**
+ * Removes the router with the supplied ID.
+ *
+ * @param osRouterId openstack router id
+ * @return removed router; null if failed
+ */
+ Router removeRouter(String osRouterId);
+
+ /**
+ * Returns the router with the supplied router ID.
+ *
+ * @param osRouterId openstack router id
+ * @return openstack router; null if not found
+ */
+ Router router(String osRouterId);
+
+ /**
+ * Returns all routers.
+ *
+ * @return set of openstack router
+ */
+ Set<Router> routers();
+
+ /**
+ * Adds a new interface to the router.
+ *
+ * @param osRouterIface openstack router interface
+ */
+ void addRouterInterface(RouterInterface osRouterIface);
+
+ /**
+ * Updates the router interface.
+ *
+ * @param osRouterIface openstack router interface
+ */
+ void updateRouterInterface(RouterInterface osRouterIface);
+
+ /**
+ * Removes the router interface.
+ *
+ * @param osRouterIfaceId openstack router interface id
+ * @return removed router interface; null if failed
+ */
+ RouterInterface removeRouterInterface(String osRouterIfaceId);
+
+ /**
+ * Returns the router interface with the given ID.
+ *
+ * @param osRouterIfaceId openstack router interface port id
+ * @return openstack router interface
+ */
+ RouterInterface routerInterface(String osRouterIfaceId);
+
+ /**
+ * Returns all router interfaces.
+ *
+ * @return set of openstack router interfaces
+ */
+ Set<RouterInterface> routerInterfaces();
+
+ /**
+ * Creates a new floating IP address.
+ *
+ * @param floatingIp openstack floating ip
+ */
+ void createFloatingIp(NetFloatingIP floatingIp);
+
+ /**
+ * Updates the floating IP address.
+ *
+ * @param floatingIp updated openstack floating ip
+ */
+ void updateFloatingIp(NetFloatingIP floatingIp);
+
+ /**
+ * Removes the floating IP with the supplied ID.
+ *
+ * @param floatingIpId floating ip id
+ * @return removed floating ip; null if failed
+ */
+ NetFloatingIP removeFloatingIp(String floatingIpId);
+
+ /**
+ * Returns the floating IP with the supplied ID.
+ *
+ * @param floatingIpId floating ip id
+ * @return openstack floating ip; null if not found
+ */
+ NetFloatingIP floatingIp(String floatingIpId);
+
+ /**
+ * Returns all floating IP addresses.
+ *
+ * @return openstack floating ip; empty set if no floating ip exists
+ */
+ Set<NetFloatingIP> floatingIps();
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStoreDelegate.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStoreDelegate.java
new file mode 100644
index 0000000..12ce679
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-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.api;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * OpenStack router store delegate abstraction.
+ */
+public interface OpenstackRouterStoreDelegate extends StoreDelegate<OpenstackRouterEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java
deleted file mode 100644
index 29e492e..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.api;
-
-import org.onosproject.net.Host;
-
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackRouterInterface;
-
-/**
- * Handles router update requests from OpenStack.
- */
-public interface OpenstackRoutingService {
-
- /**
- * Handles the router create request from OpenStack.
- *
- * @param osRouter router information
- */
- void createRouter(OpenstackRouter osRouter);
-
- /**
- * Handles the router update request from OpenStack.
- * Update router is called when the name, administrative state, or the external
- * gateway setting is updated. The external gateway update is the only case
- * that openstack routing service cares.
- *
- * @param osRouter router information
- */
- void updateRouter(OpenstackRouter osRouter);
-
- /**
- * Handles the router remove request from OpenStack.
- *
- * @param osRouterId identifier of the router
- */
- void removeRouter(String osRouterId);
-
- /**
- * Handles router interface add request from OpenStack.
- *
- * @param osInterface router interface information
- */
- void addRouterInterface(OpenstackRouterInterface osInterface);
-
- /**
- * Handles router interface remove request from OpenStack.
- *
- * @param osInterface router interface information
- */
- void removeRouterInterface(OpenstackRouterInterface osInterface);
-
- /**
- * Handles to purge data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void purgeVmFlow(Host host);
-
- /**
- * Handles to reinstall data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
deleted file mode 100644
index 1709c95..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.api;
-
-import org.onosproject.net.Host;
-
-import org.onosproject.openstackinterface.OpenstackPort;
-
-/**
- * Represents OpenstackSecurityGroupService Interface.
- */
-public interface OpenstackSecurityGroupService {
-
- /**
- * Updates the flow rules for Security Group for the VM (OpenstackPort).
- *
- * @param osPort OpenstackPort information for the VM
- */
- void updateSecurityGroup(OpenstackPort osPort);
-
- /**
- * Handles to purge data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void purgeVmFlow(Host host);
-
- /**
- * Handles to reinstall data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java
deleted file mode 100644
index dc1113e..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.api;
-
-import org.onosproject.net.Host;
-
-/**
- * Handles switch update requests from OpenStack and CLI.
- */
-public interface OpenstackSwitchingService {
- /**
- * Handles to purge data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void purgeVmFlow(Host host);
-
- /**
- * Handles to reinstall data plane flow of existing VM.
- *
- * @param host VM Host information
- */
- void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
index 0cd17c1..1696e21 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-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.
@@ -15,6 +15,8 @@
*/
/**
- * Application for OpenstackRouting.
+ * Implements OpenStack L3 service plugin, which routes packets between subnets,
+ * forwards packets from internal networks to external ones, and accesses instances
+ * from external networks through floating IPs.
*/
-package org.onosproject.openstacknetworking.api;
\ No newline at end of file
+package org.onosproject.openstacknetworking.api;
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
new file mode 100644
index 0000000..511e89e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-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.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
+
+import java.util.Comparator;
+import java.util.List;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack floating IP addresses.
+ */
+@Command(scope = "onos", name = "openstack-floatingips",
+ description = "Lists all OpenStack floating IP addresses")
+public class OpenstackFloatingIpListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT = "%-40s%-20s%-20s";
+
+ @Override
+ protected void execute() {
+ OpenstackRouterService service = AbstractShellCommand.get(OpenstackRouterService.class);
+ List<NetFloatingIP> floatingIps = Lists.newArrayList(service.floatingIps());
+ floatingIps.sort(Comparator.comparing(NetFloatingIP::getFloatingIpAddress));
+
+ if (outputJson()) {
+ try {
+ print("%s", mapper().writeValueAsString(json(floatingIps)));
+ } catch (JsonProcessingException e) {
+ print("Failed to list networks in JSON format");
+ }
+ return;
+ }
+
+ print(FORMAT, "ID", "Floating IP", "Fixed IP");
+ for (NetFloatingIP floatingIp: floatingIps) {
+ print(FORMAT, floatingIp.getId(),
+ floatingIp.getFloatingIpAddress(),
+ Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
+ "" : floatingIp.getFixedIpAddress());
+ }
+ }
+
+ private JsonNode json(List<NetFloatingIP> floatingIps) {
+ ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+ for (NetFloatingIP floatingIp: floatingIps) {
+ result.add(writeFloatingIp(floatingIp));
+ }
+ return result;
+ }
+
+ private ObjectNode writeFloatingIp(NetFloatingIP floatingIp) {
+ try {
+ String strFloatingIp = ObjectMapperSingleton.getContext(NeutronFloatingIP.class)
+ .writerFor(NeutronFloatingIP.class)
+ .writeValueAsString(floatingIp);
+ log.trace(strFloatingIp);
+ return (ObjectNode) mapper().readTree(strFloatingIp.getBytes());
+ } catch (Exception e) {
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
deleted file mode 100644
index b2f5b8d..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.host.HostService;
-
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Purge Flows of OpenstackInstance Data Plane.
- */
-
-@Command(scope = "onos", name = "openstack-purge-flows",
- description = "Purge data plane flows of existing VM.")
-public class OpenstackInstancePurgeFlowsCommand extends AbstractShellCommand {
-
- @Option(name = "-a", aliases = "--all",
- description = "HostIDs are all existing VM",
- required = false, multiValued = false)
- private Boolean allhost = false;
-
- @Argument(index = 0, name = "hostId", description = "HostID(s)",
- required = false, multiValued = true)
- private String[] hostids = null;
-
- @Override
- protected void execute() {
- HostService hostService = AbstractShellCommand.get(HostService.class);
-
- OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
- OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
- OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
- OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
-
- if (allhost) {
- switchingService.purgeVmFlow(null);
- sgService.purgeVmFlow(null);
- routingService.purgeVmFlow(null);
- floatingIpService.purgeVmFlow(null);
-
- hostService.getHosts().forEach(host -> {
- printHost(host);
- });
- } else if (hostids != null) {
- for (String hostid : hostids) {
- Host host = hostService.getHost(HostId.hostId(hostid));
- if (host == null) {
- continue;
- }
- switchingService.purgeVmFlow(host);
- sgService.purgeVmFlow(host);
- routingService.purgeVmFlow(host);
- floatingIpService.purgeVmFlow(host);
- printHost(host);
- }
- }
- }
-
- private void printHost(Host host) {
- print("Purge data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
- host.id(), host.annotations().value(VXLAN_ID),
- host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
deleted file mode 100644
index eda334d..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.host.HostService;
-
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Re-Install Flows of OpenstackInstance Data Plane.
- */
-
-@Command(scope = "onos", name = "openstack-reinstall-flows",
- description = "Re-install data plane flows of existing VM.")
-public class OpenstackInstanceReInstallFlowCommand extends AbstractShellCommand {
-
- @Option(name = "-a", aliases = "--all",
- description = "HostIDs are all existing VM", required = false, multiValued = false)
- private Boolean allhost = false;
-
- @Argument(index = 0, name = "hostId", description = "HostID(s)",
- required = false, multiValued = true)
- private String[] hostids = null;
-
- @Override
- protected void execute() {
- HostService hostService = AbstractShellCommand.get(HostService.class);
-
- OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
- OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
- OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
- OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
-
- if (allhost) {
- hostService.getHosts().forEach(host -> {
- switchingService.reinstallVmFlow(host);
- sgService.reinstallVmFlow(host);
- routingService.reinstallVmFlow(host);
- floatingIpService.reinstallVmFlow(host);
- printHost(host);
-
- });
- } else if (hostids != null) {
- for (String hostid : hostids) {
- Host host = hostService.getHost(HostId.hostId(hostid));
- if (host == null) {
- continue;
- }
- switchingService.reinstallVmFlow(host);
- sgService.reinstallVmFlow(host);
- routingService.reinstallVmFlow(host);
- floatingIpService.reinstallVmFlow(host);
- printHost(host);
- }
- }
- }
-
- private void printHost(Host host) {
- print("Re-install data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
- host.id(), host.annotations().value(VXLAN_ID),
- host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
new file mode 100644
index 0000000..8b489c9
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017-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.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack networks.
+ */
+@Command(scope = "onos", name = "openstack-networks",
+ description = "Lists all OpenStack networks")
+public class OpenstackNetworkListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+ @Override
+ protected void execute() {
+ OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+ List<Network> networks = Lists.newArrayList(service.networks());
+ networks.sort(Comparator.comparing(Network::getName));
+
+ if (outputJson()) {
+ try {
+ print("%s", mapper().writeValueAsString(json(networks)));
+ } catch (JsonProcessingException e) {
+ print("Failed to list networks in JSON format");
+ }
+ return;
+ }
+
+ print(FORMAT, "ID", "Name", "VNI", "Subnets");
+ for (Network net: networks) {
+ List<String> subnets = service.subnets().stream()
+ .filter(subnet -> subnet.getNetworkId().equals(net.getId()))
+ .map(Subnet::getCidr)
+ .collect(Collectors.toList());
+ print(FORMAT, net.getId(),
+ net.getName(),
+ net.getProviderSegID(),
+ subnets.isEmpty() ? "" : subnets);
+ }
+ }
+
+ private JsonNode json(List<Network> networks) {
+ ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+ for (Network net: networks) {
+ result.add(writeNetwork(net));
+ }
+ return result;
+ }
+
+ private ObjectNode writeNetwork(Network net) {
+ try {
+ String strNet = ObjectMapperSingleton.getContext(NeutronNetwork.class)
+ .writerFor(NeutronNetwork.class)
+ .writeValueAsString(net);
+ return (ObjectNode) mapper().readTree(strNet.getBytes());
+ } catch (Exception e) {
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
new file mode 100644
index 0000000..58d44cd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017-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.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack ports.
+ */
+@Command(scope = "onos", name = "openstack-ports",
+ description = "Lists all OpenStack ports")
+public class OpenstackPortListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+ @Argument(name = "networkId", description = "Network ID")
+ private String networkId = null;
+
+ @Override
+ protected void execute() {
+ OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+
+ List<Port> ports = Lists.newArrayList(service.ports());
+ ports.sort(Comparator.comparing(Port::getNetworkId));
+
+ if (!Strings.isNullOrEmpty(networkId)) {
+ ports.removeIf(port -> !port.getNetworkId().equals(networkId));
+ }
+
+ if (outputJson()) {
+ try {
+ print("%s", mapper().writeValueAsString(json(ports)));
+ } catch (JsonProcessingException e) {
+ print("Failed to list ports in JSON format");
+ }
+ return;
+ }
+
+ print(FORMAT, "ID", "Network", "MAC", "Fixed IPs");
+ for (Port port: ports) {
+ List<String> fixedIps = port.getFixedIps().stream()
+ .map(IP::getIpAddress)
+ .collect(Collectors.toList());
+ Network osNet = service.network(port.getNetworkId());
+ print(FORMAT, port.getId(),
+ osNet.getName(),
+ port.getMacAddress(),
+ fixedIps.isEmpty() ? "" : fixedIps);
+ }
+ }
+
+ private JsonNode json(List<Port> ports) {
+ ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+ for (Port port: ports) {
+ result.add(writePort(port));
+ }
+ return result;
+ }
+
+ private ObjectNode writePort(Port port) {
+ try {
+ String strPort = ObjectMapperSingleton.getContext(NeutronPort.class)
+ .writerFor(NeutronPort.class)
+ .writeValueAsString(port);
+ return (ObjectNode) mapper().readTree(strPort.getBytes());
+ } catch (Exception e) {
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
new file mode 100644
index 0000000..4f7c8bd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017-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.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack routers.
+ */
+@Command(scope = "onos", name = "openstack-routers",
+ description = "Lists all OpenStack routers")
+public class OpenstackRouterListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+ private final OpenstackRouterService routerService =
+ AbstractShellCommand.get(OpenstackRouterService.class);
+ private final OpenstackNetworkService netService =
+ AbstractShellCommand.get(OpenstackNetworkService.class);
+
+ @Override
+ protected void execute() {
+ List<Router> routers = Lists.newArrayList(routerService.routers());
+ routers.sort(Comparator.comparing(Router::getName));
+
+ if (outputJson()) {
+ try {
+ print("%s", mapper().writeValueAsString(json(routers)));
+ } catch (JsonProcessingException e) {
+ print("Failed to list routers in JSON format");
+ }
+ return;
+ }
+ print(FORMAT, "ID", "Name", "External", "Internal");
+ for (Router router: routers) {
+ String exNetId = router.getExternalGatewayInfo() != null ?
+ router.getExternalGatewayInfo().getNetworkId() : null;
+
+ List<String> externals = Lists.newArrayList();
+ if (exNetId != null) {
+ // FIXME fix openstack4j to provide external gateway ip info
+ externals = netService.ports(exNetId).stream()
+ .filter(port -> Objects.equals(port.getDeviceId(),
+ router.getId()))
+ .flatMap(port -> port.getFixedIps().stream())
+ .map(IP::getIpAddress)
+ .collect(Collectors.toList());
+ }
+
+ List<String> internals = Lists.newArrayList();
+ routerService.routerInterfaces(router.getId()).forEach(iface -> {
+ internals.add(getRouterIfaceIp(iface));
+ });
+ print(FORMAT, router.getId(), router.getName(), externals, internals);
+ }
+ }
+
+ private String getRouterIfaceIp(RouterInterface iface) {
+ Port osPort = netService.port(iface.getPortId());
+ IP ipAddr = osPort.getFixedIps().stream()
+ .filter(ip -> ip.getSubnetId().equals(iface.getSubnetId()))
+ .findAny().orElse(null);
+ return ipAddr == null ? "" : ipAddr.getIpAddress();
+ }
+
+ private JsonNode json(List<Router> routers) {
+ ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+ for (Router router: routers) {
+ result.add(writeRouter(router));
+ }
+ return result;
+ }
+
+ private ObjectNode writeRouter(Router osRouter) {
+ try {
+ String strNet = ObjectMapperSingleton.getContext(NeutronRouter.class)
+ .writerFor(NeutronRouter.class)
+ .writeValueAsString(osRouter);
+ return (ObjectNode) mapper().readTree(strNet.getBytes());
+ } catch (Exception e) {
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java
deleted file mode 100644
index f37a323..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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.impl;
-
-import org.onlab.osgi.DefaultServiceDirectory;
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.packet.Ip4Address;
-import org.onlab.util.Tools;
-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.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackSubnet;
-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.api.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 OpenstackInterfaceService openstackService;
-
- protected HostListener hostListener = new InternalHostListener();
-
- protected void activate() {
- ServiceDirectory services = new DefaultServiceDirectory();
- coreService = services.get(CoreService.class);
- mastershipService = services.get(MastershipService.class);
- openstackService = services.get(OpenstackInterfaceService.class);
- hostService = services.get(HostService.class);
- hostService.addListener(hostListener);
-
- log.info("Started");
- }
-
- protected void deactivate() {
- hostService.removeListener(hostListener);
- eventExecutor.shutdown();
-
- log.info("Stopped");
- }
-
- /**
- * Performs any action when a host is detected.
- *
- * @param host detected host
- */
- protected abstract void hostDetected(Host host);
-
- /**
- * Performs any action when a host is removed.
- *
- * @param host removed host
- */
- protected 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 Set<Host> getHosts(OpenstackSubnet osSubnet) {
- return Tools.stream(hostService.getHosts())
- .filter(host -> host.annotations().value(SUBNET_ID).equals(osSubnet.id()))
- .collect(Collectors.toSet());
- }
-
- protected Optional<OpenstackRouter> getRouter(Host host) {
- return openstackService.routers().stream()
- .filter(router -> routableSubNets(router.id()).stream()
- .filter(subnet -> subnet.id().equals(host.annotations().value(SUBNET_ID)))
- .findAny().isPresent())
- .findAny();
- }
-
- protected Set<OpenstackSubnet> routableSubNets(String osRouterId) {
- return openstackService.ports().stream()
- .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) &&
- p.deviceId().equals(osRouterId))
- .map(p -> openstackService.subnet(p.fixedIps().keySet().stream().findFirst().get()))
- .collect(Collectors.toSet());
- }
-
- 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;
- }
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
new file mode 100644
index 0000000..ac29491
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2017-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.impl;
+
+import com.google.common.collect.ImmutableSet;
+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.Service;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.openstack4j.model.network.IPVersionType;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.NetworkType;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.State;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronAllowedAddressPair;
+import org.openstack4j.openstack.networking.domain.NeutronExtraDhcpOptCreate;
+import org.openstack4j.openstack.networking.domain.NeutronHostRoute;
+import org.openstack4j.openstack.networking.domain.NeutronIP;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
+import org.openstack4j.openstack.networking.domain.NeutronPool;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+import org.openstack4j.openstack.networking.domain.NeutronSubnet;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages the inventory of OpenStack network, subnet, and port using a {@code ConsistentMap}.
+ */
+@Service
+@Component(immediate = true)
+public class DistributedOpenstackNetworkStore
+ extends AbstractStore<OpenstackNetworkEvent, OpenstackNetworkStoreDelegate>
+ implements OpenstackNetworkStore {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String ERR_NOT_FOUND = " does not exist";
+ private static final String ERR_DUPLICATE = " already exists";
+
+ private static final KryoNamespace SERIALIZER_NEUTRON_L2 = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(Network.class)
+ .register(NeutronNetwork.class)
+ .register(State.class)
+ .register(NetworkType.class)
+ .register(Port.class)
+ .register(NeutronPort.class)
+ .register(NeutronIP.class)
+ .register(NeutronAllowedAddressPair.class)
+ .register(NeutronExtraDhcpOptCreate.class)
+ .register(Subnet.class)
+ .register(NeutronSubnet.class)
+ .register(NeutronPool.class)
+ .register(NeutronHostRoute.class)
+ .register(IPVersionType.class)
+ .build();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+ private final MapEventListener<String, Network> networkMapListener = new OpenstackNetworkMapListener();
+ private final MapEventListener<String, Subnet> subnetMapListener = new OpenstackSubnetMapListener();
+ private final MapEventListener<String, Port> portMapListener = new OpenstackPortMapListener();
+
+ private ConsistentMap<String, Network> osNetworkStore;
+ private ConsistentMap<String, Subnet> osSubnetStore;
+ private ConsistentMap<String, Port> osPortStore;
+
+ @Activate
+ protected void activate() {
+ ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+
+ osNetworkStore = storageService.<String, Network>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
+ .withName("openstack-networkstore")
+ .withApplicationId(appId)
+ .build();
+ osNetworkStore.addListener(networkMapListener);
+
+ osSubnetStore = storageService.<String, Subnet>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
+ .withName("openstack-subnetstore")
+ .withApplicationId(appId)
+ .build();
+ osSubnetStore.addListener(subnetMapListener);
+
+ osPortStore = storageService.<String, Port>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
+ .withName("openstack-portstore")
+ .withApplicationId(appId)
+ .build();
+ osPortStore.addListener(portMapListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osNetworkStore.removeListener(networkMapListener);
+ osSubnetStore.removeListener(subnetMapListener);
+ osPortStore.removeListener(portMapListener);
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ @Override
+ public void createNetwork(Network osNet) {
+ osNetworkStore.compute(osNet.getId(), (id, existing) -> {
+ final String error = osNet.getName() + ERR_DUPLICATE;
+ checkArgument(existing == null, error);
+ return osNet;
+ });
+ }
+
+ @Override
+ public void updateNetwork(Network osNet) {
+ osNetworkStore.compute(osNet.getId(), (id, existing) -> {
+ final String error = osNet.getName() + ERR_NOT_FOUND;
+ checkArgument(existing != null, error);
+ return osNet;
+ });
+ }
+
+ @Override
+ public Network removeNetwork(String netId) {
+ Versioned<Network> osNet = osNetworkStore.remove(netId);
+ return osNet == null ? null : osNet.value();
+ }
+
+ @Override
+ public Network network(String netId) {
+ Versioned<Network> versioned = osNetworkStore.get(netId);
+ return versioned == null ? null : versioned.value();
+ }
+
+ @Override
+ public Set<Network> networks() {
+ Set<Network> osNets = osNetworkStore.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osNets);
+ }
+
+ @Override
+ public void createSubnet(Subnet osSubnet) {
+ osSubnetStore.compute(osSubnet.getId(), (id, existing) -> {
+ final String error = osSubnet.getId() + ERR_DUPLICATE;
+ checkArgument(existing == null, error);
+ return osSubnet;
+ });
+ }
+
+ @Override
+ public void updateSubnet(Subnet osSubnet) {
+ osSubnetStore.compute(osSubnet.getId(), (id, existing) -> {
+ final String error = osSubnet.getId() + ERR_NOT_FOUND;
+ checkArgument(existing != null, error);
+ return osSubnet;
+ });
+ }
+
+ @Override
+ public Subnet removeSubnet(String subnetId) {
+ Versioned<Subnet> osSubnet = osSubnetStore.remove(subnetId);
+ return osSubnet == null ? null : osSubnet.value();
+ }
+
+ @Override
+ public Subnet subnet(String subnetId) {
+ Versioned<Subnet> osSubnet = osSubnetStore.get(subnetId);
+ return osSubnet == null ? null : osSubnet.value();
+ }
+
+ @Override
+ public Set<Subnet> subnets() {
+ Set<Subnet> osSubnets = osSubnetStore.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osSubnets);
+ }
+
+ @Override
+ public void createPort(Port osPort) {
+ osPortStore.compute(osPort.getId(), (id, existing) -> {
+ final String error = osPort.getId() + ERR_DUPLICATE;
+ checkArgument(existing == null, error);
+ return osPort;
+ });
+ }
+
+ @Override
+ public void updatePort(Port osPort) {
+ osPortStore.compute(osPort.getId(), (id, existing) -> {
+ final String error = osPort.getId() + ERR_NOT_FOUND;
+ checkArgument(existing != null, error);
+ return osPort;
+ });
+ }
+
+ @Override
+ public Port removePort(String portId) {
+ Versioned<Port> osPort = osPortStore.remove(portId);
+ return osPort == null ? null : osPort.value();
+ }
+
+ @Override
+ public Port port(String portId) {
+ Versioned<Port> osPort = osPortStore.get(portId);
+ return osPort == null ? null : osPort.value();
+ }
+
+ @Override
+ public Set<Port> ports() {
+ Set<Port> osPorts = osPortStore.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osPorts);
+ }
+
+ private class OpenstackNetworkMapListener implements MapEventListener<String, Network> {
+
+ @Override
+ public void event(MapEvent<String, Network> event) {
+ switch (event.type()) {
+ case UPDATE:
+ log.debug("OpenStack network updated {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_NETWORK_UPDATED,
+ event.newValue().value()));
+ });
+ break;
+ case INSERT:
+ log.debug("OpenStack network created {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_NETWORK_CREATED,
+ event.newValue().value()));
+ });
+ break;
+ case REMOVE:
+ log.debug("OpenStack network removed {}", event.oldValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_NETWORK_REMOVED,
+ event.oldValue().value()));
+ });
+ break;
+ default:
+ log.error("Unsupported event type");
+ break;
+ }
+ }
+ }
+
+ private class OpenstackSubnetMapListener implements MapEventListener<String, Subnet> {
+
+ @Override
+ public void event(MapEvent<String, Subnet> event) {
+ switch (event.type()) {
+ case UPDATE:
+ log.debug("OpenStack subnet updated {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_SUBNET_UPDATED,
+ network(event.newValue().value().getNetworkId()),
+ event.newValue().value()));
+ });
+ break;
+ case INSERT:
+ log.debug("OpenStack subnet created {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_SUBNET_CREATED,
+ network(event.newValue().value().getNetworkId()),
+ event.newValue().value()));
+ });
+ break;
+ case REMOVE:
+ log.debug("OpenStack subnet removed {}", event.oldValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_SUBNET_REMOVED,
+ network(event.oldValue().value().getNetworkId()),
+ event.oldValue().value()));
+ });
+ break;
+ default:
+ log.error("Unsupported event type");
+ break;
+ }
+ }
+ }
+
+ private class OpenstackPortMapListener implements MapEventListener<String, Port> {
+
+ @Override
+ public void event(MapEvent<String, Port> event) {
+ switch (event.type()) {
+ case UPDATE:
+ log.debug("OpenStack port updated {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_PORT_UPDATED,
+ network(event.newValue().value().getNetworkId()),
+ event.newValue().value()));
+ });
+ break;
+ case INSERT:
+ log.debug("OpenStack port created {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_PORT_CREATED,
+ network(event.newValue().value().getNetworkId()),
+ event.newValue().value()));
+ });
+ break;
+ case REMOVE:
+ log.debug("OpenStack port removed {}", event.oldValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNetworkEvent(
+ OPENSTACK_PORT_REMOVED,
+ network(event.oldValue().value().getNetworkId()),
+ event.oldValue().value()));
+ });
+ break;
+ default:
+ log.error("Unsupported event type");
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
new file mode 100644
index 0000000..6b10512
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2017-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.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStore;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.State;
+import org.openstack4j.openstack.networking.domain.NeutronExternalGateway;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
+import org.openstack4j.openstack.networking.domain.NeutronHostRoute;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
+import org.osgi.service.component.annotations.Activate;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages the inventory of OpenStack router and floating IP using a {@code ConsistentMap}.
+ */
+@Service
+@Component(immediate = true)
+public class DistributedOpenstackRouterStore
+ extends AbstractStore<OpenstackRouterEvent, OpenstackRouterStoreDelegate>
+ implements OpenstackRouterStore {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String ERR_NOT_FOUND = " does not exist";
+ private static final String ERR_DUPLICATE = " already exists";
+
+ private static final KryoNamespace SERIALIZER_NEUTRON_L3 = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(Router.class)
+ .register(NeutronRouter.class)
+ .register(State.class)
+ .register(NeutronHostRoute.class)
+ .register(ExternalGateway.class)
+ .register(NeutronExternalGateway.class)
+ .register(RouterInterface.class)
+ .register(NeutronRouterInterface.class)
+ .register(NetFloatingIP.class)
+ .register(NeutronFloatingIP.class)
+ .build();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+ private final MapEventListener<String, Router> routerMapListener = new OpenstackRouterMapListener();
+ private final MapEventListener<String, RouterInterface> routerInterfaceMapListener =
+ new OpenstackRouterInterfaceMapListener();
+ private final MapEventListener<String, NetFloatingIP> floatingIpMapListener =
+ new OpenstackFloatingIpMapListener();
+
+ private ConsistentMap<String, Router> osRouterStore;
+ private ConsistentMap<String, RouterInterface> osRouterInterfaceStore;
+ private ConsistentMap<String, NetFloatingIP> osFloatingIpStore;
+
+ @Activate
+ protected void activate() {
+ ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+
+ osRouterStore = storageService.<String, Router>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3))
+ .withName("openstack-routerstore")
+ .withApplicationId(appId)
+ .build();
+ osRouterStore.addListener(routerMapListener);
+
+ osRouterInterfaceStore = storageService.<String, RouterInterface>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3))
+ .withName("openstack-routerifacestore")
+ .withApplicationId(appId)
+ .build();
+ osRouterInterfaceStore.addListener(routerInterfaceMapListener);
+
+ osFloatingIpStore = storageService.<String, NetFloatingIP>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3))
+ .withName("openstack-floatingipstore")
+ .withApplicationId(appId)
+ .build();
+ osFloatingIpStore.addListener(floatingIpMapListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osRouterStore.removeListener(routerMapListener);
+ osRouterInterfaceStore.removeListener(routerInterfaceMapListener);
+ osFloatingIpStore.removeListener(floatingIpMapListener);
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ @Override
+ public void createRouter(Router osRouter) {
+ osRouterStore.compute(osRouter.getId(), (id, existing) -> {
+ final String error = osRouter.getName() + ERR_DUPLICATE;
+ checkArgument(existing == null, error);
+ return osRouter;
+ });
+ }
+
+ @Override
+ public void updateRouter(Router osRouter) {
+ osRouterStore.compute(osRouter.getId(), (id, existing) -> {
+ final String error = osRouter.getName() + ERR_NOT_FOUND;
+ checkArgument(existing != null, error);
+ return osRouter;
+ });
+ }
+
+ @Override
+ public Router removeRouter(String routerId) {
+ Versioned<Router> osRouter = osRouterStore.remove(routerId);
+ return osRouter == null ? null : osRouter.value();
+ }
+
+ @Override
+ public Router router(String routerId) {
+ Versioned<Router> versioned = osRouterStore.get(routerId);
+ return versioned == null ? null : versioned.value();
+ }
+
+ @Override
+ public Set<Router> routers() {
+ Set<Router> osRouters = osRouterStore.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osRouters);
+ }
+
+ @Override
+ public void addRouterInterface(RouterInterface osRouterIface) {
+ osRouterInterfaceStore.compute(osRouterIface.getPortId(), (id, existing) -> {
+ final String error = osRouterIface.getPortId() + ERR_DUPLICATE;
+ checkArgument(existing == null, error);
+ return osRouterIface;
+ });
+ }
+
+ @Override
+ public void updateRouterInterface(RouterInterface osRouterIface) {
+ osRouterInterfaceStore.compute(osRouterIface.getPortId(), (id, existing) -> {
+ final String error = osRouterIface.getPortId() + ERR_NOT_FOUND;
+ checkArgument(existing != null, error);
+ return osRouterIface;
+ });
+ }
+
+ @Override
+ public RouterInterface removeRouterInterface(String routerIfaceId) {
+ Versioned<RouterInterface> osRouterIface = osRouterInterfaceStore.remove(routerIfaceId);
+ return osRouterIface == null ? null : osRouterIface.value();
+ }
+
+ @Override
+ public RouterInterface routerInterface(String routerIfaceId) {
+ Versioned<RouterInterface> osRouterIface = osRouterInterfaceStore.get(routerIfaceId);
+ return osRouterIface == null ? null : osRouterIface.value();
+ }
+
+ @Override
+ public Set<RouterInterface> routerInterfaces() {
+ Set<RouterInterface> osRouterIfaces = osRouterInterfaceStore.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osRouterIfaces);
+ }
+
+ @Override
+ public void createFloatingIp(NetFloatingIP osFloatingIp) {
+ osFloatingIpStore.compute(osFloatingIp.getId(), (id, existing) -> {
+ final String error = osFloatingIp.getId() + ERR_DUPLICATE;
+ checkArgument(existing == null, error);
+ return osFloatingIp;
+ });
+ }
+
+ @Override
+ public void updateFloatingIp(NetFloatingIP osFloatingIp) {
+ osFloatingIpStore.compute(osFloatingIp.getId(), (id, existing) -> {
+ final String error = osFloatingIp.getId() + ERR_NOT_FOUND;
+ checkArgument(existing != null, error);
+ return osFloatingIp;
+ });
+ }
+
+ @Override
+ public NetFloatingIP removeFloatingIp(String floatingIpId) {
+ Versioned<NetFloatingIP> osFloatingIp = osFloatingIpStore.remove(floatingIpId);
+ return osFloatingIp == null ? null : osFloatingIp.value();
+ }
+
+ @Override
+ public NetFloatingIP floatingIp(String floatingIpId) {
+ Versioned<NetFloatingIP> osFloatingIp = osFloatingIpStore.get(floatingIpId);
+ return osFloatingIp == null ? null : osFloatingIp.value();
+ }
+
+ @Override
+ public Set<NetFloatingIP> floatingIps() {
+ Set<NetFloatingIP> osFloatingIps = osFloatingIpStore.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osFloatingIps);
+ }
+
+ private class OpenstackRouterMapListener implements MapEventListener<String, Router> {
+
+ @Override
+ public void event(MapEvent<String, Router> event) {
+ switch (event.type()) {
+ case UPDATE:
+ log.debug("OpenStack router updated {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_UPDATED,
+ event.newValue().value()));
+ processGatewayUpdate(event);
+ });
+ break;
+ case INSERT:
+ log.debug("OpenStack router created {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_CREATED,
+ event.newValue().value()));
+ });
+ break;
+ case REMOVE:
+ log.debug("OpenStack router removed {}", event.oldValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_REMOVED,
+ event.oldValue().value()));
+ });
+ break;
+ default:
+ log.error("Unsupported event type");
+ break;
+ }
+ }
+
+ private void processGatewayUpdate(MapEvent<String, Router> event) {
+ ExternalGateway oldGateway = event.oldValue().value().getExternalGatewayInfo();
+ ExternalGateway newGateway = event.newValue().value().getExternalGatewayInfo();
+
+ if (oldGateway == null && newGateway != null) {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_GATEWAY_ADDED,
+ event.newValue().value(), newGateway));
+ }
+ if (oldGateway != null && newGateway == null) {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_GATEWAY_ADDED,
+ event.newValue().value(), oldGateway));
+ }
+ }
+ }
+
+ private class OpenstackRouterInterfaceMapListener implements MapEventListener<String, RouterInterface> {
+
+ @Override
+ public void event(MapEvent<String, RouterInterface> event) {
+ switch (event.type()) {
+ case UPDATE:
+ log.debug("OpenStack router interface updated {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_INTERFACE_UPDATED,
+ router(event.newValue().value().getId()),
+ event.newValue().value()));
+ });
+ break;
+ case INSERT:
+ log.debug("OpenStack router interface created {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_INTERFACE_ADDED,
+ router(event.newValue().value().getId()),
+ event.newValue().value()));
+ });
+ break;
+ case REMOVE:
+ log.debug("OpenStack router interface removed {}", event.oldValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_ROUTER_INTERFACE_REMOVED,
+ router(event.oldValue().value().getId()),
+ event.oldValue().value()));
+ });
+ break;
+ default:
+ log.error("Unsupported event type");
+ break;
+ }
+ }
+ }
+
+ private class OpenstackFloatingIpMapListener implements MapEventListener<String, NetFloatingIP> {
+
+ @Override
+ public void event(MapEvent<String, NetFloatingIP> event) {
+ switch (event.type()) {
+ case UPDATE:
+ log.debug("OpenStack floating IP updated {}", event.newValue());
+ eventExecutor.execute(() -> {
+ Router osRouter = Strings.isNullOrEmpty(
+ event.newValue().value().getRouterId()) ?
+ null :
+ router(event.newValue().value().getRouterId());
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_FLOATING_IP_UPDATED,
+ osRouter,
+ event.newValue().value()));
+ processFloatingIpUpdate(event, osRouter);
+ });
+ break;
+ case INSERT:
+ log.debug("OpenStack floating IP created {}", event.newValue());
+ eventExecutor.execute(() -> {
+ Router osRouter = Strings.isNullOrEmpty(
+ event.newValue().value().getRouterId()) ?
+ null :
+ router(event.newValue().value().getRouterId());
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_FLOATING_IP_CREATED,
+ osRouter,
+ event.newValue().value()));
+ });
+ break;
+ case REMOVE:
+ log.debug("OpenStack floating IP removed {}", event.oldValue());
+ eventExecutor.execute(() -> {
+ Router osRouter = Strings.isNullOrEmpty(
+ event.oldValue().value().getRouterId()) ?
+ null :
+ router(event.oldValue().value().getRouterId());
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_FLOATING_IP_REMOVED,
+ osRouter,
+ event.oldValue().value()));
+ });
+ break;
+ default:
+ log.error("Unsupported event type");
+ break;
+ }
+ }
+
+ private void processFloatingIpUpdate(MapEvent<String, NetFloatingIP> event,
+ Router osRouter) {
+ String oldPortId = event.oldValue().value().getPortId();
+ String newPortId = event.newValue().value().getPortId();
+
+ if (Strings.isNullOrEmpty(oldPortId) && !Strings.isNullOrEmpty(newPortId)) {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_FLOATING_IP_ASSOCIATED,
+ osRouter,
+ event.newValue().value(), newPortId));
+ }
+ if (!Strings.isNullOrEmpty(oldPortId) && Strings.isNullOrEmpty(newPortId)) {
+ notifyDelegate(new OpenstackRouterEvent(
+ OPENSTACK_FLOATING_IP_DISASSOCIATED,
+ osRouter,
+ event.newValue().value(), oldPortId));
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePort.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePort.java
new file mode 100644
index 0000000..de6a3ce
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePort.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017-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.impl;
+
+import com.google.common.base.Strings;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.PortNumber;
+import org.onosproject.openstacknetworking.api.InstancePort;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of instance port based on host subsystem.
+ * Basically, HostBasedInstancePort is just a wrapper of a host, which helps
+ * mapping between OpenStack port and the OVS port and retrieving information
+ * such as IP address, location, and so on.
+ */
+public final class HostBasedInstancePort implements InstancePort {
+
+ static final String ANNOTATION_NETWORK_ID = "networkId";
+ static final String ANNOTATION_PORT_ID = "portId";
+ static final String ANNOTATION_CREATE_TIME = "createTime";
+
+ private final Host host;
+
+ /**
+ * Default constructor.
+ *
+ * @param instance host object of this instance
+ */
+ private HostBasedInstancePort(Host instance) {
+ this.host = instance;
+ }
+
+ /**
+ * Returns new instance.
+ *
+ * @param host host object of this instance
+ * @return instance
+ */
+ public static HostBasedInstancePort of(Host host) {
+ checkNotNull(host);
+ checkArgument(!Strings.isNullOrEmpty(host.annotations().value(ANNOTATION_NETWORK_ID)));
+ checkArgument(!Strings.isNullOrEmpty(host.annotations().value(ANNOTATION_PORT_ID)));
+ checkArgument(!Strings.isNullOrEmpty(host.annotations().value(ANNOTATION_CREATE_TIME)));
+
+ return new HostBasedInstancePort(host);
+ }
+
+ @Override
+ public String networkId() {
+ return host.annotations().value(ANNOTATION_NETWORK_ID);
+ }
+
+ @Override
+ public String portId() {
+ return host.annotations().value(ANNOTATION_PORT_ID);
+ }
+
+ @Override
+ public MacAddress macAddress() {
+ return host.mac();
+ }
+
+ @Override
+ public IpAddress ipAddress() {
+ Optional<IpAddress> ipAddr = host.ipAddresses().stream().findFirst();
+ return ipAddr.orElse(null);
+ }
+
+ @Override
+ public DeviceId deviceId() {
+ return host.location().deviceId();
+ }
+
+ @Override
+ public PortNumber portNumber() {
+ return host.location().port();
+ }
+
+ @Override
+ public String toString() {
+ return host.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof HostBasedInstancePort) {
+ HostBasedInstancePort that = (HostBasedInstancePort) obj;
+ if (Objects.equals(this.portId(), that.portId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(portId());
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java
new file mode 100644
index 0000000..85fd5bf
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2017-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.impl;
+
+import com.google.common.collect.ImmutableSet;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.Tools;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortEvent;
+import org.onosproject.openstacknetworking.api.InstancePortListener;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_DETECTED;
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_UPDATED;
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_VANISHED;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing host based instance ports.
+ * It also provides instance port events for the hosts mapped to OpenStack VM interface.
+ */
+@Service
+@Component(immediate = true)
+public class HostBasedInstancePortManager
+ extends ListenerRegistry<InstancePortEvent, InstancePortListener>
+ implements InstancePortService {
+
+ protected final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ private final HostListener hostListener = new InternalHostListener();
+
+ @Activate
+ protected void activate() {
+ hostService.addListener(hostListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ hostService.removeListener(hostListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public InstancePort instancePort(MacAddress macAddress) {
+ Host host = hostService.getHost(HostId.hostId(macAddress));
+ if (host == null || !isValidHost(host)) {
+ return null;
+ }
+ return HostBasedInstancePort.of(host);
+ }
+
+ @Override
+ public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
+ return Tools.stream(hostService.getHosts()).filter(this::isValidHost)
+ .map(HostBasedInstancePort::of)
+ .filter(instPort -> instPort.networkId().equals(osNetId))
+ .filter(instPort -> instPort.ipAddress().equals(ipAddress))
+ .findAny().orElse(null);
+ }
+
+ @Override
+ public InstancePort instancePort(String osPortId) {
+ return Tools.stream(hostService.getHosts()).filter(this::isValidHost)
+ .map(HostBasedInstancePort::of)
+ .filter(instPort -> instPort.portId().equals(osPortId))
+ .findAny().orElse(null);
+ }
+
+ @Override
+ public Set<InstancePort> instancePorts() {
+ Set<InstancePort> instPors = Tools.stream(hostService.getHosts())
+ .filter(this::isValidHost)
+ .map(HostBasedInstancePort::of)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(instPors);
+ }
+
+ @Override
+ public Set<InstancePort> instancePorts(String osNetId) {
+ Set<InstancePort> instPors = Tools.stream(hostService.getHosts())
+ .filter(this::isValidHost)
+ .map(HostBasedInstancePort::of)
+ .filter(instPort -> instPort.networkId().equals(osNetId))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(instPors);
+ }
+
+ private boolean isValidHost(Host host) {
+ return !host.ipAddresses().isEmpty() &&
+ host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
+ host.annotations().value(ANNOTATION_PORT_ID) != null;
+ }
+
+ private class InternalHostListener implements HostListener {
+
+ @Override
+ public boolean isRelevant(HostEvent event) {
+ Host host = event.subject();
+ if (!isValidHost(host)) {
+ log.debug("Invalid host detected, ignore it {}", host);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void event(HostEvent event) {
+ InstancePort instPort = HostBasedInstancePort.of(event.subject());
+ InstancePortEvent instPortEvent;
+ switch (event.type()) {
+ case HOST_UPDATED:
+ instPortEvent = new InstancePortEvent(
+ OPENSTACK_INSTANCE_PORT_UPDATED,
+ instPort);
+ log.debug("Instance port is updated: {}", instPort);
+ process(instPortEvent);
+ break;
+ case HOST_ADDED:
+ instPortEvent = new InstancePortEvent(
+ OPENSTACK_INSTANCE_PORT_DETECTED,
+ instPort);
+ log.debug("Instance port is detected: {}", instPort);
+ process(instPortEvent);
+ break;
+ case HOST_REMOVED:
+ instPortEvent = new InstancePortEvent(
+ OPENSTACK_INSTANCE_PORT_VANISHED,
+ instPort);
+ log.debug("Instance port is disabled: {}", instPort);
+ process(instPortEvent);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java
deleted file mode 100644
index 602b0f7..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * 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.impl;
-
-import com.google.common.base.Strings;
-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.apache.felix.scr.annotations.Service;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.host.HostService;
-import org.onosproject.openstackinterface.OpenstackFloatingIP;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-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.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-
-
-@Service
-@Component(immediate = true)
-public class OpenstackFloatingIpManager extends AbstractVmHandler implements OpenstackFloatingIpService {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostService hostService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected FlowObjectiveService flowObjectiveService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected StorageService storageService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackNodeService nodeService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ScalableGatewayService gatewayService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
-
- private static final String NOT_ASSOCIATED = "null";
- private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER =
- KryoNamespace.newBuilder().register(KryoNamespaces.API);
-
- private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
- groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
- private final InternalNodeListener nodeListener = new InternalNodeListener();
- private ConsistentMap<IpAddress, Host> floatingIpMap;
-
- private ApplicationId appId;
-
- @Activate
- protected void activate() {
- super.activate();
- appId = coreService.registerApplication(ROUTING_APP_ID);
- nodeService.addListener(nodeListener);
- floatingIpMap = storageService.<IpAddress, Host>consistentMapBuilder()
- .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
- .withName("openstackrouting-floatingip")
- .withApplicationId(appId)
- .build();
-
- log.info("Started");
- }
-
- @Deactivate
- protected void deactivate() {
- super.deactivate();
- nodeService.removeListener(nodeListener);
- log.info("Stopped");
- }
-
- @Override
- protected void hostDetected(Host host) {
- IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
- Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
- .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
- .findFirst();
- if (floatingIp.isPresent()) {
- eventExecutor.execute(() -> associateFloatingIp(floatingIp.get()));
- }
- }
-
- @Override
- protected void hostRemoved(Host host) {
- IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
- Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
- .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
- .findFirst();
- if (floatingIp.isPresent()) {
- eventExecutor.execute(() -> disassociateFloatingIp(floatingIp.get()));
- }
- }
-
- @Override
- public void reinstallVmFlow(Host host) {
- if (host == null) {
- hostService.getHosts().forEach(h -> {
- hostDetected(h);
- log.info("Re-Install data plane flow of virtual machine {}", h);
- });
- } else {
- hostDetected(host);
- log.info("Re-Install data plane flow of virtual machine {}", host);
- }
- }
-
- @Override
- public void purgeVmFlow(Host host) {
- if (host == null) {
- hostService.getHosts().forEach(h -> {
- hostRemoved(h);
- log.info("Purge data plane flow of virtual machine {}", h);
- });
- } else {
- hostRemoved(host);
- log.info("Purge data plane flow of virtual machine {}", host);
- }
- }
-
- @Override
- public void createFloatingIp(OpenstackFloatingIP floatingIp) {
- }
-
- @Override
- public void updateFloatingIp(OpenstackFloatingIP floatingIp) {
- if (Strings.isNullOrEmpty(floatingIp.portId()) ||
- floatingIp.portId().equals(NOT_ASSOCIATED)) {
- eventExecutor.execute(() -> disassociateFloatingIp(floatingIp));
- } else {
- eventExecutor.execute(() -> associateFloatingIp(floatingIp));
- }
- }
-
- @Override
- public void deleteFloatingIp(String floatingIpId) {
- }
-
- private void associateFloatingIp(OpenstackFloatingIP floatingIp) {
- Optional<Host> associatedVm = Tools.stream(hostService.getHosts())
- .filter(host -> Objects.equals(
- host.annotations().value(PORT_ID),
- floatingIp.portId()))
- .findAny();
- if (!associatedVm.isPresent()) {
- log.warn("Failed to associate floating IP({}) to port:{}",
- floatingIp.floatingIpAddress(),
- floatingIp.portId());
- return;
- }
-
- floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get());
- populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get());
-
- log.info("Associated floating IP {} to fixed IP {}",
- floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress());
- }
-
- private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) {
- Versioned<Host> associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress());
- if (associatedVm == null) {
- log.warn("Failed to disassociate floating IP({})",
- floatingIp.floatingIpAddress());
- // No VM is actually associated with the floating IP, do nothing
- return;
- }
-
- removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value());
- log.info("Disassociated floating IP {} from fixed IP {}",
- floatingIp.floatingIpAddress(),
- associatedVm.value().ipAddresses());
- }
-
- private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
- populateFloatingIpIncomingRules(floatingIp, associatedVm);
- populateFloatingIpOutgoingRules(floatingIp, associatedVm);
- }
-
- private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
- Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
- if (!fixedIp.isPresent()) {
- log.warn("Failed to remove floating IP({}) from {}",
- floatingIp, associatedVm);
- return;
- }
-
- TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
- TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
-
- sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
- .matchIPSrc(fixedIp.get().toIpPrefix());
-
- sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(floatingIp.toIpPrefix());
-
- gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
- TrafficSelector.Builder sForTrafficFromVmBuilder = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(floatingIp.toIpPrefix())
- .matchInPort(nodeService.tunnelPort(deviceId).get());
-
- RulePopulatorUtil.setRule(
- flowObjectiveService,
- appId,
- deviceId,
- sOutgoingBuilder.build(),
- DefaultTrafficTreatment.builder().build(),
- ForwardingObjective.Flag.VERSATILE,
- FLOATING_RULE_PRIORITY, false);
-
- RulePopulatorUtil.setRule(
- flowObjectiveService,
- appId,
- deviceId,
- sIncomingBuilder.build(),
- DefaultTrafficTreatment.builder().build(),
- ForwardingObjective.Flag.VERSATILE,
- FLOATING_RULE_PRIORITY, false);
-
- RulePopulatorUtil.setRule(
- flowObjectiveService,
- appId,
- deviceId,
- sForTrafficFromVmBuilder.build(),
- DefaultTrafficTreatment.builder().build(),
- ForwardingObjective.Flag.VERSATILE,
- FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY, false);
- });
- }
-
- private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
- DeviceId cnodeId = associatedVm.location().deviceId();
- Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
- Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
-
- if (!fixedIp.isPresent() || !dataIp.isPresent()) {
- log.warn("Failed to associate floating IP({})", floatingIp);
- return;
- }
-
- TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(floatingIp.toIpPrefix())
- .build();
-
- gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
- TrafficTreatment treatmentForTrafficFromExternal = DefaultTrafficTreatment.builder()
- .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
- .setEthDst(associatedVm.mac())
- .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
- .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
- .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
- gnodeId)
- .setOutput(nodeService.tunnelPort(gnodeId).get())
- .build();
-
- ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
- .withSelector(selectorForTrafficFromExternal)
- .withTreatment(treatmentForTrafficFromExternal)
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .withPriority(FLOATING_RULE_PRIORITY)
- .fromApp(appId)
- .add();
-
- flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
-
-
- TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(floatingIp.toIpPrefix())
- .matchInPort(nodeService.tunnelPort(gnodeId).get())
- .build();
-
- TrafficTreatment treatmentForTrafficFromVm = DefaultTrafficTreatment.builder()
- .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
- .setEthDst(associatedVm.mac())
- .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
- .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
- .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
- gnodeId)
- .setOutput(PortNumber.IN_PORT)
- .build();
-
- ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
- .withSelector(selectorForTrafficFromVm)
- .withTreatment(treatmentForTrafficFromVm)
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
- .fromApp(appId)
- .add();
-
- flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
-
- });
- }
-
- private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
- TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
- .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
- .build();
-
- gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setIpSrc(floatingIp)
- .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
- .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
- .setOutput(gatewayService.getUplinkPort(gnodeId))
- .build();
-
- ForwardingObjective fo = DefaultForwardingObjective.builder()
- .withSelector(selector)
- .withTreatment(treatment)
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .withPriority(FLOATING_RULE_PRIORITY)
- .fromApp(appId)
- .add();
-
- flowObjectiveService.forward(gnodeId, fo);
- });
- }
-
- // TODO consider the case that port with associated floating IP is attached to a VM
-
- private class InternalNodeListener implements OpenstackNodeListener {
-
- @Override
- public void event(OpenstackNodeEvent event) {
- OpenstackNode node = event.node();
-
- switch (event.type()) {
- case COMPLETE:
- reinstallVmFlow(null);
- break;
- case INIT:
- case DEVICE_CREATED:
- case INCOMPLETE:
- default:
- break;
- }
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java
deleted file mode 100644
index d58062a..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * 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.impl;
-
-import com.google.common.collect.Maps;
-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.Ethernet;
-import org.onlab.packet.ICMP;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-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.OutboundPacket;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketPriority;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketService;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.GatewayNode;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-import org.slf4j.Logger;
-
-import java.nio.ByteBuffer;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
-import static org.slf4j.LoggerFactory.getLogger;
-
-
-/**
- * Handle ICMP packet sent from OpenStack Gateway nodes.
- * For a request to any private network gateway IPs, it generates fake reply.
- * For a request to the external network, it does source NAT with a public IP and
- * forward the request to the external only if the request instance has external
- * connection setups.
- */
-@Component(immediate = true)
-public class OpenstackIcmpHandler {
- protected final Logger log = getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected PacketService packetService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostService hostService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ScalableGatewayService gatewayService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackNodeService nodeService;
-
- private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
- groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
- private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
- private final InternalNodeListener nodeListener = new InternalNodeListener();
- private final Map<String, Host> icmpInfoMap = Maps.newHashMap();
-
- ApplicationId appId;
-
- @Activate
- protected void activate() {
- appId = coreService.registerApplication(ROUTING_APP_ID);
- packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
- nodeService.addListener(nodeListener);
- requestPacket(appId);
-
- log.info("Started");
- }
-
- @Deactivate
- protected void deactivate() {
- packetService.removeProcessor(packetProcessor);
- log.info("Stopped");
- }
-
- private void requestPacket(ApplicationId appId) {
- TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPProtocol(IPv4.PROTOCOL_ICMP)
- .build();
-
- gatewayService.getGatewayDeviceIds().forEach(gateway -> {
- packetService.requestPackets(icmpSelector,
- PacketPriority.CONTROL,
- appId,
- Optional.of(gateway));
- log.debug("Requested ICMP packet on {}", gateway);
- });
- }
-
- private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
- IPv4 ipPacket = (IPv4) ethernet.getPayload();
- log.trace("Processing ICMP packet from ip {}, mac {}",
- Ip4Address.valueOf(ipPacket.getSourceAddress()),
- ethernet.getSourceMAC());
-
- ICMP icmp = (ICMP) ipPacket.getPayload();
- short icmpId = getIcmpId(icmp);
-
- DeviceId srcDevice = context.inPacket().receivedFrom().deviceId();
- switch (icmp.getIcmpType()) {
- case ICMP.TYPE_ECHO_REQUEST:
- Optional<Host> reqHost = hostService.getHostsByMac(ethernet.getSourceMAC())
- .stream().findFirst();
- if (!reqHost.isPresent()) {
- log.warn("No host found for MAC {}", ethernet.getSourceMAC());
- return;
- }
-
- // TODO Considers icmp between internal subnets belong to the same router.
- // TODO do we have to support ICMP reply for non-existing gateway?
- Ip4Address gatewayIp = Ip4Address.valueOf(
- reqHost.get().annotations().value(Constants.GATEWAY_IP));
- if (Objects.equals(ipPacket.getDestinationAddress(), gatewayIp.toInt())) {
- processRequestToGateway(ipPacket, reqHost.get());
- } else {
- Optional<Ip4Address> srcNatIp = getSrcNatIp(reqHost.get());
- if (!srcNatIp.isPresent()) {
- log.trace("VM {} has no external connection", reqHost.get());
- return;
- }
-
- sendRequestToExternal(ipPacket, srcDevice, srcNatIp.get());
- String icmpInfoKey = String.valueOf(icmpId)
- .concat(String.valueOf(srcNatIp.get().toInt()))
- .concat(String.valueOf(ipPacket.getDestinationAddress()));
- icmpInfoMap.putIfAbsent(icmpInfoKey, reqHost.get());
- }
- break;
- case ICMP.TYPE_ECHO_REPLY:
- String icmpInfoKey = String.valueOf(icmpId)
- .concat(String.valueOf(ipPacket.getDestinationAddress()))
- .concat(String.valueOf(ipPacket.getSourceAddress()));
-
- processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey));
- icmpInfoMap.remove(icmpInfoKey);
- break;
- default:
- break;
- }
- }
-
- // TODO do we have to handle the request to the fake gateway?
- private void processRequestToGateway(IPv4 ipPacket, Host reqHost) {
- ICMP icmpReq = (ICMP) ipPacket.getPayload();
- icmpReq.setChecksum((short) 0);
- icmpReq.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
-
- int destinationAddress = ipPacket.getSourceAddress();
-
- ipPacket.setSourceAddress(ipPacket.getDestinationAddress())
- .setDestinationAddress(destinationAddress)
- .resetChecksum();
-
- ipPacket.setPayload(icmpReq);
- Ethernet icmpReply = new Ethernet();
- icmpReply.setEtherType(Ethernet.TYPE_IPV4)
- .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
- .setDestinationMACAddress(reqHost.mac())
- .setPayload(ipPacket);
-
- sendReply(icmpReply, reqHost);
- }
-
- private void sendRequestToExternal(IPv4 ipPacket, DeviceId srcDevice, Ip4Address srcNatIp) {
- ICMP icmpReq = (ICMP) ipPacket.getPayload();
- icmpReq.resetChecksum();
- ipPacket.setSourceAddress(srcNatIp.toInt()).resetChecksum();
- ipPacket.setPayload(icmpReq);
-
- Ethernet icmpRequestEth = new Ethernet();
- icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
- .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
- .setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC)
- .setPayload(ipPacket);
-
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setOutput(gatewayService.getUplinkPort(srcDevice))
- .build();
-
- OutboundPacket packet = new DefaultOutboundPacket(
- srcDevice,
- treatment,
- ByteBuffer.wrap(icmpRequestEth.serialize()));
-
- packetService.emit(packet);
- }
-
- private void processReplyFromExternal(IPv4 ipPacket, Host dstHost) {
- ICMP icmpReply = (ICMP) ipPacket.getPayload();
- icmpReply.resetChecksum();
-
- Ip4Address ipAddress = dstHost.ipAddresses().stream().findFirst().get().getIp4Address();
- ipPacket.setDestinationAddress(ipAddress.toInt())
- .resetChecksum();
- ipPacket.setPayload(icmpReply);
-
- Ethernet icmpResponseEth = new Ethernet();
- icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
- .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
- .setDestinationMACAddress(dstHost.mac())
- .setPayload(ipPacket);
-
- sendReply(icmpResponseEth, dstHost);
- }
-
- private void sendReply(Ethernet icmpReply, Host dstHost) {
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setOutput(dstHost.location().port())
- .build();
-
- OutboundPacket packet = new DefaultOutboundPacket(
- dstHost.location().deviceId(),
- treatment,
- ByteBuffer.wrap(icmpReply.serialize()));
-
- packetService.emit(packet);
- }
-
- private Optional<Ip4Address> getSrcNatIp(Host host) {
- // TODO cache external gateway IP for each network because
- // asking Neutron for every ICMP request is a bad idea
- Optional<OpenstackPort> osPort = openstackService.ports().stream()
- .filter(port -> port.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) &&
- Objects.equals(host.annotations().value(NETWORK_ID),
- port.networkId()))
- .findAny();
- if (!osPort.isPresent()) {
- return Optional.empty();
- }
-
- OpenstackRouter osRouter = openstackService.router(osPort.get().deviceId());
- if (osRouter == null) {
- return Optional.empty();
- }
-
- return osRouter.gatewayExternalInfo().externalFixedIps()
- .values().stream().findAny();
- }
-
- private short getIcmpId(ICMP icmp) {
- return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
- }
-
- private class InternalPacketProcessor implements PacketProcessor {
-
- @Override
- public void process(PacketContext context) {
- if (context.isHandled()) {
- return;
- } else if (!gatewayService.getGatewayDeviceIds().contains(
- context.inPacket().receivedFrom().deviceId())) {
- // return if the packet is not from gateway nodes
- return;
- }
-
- InboundPacket pkt = context.inPacket();
- Ethernet ethernet = pkt.parsed();
- if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
- return;
- }
-
- IPv4 iPacket = (IPv4) ethernet.getPayload();
- if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
- eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
- }
- }
- }
-
- private class InternalNodeListener implements OpenstackNodeListener {
-
- @Override
- public void event(OpenstackNodeEvent event) {
- OpenstackNode node = event.node();
-
- switch (event.type()) {
- case COMPLETE:
- if (node.type() == GATEWAY) {
- log.info("GATEWAY node {} detected", node.hostname());
- eventExecutor.execute(() -> {
- GatewayNode gnode = GatewayNode.builder()
- .gatewayDeviceId(node.intBridge())
- .dataIpAddress(node.dataIp().getIp4Address())
- .uplinkIntf(node.externalPortName().get())
- .build();
- gatewayService.addGatewayNode(gnode);
- requestPacket(appId);
- });
- }
- break;
- case INIT:
- case DEVICE_CREATED:
- case INCOMPLETE:
- default:
- break;
- }
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
new file mode 100644
index 0000000..2517510
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2017-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.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.apache.felix.scr.annotations.Service;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing OpenStack network,
+ * subnet, and port.
+ */
+@Service
+@Component(immediate = true)
+public class OpenstackNetworkManager
+ extends ListenerRegistry<OpenstackNetworkEvent, OpenstackNetworkListener>
+ implements OpenstackNetworkAdminService, OpenstackNetworkService {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String MSG_NETWORK = "OpenStack network %s %s";
+ private static final String MSG_SUBNET = "OpenStack subnet %s %s";
+ private static final String MSG_PORT = "OpenStack port %s %s";
+ private static final String MSG_CREATED = "created";
+ private static final String MSG_UPDATED = "updated";
+ private static final String MSG_REMOVED = "removed";
+
+ private static final String ERR_NULL_NETWORK = "OpenStack network cannot be null";
+ private static final String ERR_NULL_NETWORK_ID = "OpenStack network ID cannot be null";
+ private static final String ERR_NULL_NETWORK_NAME = "OpenStack network name cannot be null";
+ private static final String ERR_NULL_SUBNET = "OpenStack subnet cannot be null";
+ private static final String ERR_NULL_SUBNET_ID = "OpenStack subnet ID cannot be null";
+ private static final String ERR_NULL_SUBNET_NET_ID = "OpenStack subnet network ID cannot be null";
+ private static final String ERR_NULL_SUBNET_CIDR = "OpenStack subnet CIDR cannot be null";
+ private static final String ERR_NULL_PORT = "OpenStack port cannot be null";
+ private static final String ERR_NULL_PORT_ID = "OpenStack port ID cannot be null";
+ private static final String ERR_NULL_PORT_NET_ID = "OpenStack port network ID cannot be null";
+
+ private static final String ERR_IN_USE = " still in use";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkStore osNetworkStore;
+
+ private final OpenstackNetworkStoreDelegate delegate = new InternalNetworkStoreDelegate();
+
+ @Activate
+ protected void activate() {
+ coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+ osNetworkStore.setDelegate(delegate);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osNetworkStore.unsetDelegate(delegate);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void createNetwork(Network osNet) {
+ checkNotNull(osNet, ERR_NULL_NETWORK);
+ checkArgument(!Strings.isNullOrEmpty(osNet.getId()), ERR_NULL_NETWORK_ID);
+ checkArgument(!Strings.isNullOrEmpty(osNet.getName()), ERR_NULL_NETWORK_NAME);
+
+ osNetworkStore.createNetwork(osNet);
+ log.info(String.format(MSG_NETWORK, osNet.getName(), MSG_CREATED));
+ }
+
+ @Override
+ public void updateNetwork(Network osNet) {
+ checkNotNull(osNet, ERR_NULL_NETWORK);
+ checkArgument(!Strings.isNullOrEmpty(osNet.getId()), ERR_NULL_NETWORK_ID);
+ checkArgument(!Strings.isNullOrEmpty(osNet.getName()), ERR_NULL_NETWORK_NAME);
+
+ osNetworkStore.updateNetwork(osNet);
+ log.info(String.format(MSG_NETWORK, osNet.getId(), MSG_UPDATED));
+ }
+
+ @Override
+ public void removeNetwork(String netId) {
+ checkArgument(!Strings.isNullOrEmpty(netId), ERR_NULL_NETWORK_ID);
+ synchronized (this) {
+ if (isNetworkInUse(netId)) {
+ final String error = String.format(MSG_NETWORK, netId, ERR_IN_USE);
+ throw new IllegalStateException(error);
+ }
+ Network osNet = osNetworkStore.removeNetwork(netId);
+ if (osNet != null) {
+ log.info(String.format(MSG_NETWORK, osNet.getName(), MSG_REMOVED));
+ }
+ }
+ }
+
+ @Override
+ public void createSubnet(Subnet osSubnet) {
+ checkNotNull(osSubnet, ERR_NULL_SUBNET);
+ checkArgument(!Strings.isNullOrEmpty(osSubnet.getId()), ERR_NULL_SUBNET_ID);
+ checkArgument(!Strings.isNullOrEmpty(osSubnet.getNetworkId()), ERR_NULL_SUBNET_NET_ID);
+ checkArgument(!Strings.isNullOrEmpty(osSubnet.getCidr()), ERR_NULL_SUBNET_CIDR);
+
+ osNetworkStore.createSubnet(osSubnet);
+ log.info(String.format(MSG_SUBNET, osSubnet.getCidr(), MSG_CREATED));
+ }
+
+ @Override
+ public void updateSubnet(Subnet osSubnet) {
+ checkNotNull(osSubnet, ERR_NULL_SUBNET);
+ checkArgument(!Strings.isNullOrEmpty(osSubnet.getId()), ERR_NULL_SUBNET_ID);
+ checkArgument(!Strings.isNullOrEmpty(osSubnet.getNetworkId()), ERR_NULL_SUBNET_NET_ID);
+ checkArgument(!Strings.isNullOrEmpty(osSubnet.getCidr()), ERR_NULL_SUBNET_CIDR);
+
+ osNetworkStore.updateSubnet(osSubnet);
+ log.info(String.format(MSG_SUBNET, osSubnet.getCidr(), MSG_UPDATED));
+ }
+
+ @Override
+ public void removeSubnet(String subnetId) {
+ checkArgument(!Strings.isNullOrEmpty(subnetId), ERR_NULL_SUBNET_ID);
+ synchronized (this) {
+ if (isSubnetInUse(subnetId)) {
+ final String error = String.format(MSG_SUBNET, subnetId, ERR_IN_USE);
+ throw new IllegalStateException(error);
+ }
+ Subnet osSubnet = osNetworkStore.removeSubnet(subnetId);
+ if (osSubnet != null) {
+ log.info(String.format(MSG_SUBNET, osSubnet.getCidr(), MSG_REMOVED));
+ }
+ }
+ }
+
+ @Override
+ public void createPort(Port osPort) {
+ checkNotNull(osPort, ERR_NULL_PORT);
+ checkArgument(!Strings.isNullOrEmpty(osPort.getId()), ERR_NULL_PORT_ID);
+ checkArgument(!Strings.isNullOrEmpty(osPort.getNetworkId()), ERR_NULL_PORT_NET_ID);
+
+ osNetworkStore.createPort(osPort);
+ log.info(String.format(MSG_PORT, osPort.getId(), MSG_CREATED));
+ }
+
+ @Override
+ public void updatePort(Port osPort) {
+ checkNotNull(osPort, ERR_NULL_PORT);
+ checkArgument(!Strings.isNullOrEmpty(osPort.getId()), ERR_NULL_PORT_ID);
+ checkArgument(!Strings.isNullOrEmpty(osPort.getNetworkId()), ERR_NULL_PORT_NET_ID);
+
+ osNetworkStore.updatePort(osPort);
+ log.info(String.format(MSG_SUBNET, osPort.getId(), MSG_UPDATED));
+ }
+
+ @Override
+ public void removePort(String portId) {
+ checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_PORT_ID);
+ synchronized (this) {
+ if (isPortInUse(portId)) {
+ final String error = String.format(MSG_PORT, portId, ERR_IN_USE);
+ throw new IllegalStateException(error);
+ }
+ Port osPort = osNetworkStore.removePort(portId);
+ if (osPort != null) {
+ log.info(String.format(MSG_SUBNET, osPort.getId(), MSG_REMOVED));
+ }
+ }
+ }
+
+ @Override
+ public Network network(String netId) {
+ checkArgument(!Strings.isNullOrEmpty(netId), ERR_NULL_NETWORK_ID);
+ return osNetworkStore.network(netId);
+ }
+
+ @Override
+ public Set<Network> networks() {
+ return osNetworkStore.networks();
+ }
+
+ @Override
+ public Subnet subnet(String subnetId) {
+ checkArgument(!Strings.isNullOrEmpty(subnetId), ERR_NULL_SUBNET_ID);
+ return osNetworkStore.subnet(subnetId);
+ }
+
+ @Override
+ public Set<Subnet> subnets() {
+ return osNetworkStore.subnets();
+ }
+
+ @Override
+ public Set<Subnet> subnets(String netId) {
+ Set<Subnet> osSubnets = osNetworkStore.subnets().stream()
+ .filter(subnet -> Objects.equals(subnet.getNetworkId(), netId))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osSubnets);
+ }
+
+ @Override
+ public Port port(String portId) {
+ checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_PORT_ID);
+ return osNetworkStore.port(portId);
+ }
+
+ @Override
+ public Port port(org.onosproject.net.Port port) {
+ String portName = port.annotations().value(PORT_NAME);
+ if (Strings.isNullOrEmpty(portName)) {
+ return null;
+ }
+ Optional<Port> osPort = osNetworkStore.ports()
+ .stream()
+ .filter(p -> p.getId().contains(portName.substring(3)))
+ .findFirst();
+ return osPort.isPresent() ? osPort.get() : null;
+ }
+
+ @Override
+ public Set<Port> ports() {
+ return osNetworkStore.ports();
+ }
+
+ @Override
+ public Set<Port> ports(String netId) {
+ Set<Port> osPorts = osNetworkStore.ports().stream()
+ .filter(port -> Objects.equals(port.getNetworkId(), netId))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osPorts);
+ }
+
+ private boolean isNetworkInUse(String netId) {
+ return !subnets(netId).isEmpty() && !ports(netId).isEmpty();
+ }
+
+ private boolean isSubnetInUse(String subnetId) {
+ // TODO add something if needed
+ return false;
+ }
+
+ private boolean isPortInUse(String portId) {
+ // TODO add something if needed
+ return false;
+ }
+
+ private class InternalNetworkStoreDelegate implements OpenstackNetworkStoreDelegate {
+
+ @Override
+ public void notify(OpenstackNetworkEvent event) {
+ if (event != null) {
+ log.trace("send oepnstack switching event {}", event);
+ process(event);
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java
deleted file mode 100644
index 5a5eafc..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * 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.impl;
-
-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.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.TCP;
-import org.onlab.packet.TpPort;
-import org.onlab.packet.UDP;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.net.packet.InboundPacket;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketService;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-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.slf4j.Logger;
-
-import java.nio.ByteBuffer;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Handle NAT packet processing for managing flow rules in openstack nodes.
- */
-@Component(immediate = true)
-public class OpenstackPnatHandler {
- private final Logger log = getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected PacketService packetService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostService hostService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected StorageService storageService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected FlowObjectiveService flowObjectiveService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackNodeService nodeService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ScalableGatewayService gatewayService;
-
- private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
- .register(KryoNamespaces.API);
-
- private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000;
- private static final int TP_PORT_MINIMUM_NUM = 1024;
- private static final int TP_PORT_MAXIMUM_NUM = 65535;
-
- private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
- groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
- private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
-
- private ConsistentMap<Integer, String> tpPortNumMap;
- private ApplicationId appId;
-
- @Activate
- protected void activate() {
- appId = coreService.registerApplication(ROUTING_APP_ID);
- tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
- .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
- .withName("openstackrouting-tpportnum")
- .withApplicationId(appId)
- .build();
-
- packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
- log.info("Started");
- }
-
- @Deactivate
- protected void deactivate() {
- packetService.removeProcessor(packetProcessor);
- log.info("Stopped");
- }
-
- private void processPnatPacket(PacketContext context, Ethernet ethernet) {
- IPv4 iPacket = (IPv4) ethernet.getPayload();
- InboundPacket inboundPacket = context.inPacket();
-
- int srcPort = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
- OpenstackPort osPort = getOpenstackPort(ethernet.getSourceMAC());
- if (osPort == null) {
- return;
- }
- Ip4Address externalGatewayIp = getExternalGatewayIp(osPort);
- if (externalGatewayIp == null) {
- return;
- }
-
- populatePnatFlowRules(context.inPacket(),
- osPort,
- TpPort.tpPort(srcPort),
- externalGatewayIp);
-
- packetOut((Ethernet) ethernet.clone(),
- inboundPacket.receivedFrom().deviceId(),
- srcPort,
- externalGatewayIp);
- }
-
- private void packetOut(Ethernet ethernet, DeviceId deviceId, int portNum, Ip4Address externalIp) {
- IPv4 iPacket = (IPv4) ethernet.getPayload();
- TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-
- switch (iPacket.getProtocol()) {
- case IPv4.PROTOCOL_TCP:
- TCP tcpPacket = (TCP) iPacket.getPayload();
- tcpPacket.setSourcePort(portNum);
- tcpPacket.resetChecksum();
- tcpPacket.setParent(iPacket);
- iPacket.setPayload(tcpPacket);
- break;
- case IPv4.PROTOCOL_UDP:
- UDP udpPacket = (UDP) iPacket.getPayload();
- udpPacket.setSourcePort(portNum);
- udpPacket.resetChecksum();
- udpPacket.setParent(iPacket);
- iPacket.setPayload(udpPacket);
- break;
- default:
- log.trace("Temporally, this method can process UDP and TCP protocol.");
- return;
- }
-
- iPacket.setSourceAddress(externalIp.toString());
- iPacket.resetChecksum();
- iPacket.setParent(ethernet);
- ethernet.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
- ethernet.setPayload(iPacket);
-
- treatment.setOutput(gatewayService.getUplinkPort(deviceId));
- ethernet.resetChecksum();
- packetService.emit(new DefaultOutboundPacket(
- deviceId,
- treatment.build(),
- ByteBuffer.wrap(ethernet.serialize())));
- }
-
- private int getPortNum(MacAddress sourceMac, int destinationAddress) {
- int portNum = findUnusedPortNum();
- if (portNum == 0) {
- clearPortNumMap();
- portNum = findUnusedPortNum();
- }
- tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
- return portNum;
- }
-
- private int findUnusedPortNum() {
- for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
- if (!tpPortNumMap.containsKey(i)) {
- return i;
- }
- }
- return 0;
- }
-
- private void clearPortNumMap() {
- tpPortNumMap.entrySet().forEach(e -> {
- if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) {
- tpPortNumMap.remove(e.getKey());
- }
- });
- }
-
- // TODO there can be multiple routers connected to a particular openstack port
- // TODO cache router information
- private Ip4Address getExternalGatewayIp(OpenstackPort osPort) {
- Optional<OpenstackPort> routerPort = openstackService.ports().stream()
- .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
- .filter(p -> checkSameSubnet(p, osPort))
- .findAny();
- if (!routerPort.isPresent()) {
- log.warn("No router is connected to network {}", osPort.networkId());
- return null;
- }
-
- OpenstackRouter osRouter = openstackService.router(routerPort.get().deviceId());
- if (osRouter == null) {
- log.warn("Failed to get OpenStack router {}",
- routerPort.get().deviceId());
- return null;
- }
-
- return osRouter.gatewayExternalInfo().externalFixedIps().values()
- .stream().findAny().orElse(null);
- }
-
- private OpenstackPort getOpenstackPort(MacAddress srcMac) {
- Optional<Host> host = hostService.getHostsByMac(srcMac).stream()
- .filter(h -> h.annotations().value(PORT_ID) != null)
- .findAny();
- if (!host.isPresent()) {
- log.warn("Failed to find a host with MAC:{}", srcMac);
- return null;
- }
- return openstackService.port(host.get().annotations().value(PORT_ID));
- }
-
- private boolean checkSameSubnet(OpenstackPort osPortA, OpenstackPort osPortB) {
- return osPortA.fixedIps().keySet().stream()
- .anyMatch(subnetId -> osPortB.fixedIps().keySet().contains(subnetId));
- }
-
- private void populatePnatFlowRules(InboundPacket inboundPacket,
- OpenstackPort osPort,
- TpPort patPort,
- Ip4Address externalIp) {
- long vni = getVni(osPort.networkId());
- populatePnatIncomingFlowRules(vni, externalIp, patPort, inboundPacket);
- populatePnatOutgoingFlowRules(vni, externalIp, patPort, inboundPacket);
- }
-
- private long getVni(String netId) {
- // TODO remove this and use host vxlan annotation if applicable
- return Long.parseLong(openstackService.network(netId).segmentId());
- }
-
- private void populatePnatOutgoingFlowRules(long vni, Ip4Address externalIp, TpPort patPort,
- InboundPacket inboundPacket) {
- IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPProtocol(iPacket.getProtocol())
- .matchTunnelId(vni)
- .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
- .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
-
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
- switch (iPacket.getProtocol()) {
- case IPv4.PROTOCOL_TCP:
- TCP tcpPacket = (TCP) iPacket.getPayload();
- sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
- .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
- tBuilder.setTcpSrc(patPort)
- .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
- break;
- case IPv4.PROTOCOL_UDP:
- UDP udpPacket = (UDP) iPacket.getPayload();
- sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
- .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
- tBuilder.setUdpSrc(patPort)
- .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
-
- break;
- default:
- log.debug("Unsupported IPv4 protocol {}");
- break;
- }
-
- tBuilder.setIpSrc(externalIp);
- gatewayService.getGatewayNodes().forEach(gateway -> {
- TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
- tmpBuilder.setOutput(gatewayService.getUplinkPort(gateway.getGatewayDeviceId()));
- ForwardingObjective fo = DefaultForwardingObjective.builder()
- .withSelector(sBuilder.build())
- .withTreatment(tmpBuilder.build())
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .withPriority(PNAT_RULE_PRIORITY)
- .makeTemporary(PNAT_TIMEOUT)
- .fromApp(appId)
- .add();
-
- flowObjectiveService.forward(gateway.getGatewayDeviceId(), fo);
- });
- }
-
- private void populatePnatIncomingFlowRules(long vni, Ip4Address externalIp, TpPort patPort,
- InboundPacket inboundPacket) {
- IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
- IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPProtocol(iPacket.getProtocol())
- .matchIPDst(IpPrefix.valueOf(externalIp, 32))
- .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
-
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
- tBuilder.setTunnelId(vni)
- .setEthDst(inboundPacket.parsed().getSourceMAC())
- .setIpDst(internalIp);
-
- switch (iPacket.getProtocol()) {
- case IPv4.PROTOCOL_TCP:
- TCP tcpPacket = (TCP) iPacket.getPayload();
- sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
- .matchTcpDst(patPort);
- tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
- break;
- case IPv4.PROTOCOL_UDP:
- UDP udpPacket = (UDP) iPacket.getPayload();
- sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
- .matchUdpDst(patPort);
- tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
- break;
- default:
- break;
- }
-
- Optional<Host> srcVm = Tools.stream(hostService.getHostsByIp(internalIp))
- .filter(host -> Objects.equals(
- host.annotations().value(VXLAN_ID),
- String.valueOf(vni)))
- .findFirst();
- if (!srcVm.isPresent()) {
- log.warn("Failed to find source VM with IP {}", internalIp);
- return;
- }
-
- gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
- DeviceId srcDeviceId = srcVm.get().location().deviceId();
- TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
- tmpBuilder.extension(RulePopulatorUtil.buildExtension(
- deviceService,
- deviceId,
- nodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId)
- .setOutput(nodeService.tunnelPort(deviceId).get());
-
- ForwardingObjective fo = DefaultForwardingObjective.builder()
- .withSelector(sBuilder.build())
- .withTreatment(tmpBuilder.build())
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .withPriority(PNAT_RULE_PRIORITY)
- .makeTemporary(PNAT_TIMEOUT)
- .fromApp(appId)
- .add();
-
- flowObjectiveService.forward(deviceId, fo);
- });
- }
-
- private class InternalPacketProcessor implements PacketProcessor {
-
- @Override
- public void process(PacketContext context) {
- if (context.isHandled()) {
- return;
- } else if (!gatewayService.getGatewayDeviceIds().contains(
- context.inPacket().receivedFrom().deviceId())) {
- // return if the packet is not from gateway nodes
- return;
- }
-
- InboundPacket pkt = context.inPacket();
- Ethernet ethernet = pkt.parsed();
- if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
- return;
- }
-
- IPv4 iPacket = (IPv4) ethernet.getPayload();
- switch (iPacket.getProtocol()) {
- case IPv4.PROTOCOL_ICMP:
- break;
- case IPv4.PROTOCOL_UDP:
- UDP udpPacket = (UDP) iPacket.getPayload();
- if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
- udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
- // don't process DHCP
- break;
- }
- default:
- eventExecutor.execute(() -> processPnatPacket(context, ethernet));
- break;
- }
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java
new file mode 100644
index 0000000..0d74940
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2017-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.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.apache.felix.scr.annotations.Service;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStore;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStoreDelegate;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing OpenStack router and
+ * floating IP address.
+ */
+@Service
+@Component(immediate = true)
+public class OpenstackRouterManager
+ extends ListenerRegistry<OpenstackRouterEvent, OpenstackRouterListener>
+ implements OpenstackRouterAdminService, OpenstackRouterService {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String MSG_ROUTER = "OpenStack router %s %s";
+ private static final String MSG_ROUTER_IFACE = "OpenStack router interface %s %s";
+ private static final String MSG_FLOATING_IP = "OpenStack floating IP %s %s";
+ private static final String MSG_CREATED = "created";
+ private static final String MSG_UPDATED = "updated";
+ private static final String MSG_REMOVED = "removed";
+
+ private static final String ERR_NULL_ROUTER = "OpenStack router cannot be null";
+ private static final String ERR_NULL_ROUTER_ID = "OpenStack router ID cannot be null";
+ private static final String ERR_NULL_ROUTER_NAME = "OpenStack router name cannot be null";
+ private static final String ERR_NULL_IFACE = "OpenStack router interface cannot be null";
+ private static final String ERR_NULL_IFACE_ROUTER_ID = "OpenStack router interface router ID cannot be null";
+ private static final String ERR_NULL_IFACE_PORT_ID = "OpenStack router interface port ID cannot be null";
+ private static final String ERR_NULL_FLOATING = "OpenStack floating IP cannot be null";
+ private static final String ERR_NULL_FLOATING_ID = "OpenStack floating IP cannot be null";
+
+ private static final String ERR_IN_USE = " still in use";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackRouterStore osRouterStore;
+
+ private final OpenstackRouterStoreDelegate delegate = new InternalRouterStoreDelegate();
+
+ @Activate
+ protected void activate() {
+ coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+ osRouterStore.setDelegate(delegate);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osRouterStore.unsetDelegate(delegate);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void createRouter(Router osRouter) {
+ checkNotNull(osRouter, ERR_NULL_ROUTER);
+ checkArgument(!Strings.isNullOrEmpty(osRouter.getId()), ERR_NULL_ROUTER_ID);
+ checkArgument(!Strings.isNullOrEmpty(osRouter.getName()), ERR_NULL_ROUTER_NAME);
+
+ osRouterStore.createRouter(osRouter);
+ log.info(String.format(MSG_ROUTER, osRouter.getName(), MSG_CREATED));
+ }
+
+ @Override
+ public void updateRouter(Router osRouter) {
+ checkNotNull(osRouter, ERR_NULL_ROUTER);
+ checkArgument(!Strings.isNullOrEmpty(osRouter.getId()), ERR_NULL_ROUTER_ID);
+ checkArgument(!Strings.isNullOrEmpty(osRouter.getName()), ERR_NULL_ROUTER_NAME);
+
+ osRouterStore.updateRouter(osRouter);
+ log.info(String.format(MSG_ROUTER, osRouter.getId(), MSG_UPDATED));
+ }
+
+ @Override
+ public void removeRouter(String routerId) {
+ checkArgument(!Strings.isNullOrEmpty(routerId), ERR_NULL_ROUTER_ID);
+ synchronized (this) {
+ if (isRouterInUse(routerId)) {
+ final String error = String.format(MSG_ROUTER, routerId, ERR_IN_USE);
+ throw new IllegalStateException(error);
+ }
+ Router osRouter = osRouterStore.removeRouter(routerId);
+ if (osRouter != null) {
+ log.info(String.format(MSG_ROUTER, osRouter.getName(), MSG_REMOVED));
+ }
+ }
+ }
+
+ @Override
+ public void addRouterInterface(RouterInterface osIface) {
+ checkNotNull(osIface, ERR_NULL_IFACE);
+ checkArgument(!Strings.isNullOrEmpty(osIface.getId()), ERR_NULL_IFACE_ROUTER_ID);
+ checkArgument(!Strings.isNullOrEmpty(osIface.getPortId()), ERR_NULL_IFACE_PORT_ID);
+
+ osRouterStore.addRouterInterface(osIface);
+ log.info(String.format(MSG_ROUTER_IFACE, osIface.getPortId(), MSG_CREATED));
+ }
+
+ @Override
+ public void updateRouterInterface(RouterInterface osIface) {
+ checkNotNull(osIface, ERR_NULL_IFACE);
+ checkArgument(!Strings.isNullOrEmpty(osIface.getId()), ERR_NULL_IFACE_ROUTER_ID);
+ checkArgument(!Strings.isNullOrEmpty(osIface.getPortId()), ERR_NULL_IFACE_PORT_ID);
+
+ osRouterStore.updateRouterInterface(osIface);
+ log.info(String.format(MSG_ROUTER_IFACE, osIface.getPortId(), MSG_UPDATED));
+ }
+
+ @Override
+ public void removeRouterInterface(String osIfaceId) {
+ checkArgument(!Strings.isNullOrEmpty(osIfaceId), ERR_NULL_IFACE_PORT_ID);
+ synchronized (this) {
+ if (isRouterIfaceInUse(osIfaceId)) {
+ final String error = String.format(MSG_ROUTER, osIfaceId, ERR_IN_USE);
+ throw new IllegalStateException(error);
+ }
+ RouterInterface osIface = osRouterStore.removeRouterInterface(osIfaceId);
+ if (osIface != null) {
+ log.info(String.format(MSG_ROUTER_IFACE, osIface.getPortId(), MSG_REMOVED));
+ }
+ }
+ }
+
+ @Override
+ public void createFloatingIp(NetFloatingIP osFloatingIp) {
+ checkNotNull(osFloatingIp, ERR_NULL_FLOATING);
+ checkArgument(!Strings.isNullOrEmpty(osFloatingIp.getId()), ERR_NULL_FLOATING_ID);
+
+ osRouterStore.createFloatingIp(osFloatingIp);
+ log.info(String.format(MSG_FLOATING_IP, osFloatingIp.getId(), MSG_CREATED));
+ }
+
+ @Override
+ public void updateFloatingIp(NetFloatingIP osFloatingIp) {
+ checkNotNull(osFloatingIp, ERR_NULL_FLOATING);
+ checkArgument(!Strings.isNullOrEmpty(osFloatingIp.getId()), ERR_NULL_FLOATING_ID);
+
+ osRouterStore.updateFloatingIp(osFloatingIp);
+ log.info(String.format(MSG_FLOATING_IP, osFloatingIp.getId(), MSG_UPDATED));
+ }
+
+ @Override
+ public void removeFloatingIp(String floatingIpId) {
+ checkArgument(!Strings.isNullOrEmpty(floatingIpId), ERR_NULL_FLOATING_ID);
+ synchronized (this) {
+ if (isFloatingIpInUse(floatingIpId)) {
+ final String error = String.format(MSG_FLOATING_IP, floatingIpId, ERR_IN_USE);
+ throw new IllegalStateException(error);
+ }
+ NetFloatingIP osFloatingIp = osRouterStore.removeFloatingIp(floatingIpId);
+ if (osFloatingIp != null) {
+ log.info(String.format(MSG_FLOATING_IP, osFloatingIp.getId(), MSG_REMOVED));
+ }
+ }
+ }
+
+ @Override
+ public Router router(String routerId) {
+ checkArgument(!Strings.isNullOrEmpty(routerId), ERR_NULL_ROUTER_ID);
+ return osRouterStore.router(routerId);
+ }
+
+ @Override
+ public Set<Router> routers() {
+ return osRouterStore.routers();
+ }
+
+ @Override
+ public RouterInterface routerInterface(String osIfaceId) {
+ checkArgument(!Strings.isNullOrEmpty(osIfaceId), ERR_NULL_IFACE_PORT_ID);
+ return osRouterStore.routerInterface(osIfaceId);
+ }
+
+ @Override
+ public Set<RouterInterface> routerInterfaces() {
+ return osRouterStore.routerInterfaces();
+ }
+
+ @Override
+ public Set<RouterInterface> routerInterfaces(String routerId) {
+ Set<RouterInterface> osIfaces = osRouterStore.routerInterfaces().stream()
+ .filter(iface -> Objects.equals(iface.getId(), routerId))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osIfaces);
+ }
+
+ @Override
+ public NetFloatingIP floatingIp(String floatingIpId) {
+ checkArgument(!Strings.isNullOrEmpty(floatingIpId), ERR_NULL_FLOATING_ID);
+ return osRouterStore.floatingIp(floatingIpId);
+ }
+
+ @Override
+ public Set<NetFloatingIP> floatingIps() {
+ return osRouterStore.floatingIps();
+ }
+
+ @Override
+ public Set<NetFloatingIP> floatingIps(String routerId) {
+ Set<NetFloatingIP> osFloatingIps = osRouterStore.floatingIps().stream()
+ .filter(fip -> Objects.equals(fip.getRouterId(), routerId))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osFloatingIps);
+ }
+
+ private boolean isRouterInUse(String routerId) {
+ // TODO add checking
+ return false;
+ }
+
+ private boolean isRouterIfaceInUse(String osIfaceId) {
+ // TODO add checking
+ return false;
+ }
+
+ private boolean isFloatingIpInUse(String floatingIpId) {
+ // TODO add checking
+ return false;
+ }
+
+ private class InternalRouterStoreDelegate implements OpenstackRouterStoreDelegate {
+
+ @Override
+ public void notify(OpenstackRouterEvent event) {
+ if (event != null) {
+ log.trace("send oepnstack routing event {}", event);
+ process(event);
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 0bf93dc..03810c3 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -32,39 +32,41 @@
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.OpenstackPort;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
import org.onosproject.scalablegateway.api.ScalableGatewayService;
import org.onosproject.openstacknetworking.api.Constants;
import org.slf4j.Logger;
import java.nio.ByteBuffer;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.DEVICE_OWNER_FLOATING_IP;
-import static org.onosproject.openstacknetworking.api.Constants.DEVICE_OWNER_ROUTER_GATEWAY;
import static org.slf4j.LoggerFactory.getLogger;
/**
- * Handle ARP, ICMP and NAT packets from gateway nodes.
+ * Handle ARP requests from gateway nodes.
*/
@Component(immediate = true)
public class OpenstackRoutingArpHandler {
+
private final Logger log = getLogger(getClass());
+ private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
+ private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ScalableGatewayService gatewayService;
- private final ExecutorService executorService =
- newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "packet-event", log));
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
@@ -77,21 +79,25 @@
@Deactivate
protected void deactivate() {
packetService.removeProcessor(packetProcessor);
+ eventExecutor.shutdown();
log.info("Stopped");
}
private void processArpPacket(PacketContext context, Ethernet ethernet) {
ARP arp = (ARP) ethernet.getPayload();
- log.trace("arpEvent called from {} to {}",
- Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
- Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
-
if (arp.getOpCode() != ARP.OP_REQUEST) {
return;
}
+ if (log.isTraceEnabled()) {
+ log.trace("ARP request received from {} for {}",
+ Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
+ Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
+ }
+
IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
- if (getTargetMacForTargetIp(targetIp.getIp4Address()) == MacAddress.NONE) {
+ if (!isServiceIp(targetIp.getIp4Address())) {
+ log.trace("Unknown target ARP request for {}, ignore it", targetIp);
return;
}
@@ -107,6 +113,8 @@
context.inPacket().receivedFrom().deviceId(),
treatment,
ByteBuffer.wrap(ethReply.serialize())));
+
+ context.block();
}
private class InternalPacketProcessor implements PacketProcessor {
@@ -125,19 +133,20 @@
Ethernet ethernet = pkt.parsed();
if (ethernet != null &&
ethernet.getEtherType() == Ethernet.TYPE_ARP) {
- executorService.execute(() -> processArpPacket(context, ethernet));
+ eventExecutor.execute(() -> processArpPacket(context, ethernet));
}
}
}
- // TODO make a cache for the MAC, not a good idea to REST call every time it gets ARP request
- private MacAddress getTargetMacForTargetIp(Ip4Address targetIp) {
- OpenstackPort port = openstackService.ports().stream()
- .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_GATEWAY) ||
- p.deviceOwner().equals(DEVICE_OWNER_FLOATING_IP))
- .filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address()))
- .findAny().orElse(null);
-
- return port == null ? MacAddress.NONE : port.macAddress();
+ private boolean isServiceIp(IpAddress targetIp) {
+ // FIXME use floating IP and external gateway information of router instead
+ // once openstack4j fixed
+ return osNetworkService.ports().stream()
+ .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
+ DEVICE_OWNER_ROUTER_GW) ||
+ Objects.equals(osPort.getDeviceOwner(),
+ DEVICE_OWNER_FLOATING_IP))
+ .flatMap(osPort -> osPort.getFixedIps().stream())
+ .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
}
}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
new file mode 100644
index 0000000..8594aea
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -0,0 +1,364 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Strings;
+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.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+
+/**
+ * Handles OpenStack floating IP events.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingFloatingIpHandler {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InstancePortService instancePortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackRouterService osRouterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ScalableGatewayService gatewayService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+ private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpLisener();
+ private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
+
+ private ApplicationId appId;
+ private NodeId localNodeId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+ localNodeId = clusterService.getLocalNode().id();
+ leadershipService.runForLeadership(appId.name());
+ osRouterService.addListener(floatingIpLisener);
+ osNodeService.addListener(osNodeListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osNodeService.removeListener(osNodeListener);
+ osRouterService.removeListener(floatingIpLisener);
+ leadershipService.withdraw(appId.name());
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ private void floatingIpUpdated(NetFloatingIP floatingIp, String portId) {
+ Port osPort = osNetworkService.port(portId);
+ if (osPort == null) {
+ final String error = String.format(ERR_FLOW + "no port(%s) exists",
+ floatingIp.getFloatingIpAddress(),
+ floatingIp.getPortId());
+ throw new IllegalStateException(error);
+ }
+
+ if (Strings.isNullOrEmpty(floatingIp.getPortId())) {
+ setFloatingIpRules(floatingIp, osPort, false);
+ log.info("Disassociated floating IP:{} from fixed IP:{}",
+ floatingIp.getFloatingIpAddress(),
+ osPort.getFixedIps());
+ } else {
+ setFloatingIpRules(floatingIp, osPort, true);
+ log.info("Associated floating IP:{} to fixed IP:{}",
+ floatingIp.getFloatingIpAddress(),
+ floatingIp.getFixedIpAddress());
+ }
+ }
+
+ private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
+ boolean install) {
+ Network osNet = osNetworkService.network(osPort.getNetworkId());
+ if (osNet == null) {
+ final String error = String.format(ERR_FLOW + "no network(%s) exists",
+ floatingIp.getFloatingIpAddress(),
+ osPort.getNetworkId());
+ throw new IllegalStateException(error);
+ }
+
+ MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
+ InstancePort instPort = instancePortService.instancePort(srcMac);
+ if (instPort == null) {
+ final String error = String.format(ERR_FLOW + "no host(MAC:%s) found",
+ floatingIp.getFloatingIpAddress(), srcMac);
+ throw new IllegalStateException(error);
+ }
+
+ setDownstreamRules(floatingIp, osNet, instPort, install);
+ setUpstreamRules(floatingIp, osNet, instPort, install);
+ }
+
+ private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
+ InstancePort instPort, boolean install) {
+ Optional<IpAddress> dataIp = osNodeService.dataIp(instPort.deviceId());
+ if (!dataIp.isPresent()) {
+ log.warn(ERR_FLOW + "compute node {} is not ready",
+ floatingIp, instPort.deviceId());
+ return;
+ }
+
+ IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
+ TrafficSelector externalSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(floating.toIpPrefix())
+ .build();
+
+ gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
+ TrafficTreatment externalTreatment = DefaultTrafficTreatment.builder()
+ .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
+ .setEthDst(instPort.macAddress())
+ .setIpDst(instPort.ipAddress().getIp4Address())
+ .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
+ .extension(buildExtension(
+ deviceService,
+ gnodeId,
+ dataIp.get().getIp4Address()),
+ gnodeId)
+ .setOutput(osNodeService.tunnelPort(gnodeId).get())
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ gnodeId,
+ externalSelector,
+ externalTreatment,
+ ForwardingObjective.Flag.VERSATILE,
+ PRIORITY_FLOATING_EXTERNAL,
+ install);
+
+ // access from one VM to the other via floating IP
+ TrafficSelector internalSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(floating.toIpPrefix())
+ .matchInPort(osNodeService.tunnelPort(gnodeId).get())
+ .build();
+
+ TrafficTreatment internalTreatment = DefaultTrafficTreatment.builder()
+ .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
+ .setEthDst(instPort.macAddress())
+ .setIpDst(instPort.ipAddress().getIp4Address())
+ .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
+ .extension(buildExtension(
+ deviceService,
+ gnodeId,
+ dataIp.get().getIp4Address()),
+ gnodeId)
+ .setOutput(PortNumber.IN_PORT)
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ gnodeId,
+ internalSelector,
+ internalTreatment,
+ ForwardingObjective.Flag.VERSATILE,
+ PRIORITY_FLOATING_INTERNAL,
+ install);
+ });
+ }
+
+ private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
+ InstancePort instPort, boolean install) {
+ IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(Long.valueOf(osNet.getProviderSegID()))
+ .matchIPSrc(instPort.ipAddress().toIpPrefix())
+ .build();
+
+ gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setIpSrc(floating.getIp4Address())
+ .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
+ .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
+ .setOutput(gatewayService.getUplinkPort(gnodeId))
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ gnodeId,
+ selector,
+ treatment,
+ ForwardingObjective.Flag.VERSATILE,
+ PRIORITY_FLOATING_EXTERNAL,
+ install);
+ });
+ }
+
+ private class InternalFloatingIpLisener implements OpenstackRouterListener {
+
+ @Override
+ public boolean isRelevant(OpenstackRouterEvent event) {
+ // do not allow to proceed without leadership
+ NodeId leader = leadershipService.getLeader(appId.name());
+ if (!Objects.equals(localNodeId, leader)) {
+ return false;
+ }
+ return event.floatingIp() != null;
+ }
+
+ @Override
+ public void event(OpenstackRouterEvent event) {
+ switch (event.type()) {
+ case OPENSTACK_FLOATING_IP_ASSOCIATED:
+ case OPENSTACK_FLOATING_IP_DISASSOCIATED:
+ eventExecutor.execute(() -> {
+ NetFloatingIP fip = event.floatingIp();
+ log.debug("Floating IP {} is updated", fip.getFloatingIpAddress());
+ floatingIpUpdated(fip, event.portId());
+ });
+ break;
+ case OPENSTACK_FLOATING_IP_CREATED:
+ log.debug("Floating IP {} is created",
+ event.floatingIp().getFloatingIpAddress());
+ break;
+ case OPENSTACK_FLOATING_IP_UPDATED:
+ log.debug("Floating IP {} is updated",
+ event.floatingIp().getFloatingIpAddress());
+ break;
+ case OPENSTACK_FLOATING_IP_REMOVED:
+ log.debug("Floating IP {} is removed",
+ event.floatingIp().getFloatingIpAddress());
+ break;
+ case OPENSTACK_ROUTER_CREATED:
+ case OPENSTACK_ROUTER_UPDATED:
+ case OPENSTACK_ROUTER_REMOVED:
+ case OPENSTACK_ROUTER_INTERFACE_ADDED:
+ case OPENSTACK_ROUTER_INTERFACE_UPDATED:
+ case OPENSTACK_ROUTER_INTERFACE_REMOVED:
+ default:
+ // do nothing for the other events
+ break;
+ }
+ }
+ }
+
+ private class InternalNodeListener implements OpenstackNodeListener {
+
+ @Override
+ public boolean isRelevant(OpenstackNodeEvent event) {
+ // do not allow to proceed without leadership
+ NodeId leader = leadershipService.getLeader(appId.name());
+ if (!Objects.equals(localNodeId, leader)) {
+ return false;
+ }
+ return event.subject().type() == GATEWAY;
+ }
+
+ @Override
+ public void event(OpenstackNodeEvent event) {
+ OpenstackNode osNode = event.subject();
+
+ switch (event.type()) {
+ case COMPLETE:
+ eventExecutor.execute(() -> {
+ log.info("GATEWAY node {} detected", osNode.hostname());
+ osRouterService.floatingIps().stream()
+ .filter(fip -> !Strings.isNullOrEmpty(fip.getPortId()))
+ .forEach(fip -> {
+ floatingIpUpdated(fip, fip.getPortId());
+ });
+ });
+ break;
+ case INIT:
+ case DEVICE_CREATED:
+ case INCOMPLETE:
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
new file mode 100644
index 0000000..7d722ec
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -0,0 +1,543 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+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.api.Constants.*;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
+
+/**
+ * Handles OpenStack router events.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingHandler {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String MSG_ENABLED = "Enabled ";
+ private static final String MSG_DISABLED = "Disabled ";
+ private static final String ERR_SET_FLOWS = "Failed to set flows for router %s:";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ScalableGatewayService gatewayService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackRouterService osRouterService;
+
+ private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+ private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
+ private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
+
+ private ApplicationId appId;
+ private NodeId localNodeId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+ localNodeId = clusterService.getLocalNode().id();
+ leadershipService.runForLeadership(appId.name());
+ osNodeService.addListener(osNodeListener);
+ osRouterService.addListener(osRouterListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osRouterService.removeListener(osRouterListener);
+ osNodeService.removeListener(osNodeListener);
+ leadershipService.withdraw(appId.name());
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ private void routerUpdated(Router osRouter) {
+ ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
+ if (exGateway == null) {
+ osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
+ setSourceNat(iface, false);
+ });
+ } else {
+ osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
+ setSourceNat(iface, exGateway.isEnableSnat());
+ });
+ }
+ }
+
+ private void routerIfaceAdded(Router osRouter, RouterInterface osRouterIface) {
+ Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
+ if (osSubnet == null) {
+ final String error = String.format(
+ ERR_SET_FLOWS + "subnet %s does not exist",
+ osRouterIface.getId(),
+ osRouterIface.getSubnetId());
+ throw new IllegalStateException(error);
+ }
+
+ setInternalRoutes(osRouter, osSubnet, true);
+ setGatewayIcmp(osSubnet, true);
+ ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
+ if (exGateway != null && exGateway.isEnableSnat()) {
+ setSourceNat(osRouterIface, true);
+ }
+
+ log.info("Connected subnet({}) to {}", osSubnet.getCidr(), osRouter.getName());
+ }
+
+ private void routerIfaceRemoved(Router osRouter, RouterInterface osRouterIface) {
+ Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
+ if (osSubnet == null) {
+ final String error = String.format(
+ ERR_SET_FLOWS + "subnet %s does not exist",
+ osRouterIface.getId(),
+ osRouterIface.getSubnetId());
+ throw new IllegalStateException(error);
+ }
+
+ setInternalRoutes(osRouter, osSubnet, false);
+ setGatewayIcmp(osSubnet, false);
+ ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
+ if (exGateway != null && exGateway.isEnableSnat()) {
+ setSourceNat(osRouterIface, false);
+ }
+
+ log.info("Disconnected subnet({}) from {}", osSubnet.getCidr(), osRouter.getName());
+ }
+
+ private void setSourceNat(RouterInterface routerIface, boolean install) {
+ Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
+ Network osNet = osNetworkService.network(osSubnet.getNetworkId());
+
+ osNodeService.completeNodes().stream()
+ .filter(osNode -> osNode.type() == COMPUTE)
+ .forEach(osNode -> {
+ setRulesToGateway(
+ osNode.intBridge(),
+ gatewayService.getGatewayGroupId(osNode.intBridge()),
+ Long.valueOf(osNet.getProviderSegID()),
+ IpPrefix.valueOf(osSubnet.getCidr()),
+ install);
+ });
+
+ // take the first outgoing packet to controller for source NAT
+ gatewayService.getGatewayDeviceIds()
+ .forEach(gwDeviceId -> setRulesToController(
+ gwDeviceId,
+ Long.valueOf(osNet.getProviderSegID()),
+ IpPrefix.valueOf(osSubnet.getCidr()),
+ install));
+
+ final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
+ log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
+ }
+
+ private void setGatewayIcmp(Subnet osSubnet, boolean install) {
+ if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+ // do nothing if no gateway is set
+ return;
+ }
+
+ // take ICMP request to a subnet gateway through gateway node group
+ Network network = osNetworkService.network(osSubnet.getNetworkId());
+ osNodeService.completeNodes().stream()
+ .filter(osNode -> osNode.type() == COMPUTE)
+ .forEach(osNode -> setRulesToGatewayWithDstIp(
+ osNode.intBridge(),
+ gatewayService.getGatewayGroupId(osNode.intBridge()),
+ Long.valueOf(network.getProviderSegID()),
+ IpAddress.valueOf(osSubnet.getGateway()),
+ install));
+
+ IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+ gatewayService.getGatewayDeviceIds()
+ .forEach(gwDeviceId -> setGatewayIcmpRule(
+ gatewayIp,
+ gwDeviceId,
+ install
+ ));
+
+ final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
+ log.debug(updateStr + "ICMP to {}", osSubnet.getGateway());
+ }
+
+ private void setInternalRoutes(Router osRouter, Subnet updatedSubnet, boolean install) {
+ Set<Subnet> routableSubnets = routableSubnets(osRouter, updatedSubnet.getId());
+ Long updatedVni = getVni(updatedSubnet);
+
+ // installs rule from/to my subnet intentionally to fix ICMP failure
+ // to my subnet gateway if no external gateway added to the router
+ osNodeService.completeNodes().stream()
+ .filter(osNode -> osNode.type() == COMPUTE)
+ .forEach(osNode -> {
+ setInternalRouterRules(
+ osNode.intBridge(),
+ updatedVni,
+ updatedVni,
+ IpPrefix.valueOf(updatedSubnet.getCidr()),
+ IpPrefix.valueOf(updatedSubnet.getCidr()),
+ install
+ );
+
+ routableSubnets.forEach(subnet -> {
+ setInternalRouterRules(
+ osNode.intBridge(),
+ updatedVni,
+ getVni(subnet),
+ IpPrefix.valueOf(updatedSubnet.getCidr()),
+ IpPrefix.valueOf(subnet.getCidr()),
+ install
+ );
+ setInternalRouterRules(
+ osNode.intBridge(),
+ getVni(subnet),
+ updatedVni,
+ IpPrefix.valueOf(subnet.getCidr()),
+ IpPrefix.valueOf(updatedSubnet.getCidr()),
+ install
+ );
+ });
+ });
+
+ final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
+ routableSubnets.forEach(subnet -> log.debug(
+ updateStr + "route between subnet:{} and subnet:{}",
+ subnet.getCidr(),
+ updatedSubnet.getCidr()));
+ }
+
+ private Set<Subnet> routableSubnets(Router osRouter, String osSubnetId) {
+ Set<Subnet> osSubnets = osRouterService.routerInterfaces(osRouter.getId())
+ .stream()
+ .filter(iface -> !Objects.equals(iface.getSubnetId(), osSubnetId))
+ .map(iface -> osNetworkService.subnet(iface.getSubnetId()))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osSubnets);
+ }
+
+ private Long getVni(Subnet osSubnet) {
+ return Long.parseLong(osNetworkService.network(
+ osSubnet.getNetworkId()).getProviderSegID());
+ }
+
+ private void setGatewayIcmpRule(IpAddress gatewayIp, DeviceId deviceId, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .matchIPDst(gatewayIp.toIpPrefix())
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.CONTROLLER)
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ selector,
+ treatment,
+ ForwardingObjective.Flag.VERSATILE,
+ PRIORITY_ICMP_RULE,
+ install);
+ }
+
+ private void setInternalRouterRules(DeviceId deviceId, Long srcVni, Long dstVni,
+ IpPrefix srcSubnet, IpPrefix dstSubnet, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(srcVni)
+ .matchIPSrc(srcSubnet)
+ .matchIPDst(dstSubnet)
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setTunnelId(dstVni)
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ selector,
+ treatment,
+ ForwardingObjective.Flag.SPECIFIC,
+ PRIORITY_INTERNAL_ROUTING_RULE,
+ install);
+
+ selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(dstVni)
+ .matchIPSrc(srcSubnet)
+ .matchIPDst(dstSubnet)
+ .build();
+
+ treatment = DefaultTrafficTreatment.builder()
+ .setTunnelId(dstVni)
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ selector,
+ treatment,
+ ForwardingObjective.Flag.SPECIFIC,
+ PRIORITY_INTERNAL_ROUTING_RULE,
+ install);
+ }
+
+ private void setRulesToGateway(DeviceId deviceId, GroupId groupId, Long vni,
+ IpPrefix srcSubnet, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(vni)
+ .matchIPSrc(srcSubnet)
+ .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .group(groupId)
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ selector,
+ treatment,
+ ForwardingObjective.Flag.SPECIFIC,
+ PRIORITY_EXTERNAL_ROUTING_RULE,
+ install);
+ }
+
+ private void setRulesToController(DeviceId deviceId, Long vni, IpPrefix srcSubnet, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(vni)
+ .matchIPSrc(srcSubnet)
+ .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.CONTROLLER)
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ selector,
+ treatment,
+ ForwardingObjective.Flag.VERSATILE,
+ PRIORITY_EXTERNAL_ROUTING_RULE,
+ install);
+ }
+
+ private void setRulesToGatewayWithDstIp(DeviceId deviceId, GroupId groupId, Long vni,
+ IpAddress dstIp, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(vni)
+ .matchIPDst(dstIp.toIpPrefix())
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .group(groupId)
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ selector,
+ treatment,
+ ForwardingObjective.Flag.SPECIFIC,
+ PRIORITY_SWITCHING_RULE,
+ install);
+ }
+
+ private class InternalRouterEventListener implements OpenstackRouterListener {
+
+ @Override
+ public boolean isRelevant(OpenstackRouterEvent event) {
+ // do not allow to proceed without leadership
+ NodeId leader = leadershipService.getLeader(appId.name());
+ return Objects.equals(localNodeId, leader);
+ }
+
+ // FIXME only one leader in the cluster should process
+ @Override
+ public void event(OpenstackRouterEvent event) {
+ switch (event.type()) {
+ case OPENSTACK_ROUTER_CREATED:
+ log.debug("Router(name:{}, ID:{}) is created",
+ event.subject().getName(),
+ event.subject().getId());
+ eventExecutor.execute(() -> routerUpdated(event.subject()));
+ break;
+ case OPENSTACK_ROUTER_UPDATED:
+ log.debug("Router(name:{}, ID:{}) is updated",
+ event.subject().getName(),
+ event.subject().getId());
+ eventExecutor.execute(() -> routerUpdated(event.subject()));
+ break;
+ case OPENSTACK_ROUTER_REMOVED:
+ log.debug("Router(name:{}, ID:{}) is removed",
+ event.subject().getName(),
+ event.subject().getId());
+ break;
+ case OPENSTACK_ROUTER_INTERFACE_ADDED:
+ log.debug("Router interface {} added to router {}",
+ event.routerIface().getPortId(),
+ event.routerIface().getId());
+ eventExecutor.execute(() -> routerIfaceAdded(
+ event.subject(),
+ event.routerIface()));
+ break;
+ case OPENSTACK_ROUTER_INTERFACE_UPDATED:
+ log.debug("Router interface {} on {} updated",
+ event.routerIface().getPortId(),
+ event.routerIface().getId());
+ break;
+ case OPENSTACK_ROUTER_INTERFACE_REMOVED:
+ log.debug("Router interface {} removed from router {}",
+ event.routerIface().getPortId(),
+ event.routerIface().getId());
+ eventExecutor.execute(() -> routerIfaceRemoved(
+ event.subject(),
+ event.routerIface()));
+ break;
+ case OPENSTACK_ROUTER_GATEWAY_ADDED:
+ case OPENSTACK_ROUTER_GATEWAY_REMOVED:
+ case OPENSTACK_FLOATING_IP_CREATED:
+ case OPENSTACK_FLOATING_IP_UPDATED:
+ case OPENSTACK_FLOATING_IP_REMOVED:
+ case OPENSTACK_FLOATING_IP_ASSOCIATED:
+ case OPENSTACK_FLOATING_IP_DISASSOCIATED:
+ default:
+ // do nothing for the other events
+ break;
+ }
+ }
+ }
+
+ private class InternalNodeEventListener implements OpenstackNodeListener {
+
+ @Override
+ public boolean isRelevant(OpenstackNodeEvent event) {
+ // do not allow to proceed without leadership
+ NodeId leader = leadershipService.getLeader(appId.name());
+ return Objects.equals(localNodeId, leader);
+ }
+
+ @Override
+ public void event(OpenstackNodeEvent event) {
+ OpenstackNode osNode = event.subject();
+
+ switch (event.type()) {
+ case COMPLETE:
+ case INCOMPLETE:
+ eventExecutor.execute(() -> {
+ log.info("COMPLETE node {} detected", osNode.hostname());
+ reconfigureRouters();
+ });
+ break;
+ case INIT:
+ case DEVICE_CREATED:
+ default:
+ break;
+ }
+ }
+
+ private void reconfigureRouters() {
+ osRouterService.routers().forEach(osRouter -> {
+ routerUpdated(osRouter);
+ osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
+ routerIfaceAdded(osRouter, iface);
+ });
+ });
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
new file mode 100644
index 0000000..2cafcd6
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
@@ -0,0 +1,446 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+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.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.GatewayNode;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+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.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Handles ICMP packet received from a gateway node.
+ * For a request for virtual network subnet gateway, it generates fake ICMP reply.
+ * For a request for the external network, it does source NAT with the public IP and
+ * forward the request to the external only if the requested virtual subnet has
+ * external connectivity.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingIcmpHandler {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String ERR_REQ = "Failed to handle ICMP request: ";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ScalableGatewayService gatewayService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InstancePortService instancePortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackRouterService osRouterService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+ private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+ private final InternalNodeListener nodeListener = new InternalNodeListener();
+ private final Map<String, InstancePort> icmpInfoMap = Maps.newHashMap();
+
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+ packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
+ osNodeService.addListener(nodeListener);
+ requestPacket(appId);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ packetService.removeProcessor(packetProcessor);
+ osNodeService.removeListener(nodeListener);
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ private void requestPacket(ApplicationId appId) {
+ TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .build();
+
+ gatewayService.getGatewayDeviceIds().forEach(gateway -> {
+ packetService.requestPackets(
+ icmpSelector,
+ PacketPriority.CONTROL,
+ appId,
+ Optional.of(gateway));
+ log.debug("Requested ICMP packet to {}", gateway);
+ });
+ }
+
+ private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
+ IPv4 ipPacket = (IPv4) ethernet.getPayload();
+ ICMP icmp = (ICMP) ipPacket.getPayload();
+ log.trace("Processing ICMP packet source MAC:{}, source IP:{}," +
+ "dest MAC:{}, dest IP:{}",
+ ethernet.getSourceMAC(),
+ IpAddress.valueOf(ipPacket.getSourceAddress()),
+ ethernet.getDestinationMAC(),
+ IpAddress.valueOf(ipPacket.getDestinationAddress()));
+
+ switch (icmp.getIcmpType()) {
+ case ICMP.TYPE_ECHO_REQUEST:
+ handleEchoRequest(
+ context.inPacket().receivedFrom().deviceId(),
+ ethernet.getSourceMAC(),
+ ipPacket,
+ icmp);
+ context.block();
+ break;
+ case ICMP.TYPE_ECHO_REPLY:
+ handleEchoReply(ipPacket, icmp);
+ context.block();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void handleEchoRequest(DeviceId srcDevice, MacAddress srcMac, IPv4 ipPacket,
+ ICMP icmp) {
+ InstancePort instPort = instancePortService.instancePort(srcMac);
+ if (instPort == null) {
+ log.trace(ERR_REQ + "unknown source host(MAC:{})", srcMac);
+ return;
+ }
+
+ IpAddress srcIp = IpAddress.valueOf(ipPacket.getSourceAddress());
+ Subnet srcSubnet = getSourceSubnet(instPort, srcIp);
+ if (srcSubnet == null) {
+ log.trace(ERR_REQ + "unknown source subnet(IP:{})", srcIp);
+ return;
+ }
+ if (Strings.isNullOrEmpty(srcSubnet.getGateway())) {
+ log.trace(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no gateway",
+ srcSubnet.getId(), srcSubnet.getCidr());
+ return;
+ }
+
+ if (isForSubnetGateway(IpAddress.valueOf(ipPacket.getDestinationAddress()),
+ srcSubnet)) {
+ // this is a request for the subnet gateway
+ processRequestForGateway(ipPacket, instPort);
+ } else {
+ // this is a request for the external network
+ IpAddress externalIp = getExternalIp(srcSubnet);
+ if (externalIp == null) {
+ return;
+ }
+ log.debug("1");
+ sendRequestForExternal(ipPacket, srcDevice, externalIp);
+ log.debug("2");
+ String icmpInfoKey = String.valueOf(getIcmpId(icmp))
+ .concat(String.valueOf(externalIp.getIp4Address().toInt()))
+ .concat(String.valueOf(ipPacket.getDestinationAddress()));
+ icmpInfoMap.putIfAbsent(icmpInfoKey, instPort);
+ }
+ }
+
+ private void handleEchoReply(IPv4 ipPacket, ICMP icmp) {
+ String icmpInfoKey = String.valueOf(getIcmpId(icmp))
+ .concat(String.valueOf(ipPacket.getDestinationAddress()))
+ .concat(String.valueOf(ipPacket.getSourceAddress()));
+
+ processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey));
+ icmpInfoMap.remove(icmpInfoKey);
+ }
+
+ private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
+ Port osPort = osNetworkService.port(instance.portId());
+ IP fixedIp = osPort.getFixedIps().stream()
+ .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
+ .findAny().orElse(null);
+ if (fixedIp == null) {
+ return null;
+ }
+ return osNetworkService.subnet(fixedIp.getSubnetId());
+ }
+
+ private boolean isForSubnetGateway(IpAddress dstIp, Subnet srcSubnet) {
+ RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+ .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
+ .findAny().orElse(null);
+ if (osRouterIface == null) {
+ log.trace(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no router",
+ srcSubnet.getId(), srcSubnet.getCidr());
+ return false;
+ }
+
+ Router osRouter = osRouterService.router(osRouterIface.getId());
+ Set<IpAddress> routableGateways = osRouterService.routerInterfaces(osRouter.getId())
+ .stream()
+ .map(iface -> osNetworkService.subnet(iface.getSubnetId()).getGateway())
+ .map(IpAddress::valueOf)
+ .collect(Collectors.toSet());
+
+ return routableGateways.contains(dstIp);
+ }
+
+ private IpAddress getExternalIp(Subnet srcSubnet) {
+ RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+ .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
+ .findAny().orElse(null);
+ if (osRouterIface == null) {
+ final String error = String.format(ERR_REQ +
+ "subnet(ID:%s, CIDR:%s) is not connected to any router",
+ srcSubnet.getId(), srcSubnet.getCidr());
+ throw new IllegalStateException(error);
+ }
+
+ Router osRouter = osRouterService.router(osRouterIface.getId());
+ if (osRouter.getExternalGatewayInfo() == null) {
+ final String error = String.format(ERR_REQ +
+ "router(ID:%s, name:%s) does not have external gateway",
+ osRouter.getId(), osRouter.getName());
+ throw new IllegalStateException(error);
+ }
+
+ // TODO fix openstack4j for ExternalGateway provides external fixed IP list
+ ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
+ Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
+ .stream()
+ .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
+ .findAny().orElse(null);
+ if (exGatewayPort == null) {
+ final String error = String.format(ERR_REQ +
+ "no external gateway port for router (ID:%s, name:%s)",
+ osRouter.getId(), osRouter.getName());
+ throw new IllegalStateException(error);
+ }
+
+ return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
+ .findFirst().get().getIpAddress());
+ }
+
+ private void processRequestForGateway(IPv4 ipPacket, InstancePort instPort) {
+ ICMP icmpReq = (ICMP) ipPacket.getPayload();
+ icmpReq.setChecksum((short) 0);
+ icmpReq.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
+
+ int destinationAddress = ipPacket.getSourceAddress();
+
+ ipPacket.setSourceAddress(ipPacket.getDestinationAddress())
+ .setDestinationAddress(destinationAddress)
+ .resetChecksum();
+
+ ipPacket.setPayload(icmpReq);
+ Ethernet icmpReply = new Ethernet();
+ icmpReply.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
+ .setDestinationMACAddress(instPort.macAddress())
+ .setPayload(ipPacket);
+
+ sendReply(icmpReply, instPort);
+ }
+
+ private void sendRequestForExternal(IPv4 ipPacket, DeviceId srcDevice, IpAddress srcNatIp) {
+ ICMP icmpReq = (ICMP) ipPacket.getPayload();
+ icmpReq.resetChecksum();
+ ipPacket.setSourceAddress(srcNatIp.getIp4Address().toInt()).resetChecksum();
+ ipPacket.setPayload(icmpReq);
+
+ Ethernet icmpRequestEth = new Ethernet();
+ icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
+ .setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC)
+ .setPayload(ipPacket);
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(gatewayService.getUplinkPort(srcDevice))
+ .build();
+
+ OutboundPacket packet = new DefaultOutboundPacket(
+ srcDevice,
+ treatment,
+ ByteBuffer.wrap(icmpRequestEth.serialize()));
+
+ packetService.emit(packet);
+ }
+
+ private void processReplyFromExternal(IPv4 ipPacket, InstancePort instPort) {
+ ICMP icmpReply = (ICMP) ipPacket.getPayload();
+ icmpReply.resetChecksum();
+
+ ipPacket.setDestinationAddress(instPort.ipAddress().getIp4Address().toInt())
+ .resetChecksum();
+ ipPacket.setPayload(icmpReply);
+
+ Ethernet icmpResponseEth = new Ethernet();
+ icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
+ .setDestinationMACAddress(instPort.macAddress())
+ .setPayload(ipPacket);
+
+ sendReply(icmpResponseEth, instPort);
+ }
+
+ private void sendReply(Ethernet icmpReply, InstancePort instPort) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(instPort.portNumber())
+ .build();
+
+ OutboundPacket packet = new DefaultOutboundPacket(
+ instPort.deviceId(),
+ treatment,
+ ByteBuffer.wrap(icmpReply.serialize()));
+
+ packetService.emit(packet);
+ }
+
+ private short getIcmpId(ICMP icmp) {
+ return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
+ }
+
+ private class InternalPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ if (context.isHandled()) {
+ return;
+ } else if (!gatewayService.getGatewayDeviceIds().contains(
+ context.inPacket().receivedFrom().deviceId())) {
+ // return if the packet is not from gateway nodes
+ return;
+ }
+
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethernet = pkt.parsed();
+ if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
+ return;
+ }
+
+ IPv4 iPacket = (IPv4) ethernet.getPayload();
+ if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
+ eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
+ }
+ }
+ }
+
+ private class InternalNodeListener implements OpenstackNodeListener {
+
+ @Override
+ public boolean isRelevant(OpenstackNodeEvent event) {
+ // do not proceed without mastership
+ OpenstackNode osNode = event.subject();
+ return mastershipService.isLocalMaster(osNode.intBridge());
+ }
+
+ @Override
+ public void event(OpenstackNodeEvent event) {
+ OpenstackNode osNode = event.subject();
+
+ switch (event.type()) {
+ case COMPLETE:
+ if (osNode.type() == GATEWAY) {
+ log.info("GATEWAY node {} detected", osNode.hostname());
+ eventExecutor.execute(() -> {
+ // TODO add events to scalable gateway so that we
+ // don't need to add gateway here and there
+ GatewayNode gnode = GatewayNode.builder()
+ .gatewayDeviceId(osNode.intBridge())
+ .dataIpAddress(osNode.dataIp().getIp4Address())
+ .uplinkIntf(osNode.externalPortName().get())
+ .build();
+ gatewayService.addGatewayNode(gnode);
+ requestPacket(appId);
+ });
+ }
+ break;
+ case INIT:
+ case DEVICE_CREATED:
+ case INCOMPLETE:
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java
deleted file mode 100644
index 2482333..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * 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.impl;
-
-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.apache.felix.scr.annotations.Service;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.core.GroupId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackNetwork;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackRouterInterface;
-import org.onosproject.openstackinterface.OpenstackSubnet;
-import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.GatewayNode;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-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.api.Constants.*;
-import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
-
-@Component(immediate = true)
-@Service
-public class OpenstackRoutingManager extends AbstractVmHandler implements OpenstackRoutingService {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected FlowObjectiveService flowObjectiveService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackNodeService nodeService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ScalableGatewayService gatewayService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
-
- private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
- groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
- private final InternalNodeListener nodeListener = new InternalNodeListener();
-
- private ApplicationId appId;
-
- @Activate
- protected void activate() {
- super.activate();
- appId = coreService.registerApplication(ROUTING_APP_ID);
- nodeService.addListener(nodeListener);
- }
-
- @Deactivate
- protected void deactivate() {
- super.deactivate();
- nodeService.removeListener(nodeListener);
- log.info("stopped");
- }
-
- @Override
- protected void hostDetected(Host host) {
- // Installs forwarding flow rules to VMs in different nodes and different subnets
- // that are connected via a router
- Optional<OpenstackRouter> routerOfTheHost = getRouter(host);
-
- if (!routerOfTheHost.isPresent()) {
- return;
- }
-
- routableSubNets(routerOfTheHost.get().id()).stream()
- .filter(subnet -> !subnet.id().equals(host.annotations().value(SUBNET_ID)))
- .forEach(subnet -> setForwardingRulesAmongHostsInDifferentCnodes(host, getHosts(subnet), true));
- }
-
- @Override
- protected void hostRemoved(Host host) {
- // Removes forwarding flow rules to VMs in different nodes and different subnets
- // that are connected via a router
- Optional<OpenstackRouter> routerOfTheHost = getRouter(host);
- if (!routerOfTheHost.isPresent()) {
- return;
- }
-
- routableSubNets(routerOfTheHost.get().id()).stream()
- .filter(subnet -> !subnet.id().equals(host.annotations().value(SUBNET_ID)))
- .forEach(subnet -> setForwardingRulesAmongHostsInDifferentCnodes(host, getHosts(subnet), false));
- }
-
- @Override
- public void createRouter(OpenstackRouter osRouter) {
- }
-
- @Override
- public void updateRouter(OpenstackRouter osRouter) {
- if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
- routableSubNets(osRouter.id()).stream()
- .forEach(subnet -> setExternalConnection(osRouter, subnet, true));
-
- log.info("Connected external gateway {} to router {}",
- osRouter.gatewayExternalInfo().externalFixedIps(),
- osRouter.name());
- } else {
- routableSubNets(osRouter.id()).stream()
- .forEach(subnet -> setExternalConnection(osRouter, subnet, false));
-
- log.info("Disconnected external gateway from router {}",
- osRouter.name());
- }
- }
-
- @Override
- public void removeRouter(String osRouterId) {
- // Nothing to do
- // All interfaces need to be removed before the router is removed,
- // and all related flow rues are removed when the interfaces are removed.
- }
-
- @Override
- public void addRouterInterface(OpenstackRouterInterface routerIfaceAdded) {
- OpenstackRouter osRouter = openstackRouter(routerIfaceAdded.id());
- OpenstackSubnet osSubnetAdded = openstackService.subnet(routerIfaceAdded.subnetId());
- if (osRouter == null || osSubnetAdded == null) {
- log.warn("Failed to add router interface {}", routerIfaceAdded);
- return;
- }
- handleRouterInterfaces(osRouter, osSubnetAdded);
- }
-
- @Override
- public void removeRouterInterface(OpenstackRouterInterface routerIface) {
- OpenstackRouter osRouter = openstackService.router(routerIface.id());
- OpenstackSubnet osSubnetRemoved = openstackService.subnet(routerIface.subnetId());
- if (osRouter == null) {
- log.warn("Failed to remove router interface {}", routerIface);
- return;
- }
- handleRouterInterfacesRemoved(osRouter, osSubnetRemoved);
-
- log.info("Disconnected {} from router {}", osSubnetRemoved.cidr(), osRouter.name());
- }
-
- private void handleRouterInterfaces(OpenstackRouter osRouter, OpenstackSubnet osSubnetAdded) {
- OpenstackNetwork osNetworkAdded = openstackService.network(osSubnetAdded.networkId());
- if (osNetworkAdded == null) { // in case of external network subnet
- return;
- }
-
- // Sets flow rules for routing among subnets connected to a router.
- setRoutesAmongSubnets(osRouter, osSubnetAdded, true);
-
- // Sets flow rules for forwarding "packets going to external networks" to gateway nodes.
- if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
- setExternalConnection(osRouter, osSubnetAdded, true);
- }
-
- // Sets flow rules to handle ping to the virtual gateway.
- Ip4Address vGatewayIp = Ip4Address.valueOf(osSubnetAdded.gatewayIp());
- gatewayService.getGatewayDeviceIds()
- .forEach(deviceId -> setGatewayIcmpRule(vGatewayIp, deviceId, true));
-
- // Sets east-west routing rules for VMs in different Cnode to Switching Table.
- setForwardingRulesForEastWestRouting(osRouter, osSubnetAdded, true);
-
- }
-
- private void handleRouterInterfacesRemoved(OpenstackRouter osRouter, OpenstackSubnet osSubnetRemoved) {
-
- // Removes flow rules for routing among subnets connected to a router.
- setRoutesAmongSubnets(osRouter, osSubnetRemoved, false);
-
- // Removes flow rules for forwarding "packets going to external networks" to gateway nodes.
- if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
- setExternalConnection(osRouter, osSubnetRemoved, false);
- }
-
- // Removes flow rules to handle ping to the virtual gateway.
- Ip4Address vGatewayIp = Ip4Address.valueOf(osSubnetRemoved.gatewayIp());
- gatewayService.getGatewayDeviceIds()
- .forEach(deviceId -> setGatewayIcmpRule(vGatewayIp, deviceId, false));
-
- // Removes east-west routing rules for VMs in different Cnode to Switching Table.
- setForwardingRulesForEastWestRouting(osRouter, osSubnetRemoved, false);
-
- // Resets east-west routing rules for VMs in different Cnode to Switching Table.
- routableSubNets(osRouter.id()).stream()
- .forEach(subnet -> setForwardingRulesForEastWestRouting(osRouter, subnet, true));
- }
-
- private void setRoutesAmongSubnets(OpenstackRouter osRouter, OpenstackSubnet osSubnetAdded, boolean install) {
- Set<OpenstackSubnet> routableSubNets = routableSubNets(osRouter.id());
- if (routableSubNets.size() < 2) {
- // no other subnet interface is connected to this router, do nothing
- return;
- }
-
- Map<String, String> vniMap = new HashMap<>();
- openstackService.networks().forEach(n -> vniMap.put(n.id(), n.segmentId()));
-
- routableSubNets.stream()
- .filter(subnet -> !subnet.id().equals(osSubnetAdded.id()))
- .filter(subnet -> vniMap.get(subnet.networkId()) != null)
- .forEach(subnet -> nodeService.completeNodes().stream()
- .filter(node -> node.type().equals(COMPUTE))
- .forEach(node -> {
- setRoutingRules(node.intBridge(),
- Integer.parseInt(vniMap.get(subnet.networkId())),
- Integer.parseInt(vniMap.get(osSubnetAdded.networkId())),
- subnet, osSubnetAdded, install);
- setRoutingRules(node.intBridge(),
- Integer.parseInt(vniMap.get(osSubnetAdded.networkId())),
- Integer.parseInt(vniMap.get(subnet.networkId())),
- osSubnetAdded, subnet, install);
- }
- ));
- }
-
- private void setRoutingRules(DeviceId deviceId, int srcVni, int dstVni,
- OpenstackSubnet subnetSrc, OpenstackSubnet subnetDst, boolean install) {
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(srcVni)
- .matchIPSrc(IpPrefix.valueOf(subnetSrc.cidr()))
- .matchIPDst(IpPrefix.valueOf(subnetDst.cidr()));
-
- tBuilder.setTunnelId(dstVni);
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
- tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, EW_ROUTING_RULE_PRIORITY, install);
-
- // Flow rules for destination is in different subnet and different node,
- // because VNI is converted to destination VNI in the source VM node.
- sBuilder = DefaultTrafficSelector.builder();
- tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(dstVni)
- .matchIPSrc(IpPrefix.valueOf(subnetSrc.cidr()))
- .matchIPDst(IpPrefix.valueOf(subnetDst.cidr()));
-
- tBuilder.setTunnelId(dstVni);
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
- tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, EW_ROUTING_RULE_PRIORITY, install);
- }
-
- private void setExternalConnection(OpenstackRouter osRouter, OpenstackSubnet osSubNet, boolean install) {
- if (!osRouter.gatewayExternalInfo().isEnablePnat()) {
- log.debug("Source NAT is disabled");
- return;
- }
-
- //OpenstackSubnet osSubNet = openstackService.subnet(osSubNetId);
- OpenstackNetwork osNet = openstackService.network(osSubNet.networkId());
-
- nodeService.completeNodes().stream()
- .filter(node -> node.type().equals(COMPUTE))
- .forEach(node -> setRulesToGateway(
- node.intBridge(),
- gatewayService.getGatewayGroupId(node.intBridge()),
- Long.valueOf(osNet.segmentId()), osSubNet.cidr(), install));
-
- // Is this for PNAT ??
- setRulesForGatewayToController(Long.valueOf(osNet.segmentId()), osSubNet.cidr(), install);
- }
-
- private void setRulesToGateway(DeviceId deviceId, GroupId groupId, long vni, String cidr, boolean install) {
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(vni)
- .matchIPSrc(IpPrefix.valueOf(cidr))
- .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
-
- tBuilder.group(groupId);
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
- tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, ROUTING_RULE_PRIORITY, install);
- }
-
- private void setRulesForGatewayToController(long vni, String subNetCidr, boolean install) {
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(vni)
- .matchIPSrc(IpPrefix.valueOf(subNetCidr))
- .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
- tBuilder.setOutput(PortNumber.CONTROLLER);
-
- gatewayService.getGatewayDeviceIds().forEach(deviceId ->
- RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
- tBuilder.build(), ForwardingObjective.Flag.VERSATILE, ROUTING_RULE_PRIORITY, install));
- }
-
- private void setGatewayIcmpRule(Ip4Address gatewayIp, DeviceId deviceId, boolean install) {
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPProtocol(IPv4.PROTOCOL_ICMP)
- .matchIPDst(gatewayIp.toIpPrefix());
-
- tBuilder.setOutput(PortNumber.CONTROLLER);
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
- tBuilder.build(), ForwardingObjective.Flag.VERSATILE, GATEWAY_ICMP_PRIORITY, install);
- }
-
- private void setForwardingRulesForEastWestRouting(OpenstackRouter router, OpenstackSubnet subnetAdded,
- boolean install) {
-
- Set<OpenstackSubnet> subnets = routableSubNets(router.id());
-
- Set<Host> hosts = Tools.stream(hostService.getHosts())
- .filter(h -> getVni(h).equals(openstackService.network(subnetAdded.networkId()).segmentId()))
- .collect(Collectors.toSet());
-
- subnets.stream()
- .filter(subnet -> !subnet.id().equals(subnetAdded.id()))
- .forEach(subnet -> getHosts(subnet)
- .forEach(h -> setForwardingRulesAmongHostsInDifferentCnodes(h, hosts, install)));
- }
-
- private void setForwardingRulesAmongHostsInDifferentCnodes(Host host, Set<Host> remoteHosts, boolean install) {
- Ip4Address localVmIp = getIp(host);
- DeviceId localDeviceId = host.location().deviceId();
- Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
-
- if (!localDataIp.isPresent()) {
- log.debug("Failed to get data IP for device {}",
- host.location().deviceId());
- return;
- }
-
- remoteHosts.stream()
- .filter(remoteHost -> !host.location().deviceId().equals(remoteHost.location().deviceId()))
- .forEach(remoteVm -> {
- Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
- if (remoteDataIp.isPresent()) {
- setVxLanFlowRule(getVni(remoteVm),
- localDeviceId,
- remoteDataIp.get().getIp4Address(),
- getIp(remoteVm), install);
-
- setVxLanFlowRule(getVni(host),
- remoteVm.location().deviceId(),
- localDataIp.get().getIp4Address(),
- localVmIp, install);
- }
- });
- }
-
- private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
- Ip4Address vmIp, boolean install) {
- Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
- if (!tunnelPort.isPresent()) {
- log.warn("Failed to get tunnel port from {}", deviceId);
- return;
- }
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(Long.parseLong(vni))
- .matchIPDst(vmIp.toIpPrefix());
- tBuilder.extension(buildExtension(deviceService, deviceId, remoteIp), deviceId)
- .setOutput(tunnelPort.get());
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
- tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, SWITCHING_RULE_PRIORITY, install);
- }
-
-
- private OpenstackRouter openstackRouter(String routerId) {
- return openstackService.routers().stream().filter(r ->
- r.id().equals(routerId)).iterator().next();
- }
-
- @Override
- public void reinstallVmFlow(Host host) {
- // TODO: implements later
- }
-
- @Override
- public void purgeVmFlow(Host host) {
- // TODO: implements later
- }
-
- private class InternalNodeListener implements OpenstackNodeListener {
-
- @Override
- public void event(OpenstackNodeEvent event) {
- OpenstackNode node = event.node();
-
- switch (event.type()) {
- case COMPLETE:
- case INCOMPLETE:
- log.info("COMPLETE node {} detected", node.hostname());
- eventExecutor.execute(() -> {
- if (node.type() == GATEWAY) {
- GatewayNode gnode = GatewayNode.builder()
- .gatewayDeviceId(node.intBridge())
- .dataIpAddress(node.dataIp().getIp4Address())
- .uplinkIntf(node.externalPortName().get())
- .build();
- gatewayService.addGatewayNode(gnode);
- }
- });
- openstackService.routers().stream()
- .forEach(router -> routableSubNets(router.id()).stream()
- .forEach(subnet -> handleRouterInterfaces(router, subnet)));
- break;
- case INIT:
- case DEVICE_CREATED:
- default:
- break;
- }
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
new file mode 100644
index 0000000..5b6868b
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -0,0 +1,463 @@
+/*
+ * 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.impl;
+
+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.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TCP;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.UDP;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+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.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handle packets needs SNAT.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingSnatHandler {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String ERR_PACKETIN = "Failed to handle packet in: ";
+ private static final int TIME_OUT_SNAT_RULE = 120;
+ private static final int TIME_OUT_SNAT_PORT = 1200 * 1000;
+ private static final int TP_PORT_MINIMUM_NUM = 1024;
+ private static final int TP_PORT_MAXIMUM_NUM = 65535;
+
+ private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InstancePortService instancePortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackRouterService osRouterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ScalableGatewayService gatewayService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+ private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+
+ private ConsistentMap<Integer, String> tpPortNumMap;
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+ tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
+ .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
+ .withName("openstackrouting-tpportnum")
+ .withApplicationId(appId)
+ .build();
+
+ packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ packetService.removeProcessor(packetProcessor);
+ eventExecutor.shutdown();
+ log.info("Stopped");
+ }
+
+ private void processSnatPacket(PacketContext context, Ethernet eth) {
+ IPv4 iPacket = (IPv4) eth.getPayload();
+ InboundPacket packetIn = context.inPacket();
+
+ int patPort = getPortNum(eth.getSourceMAC(),
+ iPacket.getDestinationAddress());
+
+ InstancePort srcInstPort = instancePortService.instancePort(eth.getSourceMAC());
+ if (srcInstPort == null) {
+ log.trace(ERR_PACKETIN + "source host(MAC:{}) does not exist",
+ eth.getSourceMAC());
+ return;
+ }
+ IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
+ Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
+ IpAddress externalGatewayIp = getExternalIp(srcSubnet);
+ if (externalGatewayIp == null) {
+ return;
+ }
+
+ populateSnatFlowRules(context.inPacket(),
+ srcInstPort,
+ TpPort.tpPort(patPort),
+ externalGatewayIp);
+
+ packetOut((Ethernet) eth.clone(),
+ packetIn.receivedFrom().deviceId(),
+ patPort,
+ externalGatewayIp);
+ }
+
+ private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
+ Port osPort = osNetworkService.port(instance.portId());
+ IP fixedIp = osPort.getFixedIps().stream()
+ .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
+ .findAny().orElse(null);
+ if (fixedIp == null) {
+ return null;
+ }
+ return osNetworkService.subnet(fixedIp.getSubnetId());
+ }
+
+ private IpAddress getExternalIp(Subnet srcSubnet) {
+ RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+ .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
+ .findAny().orElse(null);
+ if (osRouterIface == null) {
+ // this subnet is not connected to the router
+ log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
+ srcSubnet.getId(), srcSubnet.getCidr());
+ return null;
+ }
+
+ Router osRouter = osRouterService.router(osRouterIface.getId());
+ if (osRouter.getExternalGatewayInfo() == null) {
+ // this router does not have external connectivity
+ log.trace(ERR_PACKETIN + "router({}) has no external gateway",
+ osRouter.getName());
+ return null;
+ }
+
+ ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
+ if (!exGatewayInfo.isEnableSnat()) {
+ // SNAT is disabled in this router
+ log.trace(ERR_PACKETIN + "router({}) SNAT is disabled", osRouter.getName());
+ return null;
+ }
+
+ // TODO fix openstack4j for ExternalGateway provides external fixed IP list
+ Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
+ .stream()
+ .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
+ .findAny().orElse(null);
+ if (exGatewayPort == null) {
+ log.trace(ERR_PACKETIN + "no external gateway port for router({})",
+ osRouter.getName());
+ return null;
+ }
+
+ return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
+ .findFirst().get().getIpAddress());
+ }
+
+ private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
+ TpPort patPort, IpAddress externalIp) {
+ Network osNet = osNetworkService.network(srcInstPort.networkId());
+ if (osNet == null) {
+ final String error = String.format(ERR_PACKETIN + "network %s not found",
+ srcInstPort.networkId());
+ throw new IllegalStateException(error);
+ }
+
+ setDownstreamRules(srcInstPort,
+ Long.parseLong(osNet.getProviderSegID()),
+ externalIp,
+ patPort,
+ packetIn);
+
+ setUpstreamRules(Long.parseLong(osNet.getProviderSegID()),
+ externalIp,
+ patPort,
+ packetIn);
+ }
+
+ private void setDownstreamRules(InstancePort srcInstPort, Long srcVni,
+ IpAddress externalIp, TpPort patPort,
+ InboundPacket packetIn) {
+ IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
+ IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(iPacket.getProtocol())
+ .matchIPDst(IpPrefix.valueOf(externalIp, 32))
+ .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+ .setTunnelId(srcVni)
+ .setEthDst(packetIn.parsed().getSourceMAC())
+ .setIpDst(internalIp);
+
+ switch (iPacket.getProtocol()) {
+ case IPv4.PROTOCOL_TCP:
+ TCP tcpPacket = (TCP) iPacket.getPayload();
+ sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
+ .matchTcpDst(patPort);
+ tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
+ break;
+ case IPv4.PROTOCOL_UDP:
+ UDP udpPacket = (UDP) iPacket.getPayload();
+ sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
+ .matchUdpDst(patPort);
+ tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
+ break;
+ default:
+ break;
+ }
+
+ gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
+ DeviceId srcDeviceId = srcInstPort.deviceId();
+ TrafficTreatment.Builder tmpBuilder =
+ DefaultTrafficTreatment.builder(tBuilder.build());
+ tmpBuilder.extension(RulePopulatorUtil.buildExtension(
+ deviceService,
+ deviceId,
+ osNodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId)
+ .setOutput(osNodeService.tunnelPort(deviceId).get());
+
+ ForwardingObjective fo = DefaultForwardingObjective.builder()
+ .withSelector(sBuilder.build())
+ .withTreatment(tmpBuilder.build())
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(PRIORITY_SNAT_RULE)
+ .makeTemporary(TIME_OUT_SNAT_RULE)
+ .fromApp(appId)
+ .add();
+
+ flowObjectiveService.forward(deviceId, fo);
+ });
+ }
+
+ private void setUpstreamRules(Long srcVni, IpAddress externalIp, TpPort patPort,
+ InboundPacket packetIn) {
+ IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(iPacket.getProtocol())
+ .matchTunnelId(srcVni)
+ .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
+ .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ switch (iPacket.getProtocol()) {
+ case IPv4.PROTOCOL_TCP:
+ TCP tcpPacket = (TCP) iPacket.getPayload();
+ sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
+ .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
+ tBuilder.setTcpSrc(patPort)
+ .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
+ break;
+ case IPv4.PROTOCOL_UDP:
+ UDP udpPacket = (UDP) iPacket.getPayload();
+ sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
+ .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
+ tBuilder.setUdpSrc(patPort)
+ .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
+
+ break;
+ default:
+ log.debug("Unsupported IPv4 protocol {}");
+ break;
+ }
+
+ tBuilder.setIpSrc(externalIp);
+ gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
+ TrafficTreatment.Builder tmpBuilder =
+ DefaultTrafficTreatment.builder(tBuilder.build());
+ tmpBuilder.setOutput(gatewayService.getUplinkPort(deviceId));
+ ForwardingObjective fo = DefaultForwardingObjective.builder()
+ .withSelector(sBuilder.build())
+ .withTreatment(tmpBuilder.build())
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(PRIORITY_SNAT_RULE)
+ .makeTemporary(TIME_OUT_SNAT_RULE)
+ .fromApp(appId)
+ .add();
+
+ flowObjectiveService.forward(deviceId, fo);
+ });
+ }
+
+ private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
+ IpAddress externalIp) {
+ IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
+
+ switch (iPacket.getProtocol()) {
+ case IPv4.PROTOCOL_TCP:
+ TCP tcpPacket = (TCP) iPacket.getPayload();
+ tcpPacket.setSourcePort(patPort);
+ tcpPacket.resetChecksum();
+ tcpPacket.setParent(iPacket);
+ iPacket.setPayload(tcpPacket);
+ break;
+ case IPv4.PROTOCOL_UDP:
+ UDP udpPacket = (UDP) iPacket.getPayload();
+ udpPacket.setSourcePort(patPort);
+ udpPacket.resetChecksum();
+ udpPacket.setParent(iPacket);
+ iPacket.setPayload(udpPacket);
+ break;
+ default:
+ log.trace("Temporally, this method can process UDP and TCP protocol.");
+ return;
+ }
+
+ iPacket.setSourceAddress(externalIp.toString());
+ iPacket.resetChecksum();
+ iPacket.setParent(ethPacketIn);
+ ethPacketIn.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
+ ethPacketIn.setPayload(iPacket);
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(gatewayService.getUplinkPort(srcDevice))
+ .build();
+ ethPacketIn.resetChecksum();
+ packetService.emit(new DefaultOutboundPacket(
+ srcDevice,
+ treatment,
+ ByteBuffer.wrap(ethPacketIn.serialize())));
+ }
+
+ private int getPortNum(MacAddress sourceMac, int destinationAddress) {
+ int portNum = findUnusedPortNum();
+ if (portNum == 0) {
+ clearPortNumMap();
+ portNum = findUnusedPortNum();
+ }
+ tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
+ return portNum;
+ }
+
+ private int findUnusedPortNum() {
+ for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
+ if (!tpPortNumMap.containsKey(i)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ private void clearPortNumMap() {
+ tpPortNumMap.entrySet().forEach(e -> {
+ if (System.currentTimeMillis() - e.getValue().creationTime() > TIME_OUT_SNAT_PORT) {
+ tpPortNumMap.remove(e.getKey());
+ }
+ });
+ }
+
+ private class InternalPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ if (context.isHandled()) {
+ return;
+ } else if (!gatewayService.getGatewayDeviceIds().contains(
+ context.inPacket().receivedFrom().deviceId())) {
+ // return if the packet is not from gateway nodes
+ return;
+ }
+
+ InboundPacket pkt = context.inPacket();
+ Ethernet eth = pkt.parsed();
+ if (eth == null || eth.getEtherType() == Ethernet.TYPE_ARP) {
+ return;
+ }
+
+ IPv4 iPacket = (IPv4) eth.getPayload();
+ switch (iPacket.getProtocol()) {
+ case IPv4.PROTOCOL_ICMP:
+ break;
+ case IPv4.PROTOCOL_UDP:
+ UDP udpPacket = (UDP) iPacket.getPayload();
+ if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
+ udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
+ // don't process DHCP
+ break;
+ }
+ default:
+ eventExecutor.execute(() -> processSnatPacket(context, eth));
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
deleted file mode 100644
index 3f6f480..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
-* 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.impl;
-
-import com.google.common.collect.Maps;
-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.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.TpPort;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.OpenstackSecurityGroup;
-import org.onosproject.openstackinterface.OpenstackSecurityGroupRule;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Populates flows rules for Security Groups of VMs.
- *
- */
-@Component(immediate = true)
-@Service
-public class OpenstackSecurityGroupManager extends AbstractVmHandler
- implements OpenstackSecurityGroupService {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected FlowObjectiveService flowObjectiveService;
-
- private static final String PROTO_ICMP = "ICMP";
- private static final String PROTO_TCP = "TCP";
- private static final String PROTO_UDP = "UDP";
- private static final String ETHTYPE_IPV4 = "IPV4";
-
- private final Map<Host, Set<SecurityGroupRule>> securityGroupRuleMap = Maps.newConcurrentMap();
- private ApplicationId appId;
-
- @Activate
- protected void activate() {
- super.activate();
- appId = coreService.registerApplication(SWITCHING_APP_ID);
- }
-
- @Deactivate
- protected void deactivate() {
- super.deactivate();
- }
-
- @Override
- public void updateSecurityGroup(OpenstackPort osPort) {
- if (!osPort.status().equals(OpenstackPort.PortStatus.ACTIVE)) {
- return;
- }
-
- Optional<Host> host = getVmByPortId(osPort.id());
- if (!host.isPresent()) {
- log.debug("No host found with {}", osPort.id());
- return;
- }
- eventExecutor.execute(() -> updateSecurityGroupRules(host.get(), true));
- }
-
- /**
- * Populates security group rules for all VMs in the supplied tenant ID.
- * VMs in the same tenant tend to be engaged to each other by sharing the
- * same security groups or setting the remote to another security group.
- * To make the implementation simpler and robust, it tries to reinstall
- * security group rules for all the VMs in the same tenant whenever a new
- * VM is detected or port is updated.
- *
- * @param tenantId tenant id to update security group rules
- */
- private void populateSecurityGroupRules(String tenantId, boolean install) {
- securityGroupRuleMap.entrySet().stream()
- .filter(entry -> getTenantId(entry.getKey()).equals(tenantId))
- .forEach(entry -> {
- Host local = entry.getKey();
- entry.getValue().forEach(sgRule -> {
- setSecurityGroupRule(local.location().deviceId(),
- sgRule.rule(),
- getIp(local),
- sgRule.remoteIp(), install);
- });
- });
- log.debug("Updated security group rules for {}", tenantId);
- }
-
- private void setSecurityGroupRule(DeviceId deviceId, OpenstackSecurityGroupRule sgRule,
- Ip4Address vmIp, IpPrefix remoteIp,
- boolean install) {
- ForwardingObjective.Builder foBuilder = buildFlowObjective(sgRule, vmIp, remoteIp);
- if (foBuilder == null) {
- return;
- }
-
- if (install) {
- flowObjectiveService.forward(deviceId, foBuilder.add());
- } else {
- flowObjectiveService.forward(deviceId, foBuilder.remove());
- }
- }
-
- private ForwardingObjective.Builder buildFlowObjective(OpenstackSecurityGroupRule sgRule,
- Ip4Address vmIp,
- IpPrefix remoteIp) {
- if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
- // do nothing if the remote IP is my IP
- return null;
- }
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
-
- return DefaultForwardingObjective.builder()
- .withSelector(sBuilder.build())
- .withTreatment(DefaultTrafficTreatment.builder().build())
- .withPriority(ACL_RULE_PRIORITY)
- .withFlag(ForwardingObjective.Flag.SPECIFIC)
- .fromApp(appId);
- }
-
- private void buildMatchs(TrafficSelector.Builder sBuilder, OpenstackSecurityGroupRule sgRule,
- Ip4Address vmIp, IpPrefix remoteIp) {
- buildMatchEthType(sBuilder, sgRule.ethertype());
- buildMatchDirection(sBuilder, sgRule.direction(), vmIp);
- buildMatchProto(sBuilder, sgRule.protocol());
- buildMatchPort(sBuilder, sgRule.protocol(), sgRule.direction(),
- sgRule.portRangeMax(), sgRule.portRangeMin());
- buildMatchRemoteIp(sBuilder, remoteIp, sgRule.direction());
- }
-
- private void buildMatchDirection(TrafficSelector.Builder sBuilder,
- OpenstackSecurityGroupRule.Direction direction,
- Ip4Address vmIp) {
- if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
- sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
- } else {
- sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
- }
- }
-
- private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
- // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
- sBuilder.matchEthType(Ethernet.TYPE_IPV4);
- if (etherType != null && !Objects.equals(etherType, "null") &&
- !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
- log.debug("EthType {} is not supported yet in Security Group", etherType);
- }
- }
-
- private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix,
- OpenstackSecurityGroupRule.Direction direction) {
- if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
- if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
- sBuilder.matchIPDst(remoteIpPrefix);
- } else {
- sBuilder.matchIPSrc(remoteIpPrefix);
- }
- }
- }
-
- private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
- if (protocol != null) {
- switch (protocol.toUpperCase()) {
- case PROTO_ICMP:
- sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
- break;
- case PROTO_TCP:
- sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
- break;
- case PROTO_UDP:
- sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
- break;
- default:
- }
- }
- }
-
- private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol,
- OpenstackSecurityGroupRule.Direction direction,
- int portMin, int portMax) {
- if (portMin > 0 && portMax > 0 && portMin == portMax) {
- if (protocol.toUpperCase().equals(PROTO_TCP)) {
- if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
- sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
- } else {
- sBuilder.matchTcpDst(TpPort.tpPort(portMax));
- }
- } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
- if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
- sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
- } else {
- sBuilder.matchUdpDst(TpPort.tpPort(portMax));
- }
- }
- }
- }
-
- private void updateSecurityGroupRulesMap(Host host) {
- OpenstackPort osPort = openstackService.port(host.annotations().value(PORT_ID));
- if (osPort == null) {
- log.debug("Failed to get OpenStack port information for {}", host);
- return;
- }
-
- Set<SecurityGroupRule> rules = Sets.newHashSet();
- osPort.securityGroups().forEach(sgId -> {
- OpenstackSecurityGroup osSecGroup = openstackService.securityGroup(sgId);
- if (osSecGroup != null) {
- osSecGroup.rules().forEach(rule -> rules.addAll(getSgRules(rule)));
- } else {
- // TODO handle the case that the security group removed
- log.warn("Failed to get security group {}", sgId);
- }
- });
- securityGroupRuleMap.put(host, rules);
- }
-
- /**
- * Returns set of security group rules with individual remote IP by
- * converting remote group to actual IP address.
- *
- * @param sgRule security group rule
- * @return set of security group rules
- */
- private Set<SecurityGroupRule> getSgRules(OpenstackSecurityGroupRule sgRule) {
- Set<SecurityGroupRule> sgRules = Sets.newHashSet();
- if (sgRule.remoteGroupId() != null && !sgRule.remoteGroupId().equals("null")) {
- sgRules = getRemoteIps(sgRule.tenantId(), sgRule.remoteGroupId())
- .stream()
- .map(remoteIp -> new SecurityGroupRule(sgRule, remoteIp))
- .collect(Collectors.toSet());
- } else {
- sgRules.add(new SecurityGroupRule(sgRule, sgRule.remoteIpPrefix()));
- }
- return sgRules;
- }
-
- /**
- * Returns a set of host IP addresses engaged with supplied security group ID.
- * It only searches a VM in the same tenant boundary.
- *
- * @param tenantId tenant id
- * @param sgId security group id
- * @return set of ip addresses in ip prefix format
- */
- private Set<IpPrefix> getRemoteIps(String tenantId, String sgId) {
- Set<IpPrefix> remoteIps = Sets.newHashSet();
- securityGroupRuleMap.entrySet().stream()
- .filter(entry -> Objects.equals(getTenantId(entry.getKey()), tenantId))
- .forEach(entry -> {
- if (entry.getValue().stream()
- .anyMatch(rule -> rule.rule().secuityGroupId().equals(sgId))) {
- remoteIps.add(IpPrefix.valueOf(getIp(entry.getKey()), 32));
- }
- });
- return remoteIps;
- }
-
- private void updateSecurityGroupRules(Host host, boolean isHostAdded) {
- String tenantId = getTenantId(host);
- populateSecurityGroupRules(tenantId, false);
-
- if (isHostAdded) {
- updateSecurityGroupRulesMap(host);
- } else {
- securityGroupRuleMap.remove(host);
- }
-
- Tools.stream(hostService.getHosts())
- .filter(h -> Objects.equals(getTenantId(h), getTenantId(host)))
- .forEach(this::updateSecurityGroupRulesMap);
-
- populateSecurityGroupRules(tenantId, true);
- }
-
- @Override
- protected void hostDetected(Host host) {
- updateSecurityGroupRules(host, true);
- log.info("Applied security group rules for {}", host);
- }
-
- @Override
- protected void hostRemoved(Host host) {
- updateSecurityGroupRules(host, false);
- log.info("Applied security group rules for {}", host);
- }
-
- @Override
- public void reinstallVmFlow(Host host) {
- if (host == null) {
- hostService.getHosts().forEach(h -> {
- updateSecurityGroupRules(h, true);
- log.info("Re-Install data plane flow of virtual machine {}", h);
- });
- } else {
- securityGroupRuleMap.entrySet().stream()
- .filter(entry -> entry.getKey().id().equals(host.id()))
- .forEach(entry -> {
- Host local = entry.getKey();
- entry.getValue().forEach(sgRule -> {
- setSecurityGroupRule(local.location().deviceId(),
- sgRule.rule(),
- getIp(local),
- sgRule.remoteIp(), true);
- });
- });
- log.info("Re-Install data plane flow of virtual machine {}", host);
- }
- }
-
- @Override
- public void purgeVmFlow(Host host) {
- if (host == null) {
- securityGroupRuleMap.entrySet().stream()
- .forEach(entry -> {
- Host local = entry.getKey();
- entry.getValue().forEach(sgRule -> {
- setSecurityGroupRule(local.location().deviceId(),
- sgRule.rule(),
- getIp(local),
- sgRule.remoteIp(), false);
- });
- log.info("Purge data plane flow of virtual machine {}", local);
- });
- } else {
- securityGroupRuleMap.entrySet().stream()
- .filter(entry -> entry.getKey().id().equals(host.id()))
- .forEach(entry -> {
- Host local = entry.getKey();
- entry.getValue().forEach(sgRule -> {
- setSecurityGroupRule(local.location().deviceId(),
- sgRule.rule(),
- getIp(local),
- sgRule.remoteIp(), false);
- });
- });
- log.info("Purge data plane flow of virtual machine {}", host);
- }
- }
-
- private final class SecurityGroupRule {
- private final OpenstackSecurityGroupRule rule;
- private final IpPrefix remoteIp;
-
- private SecurityGroupRule(OpenstackSecurityGroupRule rule, IpPrefix remoteIp) {
- this.rule = rule;
- this.remoteIp = remoteIp;
- }
-
- private OpenstackSecurityGroupRule rule() {
- return rule;
- }
-
- private IpPrefix remoteIp() {
- return remoteIp;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof SecurityGroupRule) {
- SecurityGroupRule that = (SecurityGroupRule) obj;
- if (Objects.equals(rule, that.rule) &&
- Objects.equals(remoteIp, that.remoteIp)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(rule, remoteIp);
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index b4ae36c..2a980d3 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -25,64 +25,97 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ARP;
+import org.onlab.packet.EthType;
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.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
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.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.model.network.Subnet;
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.api.Constants.*;
+import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
/**
* Handles ARP packet from VMs.
*/
@Component(immediate = true)
-public final class OpenstackSwitchingArpHandler extends AbstractVmHandler {
+public final class OpenstackSwitchingArpHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String GATEWAY_MAC = "gatewayMac";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
+ protected ComponentConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InstancePortService instancePortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
@Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
label = "Fake MAC address for virtual network subnet gateway")
private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
- private final Set<Ip4Address> gateways = Sets.newConcurrentHashSet();
+ private final InternalOpenstackNetworkListener osNetworkListener =
+ new InternalOpenstackNetworkListener();
+ private final Set<IpAddress> gateways = Sets.newConcurrentHashSet();
+
+ private ApplicationId appId;
@Activate
protected void activate() {
+ appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+ configService.registerProperties(getClass());
+ osNetworkService.addListener(osNetworkListener);
packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
- super.activate();
+ osNetworkService.subnets().forEach(this::addSubnetGateway);
+ requestPacket();
+
+ log.info("Started");
}
@Deactivate
protected void deactivate() {
packetService.removeProcessor(packetProcessor);
- super.deactivate();
+ osNetworkService.removeListener(osNetworkListener);
+ configService.unregisterProperties(getClass(), false);
+
+ log.info("Stopped");
}
@Modified
@@ -98,12 +131,41 @@
log.info("Modified");
}
+ private void requestPacket() {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+ .build();
+
+ packetService.requestPackets(
+ selector,
+ PacketPriority.CONTROL,
+ appId);
+ }
+
+ private void addSubnetGateway(Subnet osSubnet) {
+ if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+ return;
+ }
+ IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+ gateways.add(gatewayIp);
+ log.debug("Added ARP proxy entry IP:{}", gatewayIp);
+ }
+
+ private void removeSubnetGateway(Subnet osSubnet) {
+ if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+ return;
+ }
+ IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+ gateways.remove(gatewayIp);
+ log.debug("Removed ARP proxy entry IP:{}", gatewayIp);
+ }
+
/**
* 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 context packet context
* @param ethPacket ethernet packet
*/
private void processPacketIn(PacketContext context, Ethernet ethPacket) {
@@ -112,15 +174,18 @@
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);
+ InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
+ if (srcInstPort == null) {
+ log.trace("Failed to find source instance port(MAC:{})",
+ ethPacket.getSourceMAC());
+ return;
}
+ IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
+ MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
+ getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
if (replyMac == MacAddress.NONE) {
- log.debug("Failed to find MAC address for {}", targetIp.toString());
+ log.trace("Failed to find MAC address for {}", targetIp);
return;
}
@@ -141,66 +206,24 @@
/**
* 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.
+ * instance port service.
*
* @param targetIp target ip
- * @return mac address, or null if it fails to find the mac
+ * @param osNetId openstack network id of the source instance port
+ * @return mac address, or none mac address if it fails to find the mac
*/
- private MacAddress getMacFromHostService(IpAddress targetIp) {
+ private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
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();
+ InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
+ if (instPort != null) {
+ log.trace("Found MAC from host service for {}", targetIp);
+ return instPort.macAddress();
} 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().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
@@ -216,4 +239,38 @@
processPacketIn(context, ethPacket);
}
}
+
+ private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
+
+ @Override
+ public boolean isRelevant(OpenstackNetworkEvent event) {
+ Subnet osSubnet = event.subnet();
+ if (osSubnet == null) {
+ return false;
+ }
+ return !Strings.isNullOrEmpty(osSubnet.getGateway());
+ }
+
+ @Override
+ public void event(OpenstackNetworkEvent event) {
+ switch (event.type()) {
+ case OPENSTACK_SUBNET_CREATED:
+ case OPENSTACK_SUBNET_UPDATED:
+ addSubnetGateway(event.subnet());
+ break;
+ case OPENSTACK_SUBNET_REMOVED:
+ removeSubnetGateway(event.subnet());
+ break;
+ case OPENSTACK_NETWORK_CREATED:
+ case OPENSTACK_NETWORK_UPDATED:
+ case OPENSTACK_NETWORK_REMOVED:
+ case OPENSTACK_PORT_CREATED:
+ case OPENSTACK_PORT_UPDATED:
+ case OPENSTACK_PORT_REMOVED:
+ default:
+ // do nothing for the other events
+ break;
+ }
+ }
+ }
}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java
new file mode 100644
index 0000000..d87326d8
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2017-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.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+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.DHCP;
+import org.onlab.packet.DHCPOption;
+import org.onlab.packet.DHCPPacketType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.UDP;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Dictionary;
+import java.util.List;
+
+import static org.onlab.packet.DHCP.DHCPOptionCode.*;
+import static org.onlab.packet.DHCPPacketType.DHCPACK;
+import static org.onlab.packet.DHCPPacketType.DHCPOFFER;
+import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handles DHCP requests for the virtual instances.
+ */
+@Component(immediate = true)
+public class OpenstackSwitchingDhcpHandler {
+ protected final Logger log = getLogger(getClass());
+
+ private static final String DHCP_SERVER_MAC = "dhcpServerMac";
+ private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
+ private static final byte PACKET_TTL = (byte) 127;
+ // TODO add MTU, static route option codes to ONOS DHCP and remove here
+ private static final byte DHCP_OPTION_MTU = (byte) 26;
+ private static final byte[] DHCP_DATA_LEASE_INFINITE =
+ ByteBuffer.allocate(4).putInt(-1).array();
+ private static final byte[] DHCP_DATA_MTU_DEFAULT =
+ ByteBuffer.allocate(2).putShort((short) 1450).array();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InstancePortService instancePortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ @Property(name = DHCP_SERVER_MAC, value = DEFAULT_GATEWAY_MAC_STR,
+ label = "Fake MAC address for virtual network subnet gateway")
+ private String dhcpServerMac = DEFAULT_GATEWAY_MAC_STR;
+
+ private final PacketProcessor packetProcessor = new InternalPacketProcessor();
+
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+ configService.registerProperties(getClass());
+ packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
+ requestPackets();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ cancelPackets();
+ packetService.removeProcessor(packetProcessor);
+ configService.unregisterProperties(getClass(), false);
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ protected void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ String updatedMac;
+
+ updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
+ if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
+ dhcpServerMac = updatedMac;
+ }
+
+ log.info("Modified");
+ }
+
+ private void requestPackets() {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_UDP)
+ .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+ .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
+ .build();
+ packetService.requestPackets(selector, PacketPriority.CONTROL, appId);
+ }
+
+ private void cancelPackets() {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_UDP)
+ .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+ .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
+ .build();
+ packetService.cancelPackets(selector, PacketPriority.CONTROL, appId);
+ }
+
+ 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_IPV4) {
+ return;
+ }
+ IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
+ if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_UDP) {
+ return;
+ }
+ UDP udpPacket = (UDP) ipv4Packet.getPayload();
+ if (udpPacket.getDestinationPort() != UDP.DHCP_SERVER_PORT ||
+ udpPacket.getSourcePort() != UDP.DHCP_CLIENT_PORT) {
+ return;
+ }
+
+ DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
+ processDhcp(context, dhcpPacket);
+ }
+
+ private void processDhcp(PacketContext context, DHCP dhcpPacket) {
+ if (dhcpPacket == null) {
+ log.trace("DHCP packet without payload received, do nothing");
+ return;
+ }
+
+ DHCPPacketType inPacketType = getPacketType(dhcpPacket);
+ if (inPacketType == null || dhcpPacket.getClientHardwareAddress() == null) {
+ log.trace("Malformed DHCP packet received, ignore it");
+ return;
+ }
+
+ MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
+ InstancePort reqInstPort = instancePortService.instancePort(clientMac);
+ if (reqInstPort == null) {
+ log.trace("Failed to find host(MAC:{})", clientMac);
+ return;
+ }
+ Ethernet ethPacket = context.inPacket().parsed();
+ switch (inPacketType) {
+ case DHCPDISCOVER:
+ log.trace("DHCP DISCOVER received from {}", clientMac);
+ Ethernet discoverReply = buildReply(
+ ethPacket,
+ (byte) DHCPOFFER.getValue(),
+ reqInstPort);
+ sendReply(context, discoverReply);
+ log.trace("DHCP OFFER({}) is sent for {}",
+ reqInstPort.ipAddress(), clientMac);
+ break;
+ case DHCPREQUEST:
+ log.trace("DHCP REQUEST received from {}", clientMac);
+ Ethernet requestReply = buildReply(
+ ethPacket,
+ (byte) DHCPACK.getValue(),
+ reqInstPort);
+ sendReply(context, requestReply);
+ log.trace("DHCP ACK({}) is sent for {}",
+ reqInstPort.ipAddress(), clientMac);
+ break;
+ case DHCPRELEASE:
+ log.trace("DHCP RELEASE received from {}", clientMac);
+ // do nothing
+ break;
+ default:
+ break;
+ }
+ }
+
+ private DHCPPacketType getPacketType(DHCP dhcpPacket) {
+ DHCPOption optType = dhcpPacket.getOption(OptionCode_MessageType);
+ if (optType == null) {
+ log.trace("DHCP packet with no message type, ignore it");
+ return null;
+ }
+
+ DHCPPacketType inPacketType = DHCPPacketType.getType(optType.getData()[0]);
+ if (inPacketType == null) {
+ log.trace("DHCP packet with no packet type, ignore it");
+ }
+ return inPacketType;
+ }
+
+ private Ethernet buildReply(Ethernet ethRequest, byte packetType,
+ InstancePort reqInstPort) {
+ Port osPort = osNetworkService.port(reqInstPort.portId());
+ // pick one IP address to make a reply
+ IP fixedIp = osPort.getFixedIps().stream().findFirst().get();
+ Subnet osSubnet = osNetworkService.subnet(fixedIp.getSubnetId());
+
+ Ethernet ethReply = new Ethernet();
+ ethReply.setSourceMACAddress(dhcpServerMac);
+ ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
+ ethReply.setEtherType(Ethernet.TYPE_IPV4);
+
+ IPv4 ipv4Request = (IPv4) ethRequest.getPayload();
+ IPv4 ipv4Reply = new IPv4();
+ ipv4Reply.setSourceAddress(Ip4Address.valueOf(osSubnet.getGateway()).toInt());
+ ipv4Reply.setDestinationAddress(reqInstPort.ipAddress().getIp4Address().toInt());
+ ipv4Reply.setTtl(PACKET_TTL);
+
+ UDP udpRequest = (UDP) ipv4Request.getPayload();
+ UDP udpReply = new UDP();
+ udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
+ udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
+
+ DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
+ DHCP dhcpReply = buildDhcpReply(
+ dhcpRequest,
+ packetType,
+ reqInstPort.ipAddress().getIp4Address(),
+ osSubnet);
+
+ udpReply.setPayload(dhcpReply);
+ ipv4Reply.setPayload(udpReply);
+ ethReply.setPayload(ipv4Reply);
+
+ return ethReply;
+ }
+
+ private void sendReply(PacketContext context, Ethernet ethReply) {
+ if (ethReply == null) {
+ return;
+ }
+ ConnectPoint srcPoint = context.inPacket().receivedFrom();
+ TrafficTreatment treatment = DefaultTrafficTreatment
+ .builder()
+ .setOutput(srcPoint.port())
+ .build();
+
+ packetService.emit(new DefaultOutboundPacket(
+ srcPoint.deviceId(),
+ treatment,
+ ByteBuffer.wrap(ethReply.serialize())));
+ context.block();
+ }
+
+ private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
+ Subnet osSubnet) {
+ Ip4Address gatewayIp = Ip4Address.valueOf(osSubnet.getGateway());
+ int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
+
+ DHCP dhcpReply = new DHCP();
+ dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
+ dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
+ dhcpReply.setHardwareAddressLength((byte) 6);
+ dhcpReply.setTransactionId(request.getTransactionId());
+ dhcpReply.setFlags(request.getFlags());
+ dhcpReply.setYourIPAddress(yourIp.toInt());
+ dhcpReply.setServerIPAddress(gatewayIp.toInt());
+ dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress());
+
+ List<DHCPOption> options = Lists.newArrayList();
+ // message type
+ DHCPOption option = new DHCPOption();
+ option.setCode(OptionCode_MessageType.getValue());
+ option.setLength((byte) 1);
+ byte[] optionData = {msgType};
+ option.setData(optionData);
+ options.add(option);
+
+ // server identifier
+ option = new DHCPOption();
+ option.setCode(OptionCode_DHCPServerIp.getValue());
+ option.setLength((byte) 4);
+ option.setData(gatewayIp.toOctets());
+ options.add(option);
+
+ // lease time
+ option = new DHCPOption();
+ option.setCode(OptionCode_LeaseTime.getValue());
+ option.setLength((byte) 4);
+ option.setData(DHCP_DATA_LEASE_INFINITE);
+ options.add(option);
+
+ // subnet mask
+ Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen);
+ option = new DHCPOption();
+ option.setCode(OptionCode_SubnetMask.getValue());
+ option.setLength((byte) 4);
+ option.setData(subnetMask.toOctets());
+ options.add(option);
+
+ // broadcast address
+ Ip4Address broadcast = Ip4Address.makeMaskedAddress(yourIp, subnetPrefixLen);
+ option = new DHCPOption();
+ option.setCode(OptionCode_BroadcastAddress.getValue());
+ option.setLength((byte) 4);
+ option.setData(broadcast.toOctets());
+ options.add(option);
+
+ // domain server
+ option = new DHCPOption();
+ option.setCode(OptionCode_DomainServer.getValue());
+ option.setLength((byte) 4);
+ option.setData(DEFAULT_DNS.toOctets());
+ options.add(option);
+
+ // TODO fix MTU value to be configurable
+ option = new DHCPOption();
+ option.setCode(DHCP_OPTION_MTU);
+ option.setLength((byte) 2);
+ option.setData(DHCP_DATA_MTU_DEFAULT);
+ options.add(option);
+
+ // router address
+ option = new DHCPOption();
+ option.setCode(OptionCode_RouterAddress.getValue());
+ option.setLength((byte) 4);
+ option.setData(gatewayIp.toOctets());
+ options.add(option);
+
+ // end option
+ option = new DHCPOption();
+ option.setCode(OptionCode_END.getValue());
+ option.setLength((byte) 1);
+ options.add(option);
+
+ dhcpReply.setOptions(options);
+ return dhcpReply;
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
new file mode 100644
index 0000000..50d9b37
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
@@ -0,0 +1,239 @@
+/*
+* 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.impl;
+
+import com.google.common.base.Strings;
+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.Ethernet;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortEvent;
+import org.onosproject.openstacknetworking.api.InstancePortListener;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.openstack4j.model.network.Network;
+import org.slf4j.Logger;
+
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Populates switching flow rules on OVS for the basic connectivity among the
+ * virtual instances in the same network.
+ */
+@Component(immediate = true)
+public final class OpenstackSwitchingHandler {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String ERR_SET_FLOWS = "Failed to set flows for %s: ";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InstancePortService instancePortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler"));
+ private final InstancePortListener instancePortListener = new InternalInstancePortListener();
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+ instancePortService.addListener(instancePortListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ instancePortService.removeListener(instancePortListener);
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ private void setNetworkRules(InstancePort instPort, boolean install) {
+ setTunnelTagFlowRules(instPort, install);
+ setForwardingRules(instPort, install);
+ }
+
+ private void setForwardingRules(InstancePort instPort, boolean install) {
+ // switching rules for the instPorts in the same node
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(instPort.ipAddress().toIpPrefix())
+ .matchTunnelId(getVni(instPort))
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(instPort.macAddress())
+ .setOutput(instPort.portNumber())
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ instPort.deviceId(),
+ selector,
+ treatment,
+ ForwardingObjective.Flag.SPECIFIC,
+ PRIORITY_SWITCHING_RULE,
+ install);
+
+ // switching rules for the instPorts in the remote node
+ for (OpenstackNode osNode : osNodeService.completeNodes()) {
+ if (osNode.intBridge().equals(instPort.deviceId())) {
+ continue;
+ }
+
+ TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+ .extension(buildExtension(
+ deviceService,
+ osNode.intBridge(),
+ osNodeService.dataIp(instPort.deviceId()).get().getIp4Address()),
+ osNode.intBridge())
+ .setOutput(osNodeService.tunnelPort(osNode.intBridge()).get())
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ osNode.intBridge(),
+ selector,
+ treatmentToRemote,
+ ForwardingObjective.Flag.SPECIFIC,
+ PRIORITY_SWITCHING_RULE,
+ install);
+ }
+ }
+
+ private void setTunnelTagFlowRules(InstancePort instPort, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchInPort(instPort.portNumber())
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setTunnelId(getVni(instPort))
+ .build();
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ instPort.deviceId(),
+ selector,
+ treatment,
+ ForwardingObjective.Flag.SPECIFIC,
+ PRIORITY_TUNNEL_TAG_RULE,
+ install);
+ }
+
+ private Long getVni(InstancePort instPort) {
+ Network osNet = osNetworkService.network(instPort.networkId());
+ if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
+ final String error = String.format(
+ ERR_SET_FLOWS + "Failed to get VNI for %s",
+ instPort, osNet.getName());
+ throw new IllegalStateException(error);
+ }
+ return Long.valueOf(osNet.getProviderSegID());
+ }
+
+ private class InternalInstancePortListener implements InstancePortListener {
+
+ @Override
+ public boolean isRelevant(InstancePortEvent event) {
+ InstancePort instPort = event.subject();
+ return mastershipService.isLocalMaster(instPort.deviceId());
+ }
+
+ @Override
+ public void event(InstancePortEvent event) {
+ InstancePort instPort = event.subject();
+ switch (event.type()) {
+ case OPENSTACK_INSTANCE_PORT_UPDATED:
+ case OPENSTACK_INSTANCE_PORT_DETECTED:
+ eventExecutor.execute(() -> {
+ log.info("Instance port detected MAC:{} IP:{}",
+ instPort.macAddress(),
+ instPort.ipAddress());
+ instPortDetected(event.subject());
+ });
+ break;
+ case OPENSTACK_INSTANCE_PORT_VANISHED:
+ eventExecutor.execute(() -> {
+ log.info("Instance port vanished MAC:{} IP:{}",
+ instPort.macAddress(),
+ instPort.ipAddress());
+ instPortRemoved(event.subject());
+ });
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void instPortDetected(InstancePort instPort) {
+ setNetworkRules(instPort, true);
+ // TODO add something else if needed
+ }
+
+ private void instPortRemoved(InstancePort instPort) {
+ setNetworkRules(instPort, false);
+ // TODO add something else if needed
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java
deleted file mode 100644
index 99dd927..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * 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.impl;
-
-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.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.CoreService;
-import org.onosproject.dhcp.DhcpService;
-import org.onosproject.dhcp.IpAssignment;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.Device;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.Port;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceService;
-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.host.HostService;
-import org.onosproject.net.provider.AbstractProvider;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackNetwork;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.OpenstackSubnet;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Date;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-@Service
-@Component(immediate = true)
-public final class OpenstackSwitchingHostManager extends AbstractProvider
- implements HostProvider {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostProviderRegistry hostProviderRegistry;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DhcpService dhcpService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostService hostService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected MastershipService mastershipService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackInterfaceService openstackService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackNodeService openstackNodeService;
-
- private final ExecutorService deviceEventExecutor =
- Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event"));
- private final ExecutorService configEventExecutor =
- Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "config-event"));
- private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
- private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
-
- private HostProviderService hostProvider;
-
- /**
- * Creates OpenStack switching host provider.
- */
- public OpenstackSwitchingHostManager() {
- super(new ProviderId("host", SWITCHING_APP_ID));
- }
-
- @Activate
- protected void activate() {
- coreService.registerApplication(SWITCHING_APP_ID);
- deviceService.addListener(internalDeviceListener);
- openstackNodeService.addListener(internalNodeListener);
- hostProvider = hostProviderRegistry.register(this);
-
- log.info("Started");
- }
-
- @Deactivate
- protected void deactivate() {
- hostProviderRegistry.unregister(this);
- deviceService.removeListener(internalDeviceListener);
- openstackNodeService.removeListener(internalNodeListener);
-
- deviceEventExecutor.shutdown();
- configEventExecutor.shutdown();
-
- log.info("Stopped");
- }
-
- @Override
- public void triggerProbe(Host host) {
- // no probe is required
- }
-
- private void processPortAdded(Port port) {
- // TODO check the node state is COMPLETE
- OpenstackPort osPort = openstackService.port(port);
- if (osPort == null) {
- log.warn("Failed to get OpenStack port for {}", port);
- return;
- }
-
- OpenstackNetwork osNet = openstackService.network(osPort.networkId());
- if (osNet == null) {
- log.warn("Failed to get OpenStack network {}",
- osPort.networkId());
- return;
- }
-
- Map.Entry<String, Ip4Address> fixedIp = osPort.fixedIps().entrySet().stream().findFirst().get();
-
- OpenstackSubnet openstackSubnet = openstackService.subnets().stream()
- .filter(n -> n.networkId().equals(osPort.networkId()) &&
- n.id().equals(fixedIp.getKey()))
- .findFirst().orElse(null);
- if (openstackSubnet == null) {
- log.warn("Failed to find subnet for {}", osPort);
- return;
- }
-
- registerDhcpInfo(osPort, openstackSubnet);
- ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
-
- DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
- .set(NETWORK_ID, osPort.networkId())
- .set(SUBNET_ID, fixedIp.getKey())
- .set(PORT_ID, osPort.id())
- .set(VXLAN_ID, osNet.segmentId())
- .set(TENANT_ID, osNet.tenantId())
- // TODO remove gateway IP from host annotation
- .set(GATEWAY_IP, openstackSubnet.gatewayIp());
-
- HostDescription hostDesc = new DefaultHostDescription(
- osPort.macAddress(),
- VlanId.NONE,
- new HostLocation(connectPoint, System.currentTimeMillis()),
- Sets.newHashSet(osPort.fixedIps().values()),
- annotations.build());
-
- HostId hostId = HostId.hostId(osPort.macAddress());
- hostProvider.hostDetected(hostId, hostDesc, false);
- }
-
- private void processPortRemoved(Port port) {
- ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
- removeHosts(connectPoint);
- }
-
- private void removeHosts(ConnectPoint connectPoint) {
- hostService.getConnectedHosts(connectPoint).stream()
- .forEach(host -> {
- dhcpService.removeStaticMapping(host.mac());
- hostProvider.hostVanished(host.id());
- });
- }
-
- private void registerDhcpInfo(OpenstackPort openstackPort, OpenstackSubnet openstackSubnet) {
- checkNotNull(openstackPort);
- checkNotNull(openstackSubnet);
- checkArgument(!openstackPort.fixedIps().isEmpty());
-
- Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get();
- IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr());
- Ip4Address broadcast = Ip4Address.makeMaskedAddress(
- ipAddress,
- subnetPrefix.prefixLength());
-
- // TODO: supports multiple DNS servers
- Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ?
- DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0);
-
- IpAssignment ipAssignment = IpAssignment.builder()
- .ipAddress(ipAddress)
- .leasePeriod(DHCP_INFINITE_LEASE)
- .timestamp(new Date())
- .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength()))
- .broadcast(broadcast)
- .domainServer(domainServer)
- .assignmentStatus(Option_RangeNotEnforced)
- .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp()))
- .build();
-
- dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment);
- }
-
- private class InternalDeviceListener implements DeviceListener {
-
- @Override
- public void event(DeviceEvent event) {
- Device device = event.subject();
- if (!mastershipService.isLocalMaster(device.id())) {
- // do not allow to proceed without mastership
- return;
- }
-
- Port port = event.port();
- if (port == null) {
- return;
- }
-
- String portName = port.annotations().value(PORT_NAME);
- if (Strings.isNullOrEmpty(portName) ||
- !portName.startsWith(PORT_NAME_PREFIX_VM)) {
- // handles VM connected port event only
- return;
- }
-
- switch (event.type()) {
- case PORT_UPDATED:
- if (!event.port().isEnabled()) {
- deviceEventExecutor.execute(() -> processPortRemoved(event.port()));
- } else if (event.port().isEnabled()) {
- deviceEventExecutor.execute(() -> processPortAdded(event.port()));
- }
- break;
- case PORT_ADDED:
- deviceEventExecutor.execute(() -> processPortAdded(event.port()));
- break;
- default:
- break;
- }
- }
- }
-
- private class InternalOpenstackNodeListener implements OpenstackNodeListener {
-
- @Override
- public void event(OpenstackNodeEvent event) {
- OpenstackNode node = event.node();
- // TODO check leadership of the node and make only the leader process
-
- switch (event.type()) {
- case COMPLETE:
- log.info("COMPLETE node {} detected", node.hostname());
-
- // adds existing VMs running on the complete state node
- deviceService.getPorts(node.intBridge()).stream()
- .filter(port -> port.annotations().value(PORT_NAME)
- .startsWith(PORT_NAME_PREFIX_VM) &&
- port.isEnabled())
- .forEach(port -> {
- deviceEventExecutor.execute(() -> processPortAdded(port));
- log.info("VM is detected on {}", port);
- });
-
- // removes stale VMs
- hostService.getHosts().forEach(host -> {
- if (deviceService.getPort(host.location().deviceId(),
- host.location().port()) == null) {
- deviceEventExecutor.execute(() -> removeHosts(host.location()));
- log.info("Removed stale VM {}", host.location());
- }
- });
- break;
- case INCOMPLETE:
- log.warn("{} is changed to INCOMPLETE state", node);
- break;
- case INIT:
- case DEVICE_CREATED:
- default:
- break;
- }
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
new file mode 100644
index 0000000..fe07b9d
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
@@ -0,0 +1,294 @@
+/*
+ * 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.impl;
+
+import com.google.common.base.Strings;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Tools;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+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.host.HostService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.openstack4j.model.network.Network;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_CREATE_TIME;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
+
+@Service
+@Component(immediate = true)
+public final class OpenstackSwitchingHostProvider extends AbstractProvider implements HostProvider {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String PORT_NAME_PREFIX_VM = "tap";
+ private static final String ERR_ADD_HOST = "Failed to add host: ";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostProviderRegistry hostProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+ private final ExecutorService deviceEventExecutor =
+ Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
+ private final ExecutorService configEventExecutor =
+ Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "config-event"));
+ private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
+ private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
+
+ private HostProviderService hostProvider;
+
+ /**
+ * Creates OpenStack switching host provider.
+ */
+ public OpenstackSwitchingHostProvider() {
+ super(new ProviderId("host", OPENSTACK_NETWORKING_APP_ID));
+ }
+
+ @Activate
+ protected void activate() {
+ coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+ deviceService.addListener(internalDeviceListener);
+ osNodeService.addListener(internalNodeListener);
+ hostProvider = hostProviderRegistry.register(this);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ hostProviderRegistry.unregister(this);
+ osNodeService.removeListener(internalNodeListener);
+ deviceService.removeListener(internalDeviceListener);
+
+ deviceEventExecutor.shutdown();
+ configEventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ @Override
+ public void triggerProbe(Host host) {
+ // no probe is required
+ }
+
+ private void processPortAdded(Port port) {
+ // TODO check the node state is COMPLETE
+ org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
+ if (osPort == null) {
+ log.warn(ERR_ADD_HOST + "OpenStack port for {} not found", port);
+ return;
+ }
+
+ Network osNet = osNetworkService.network(osPort.getNetworkId());
+ if (osNet == null) {
+ log.warn(ERR_ADD_HOST + "OpenStack network {} not found",
+ osPort.getNetworkId());
+ return;
+ }
+
+ if (osPort.getFixedIps().isEmpty()) {
+ log.warn(ERR_ADD_HOST + "no fixed IP for port {}", osPort.getId());
+ return;
+ }
+
+ MacAddress macAddr = MacAddress.valueOf(osPort.getMacAddress());
+ Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
+ .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
+ .collect(Collectors.toSet());
+ ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+
+ DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+ .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
+ .set(ANNOTATION_PORT_ID, osPort.getId())
+ .set(ANNOTATION_CREATE_TIME, String.valueOf(System.currentTimeMillis()));
+
+ HostDescription hostDesc = new DefaultHostDescription(
+ macAddr,
+ VlanId.NONE,
+ new HostLocation(connectPoint, System.currentTimeMillis()),
+ fixedIps,
+ annotations.build());
+
+ HostId hostId = HostId.hostId(macAddr);
+ hostProvider.hostDetected(hostId, hostDesc, false);
+ }
+
+ private void processPortRemoved(Port port) {
+ ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+ hostService.getConnectedHosts(connectPoint).forEach(host -> {
+ hostProvider.hostVanished(host.id());
+ });
+ }
+
+ private class InternalDeviceListener implements DeviceListener {
+
+ @Override
+ public boolean isRelevant(DeviceEvent event) {
+ Device device = event.subject();
+ if (!mastershipService.isLocalMaster(device.id())) {
+ // do not allow to proceed without mastership
+ return false;
+ }
+ Port port = event.port();
+ if (port == null) {
+ return false;
+ }
+ String portName = port.annotations().value(PORT_NAME);
+ if (Strings.isNullOrEmpty(portName) ||
+ !portName.startsWith(PORT_NAME_PREFIX_VM)) {
+ // handles Nova created port event only
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case PORT_UPDATED:
+ if (!event.port().isEnabled()) {
+ deviceEventExecutor.execute(() -> {
+ log.debug("Instance port {} is removed from {}",
+ event.port().annotations().value(PORT_NAME),
+ event.subject().id());
+ processPortRemoved(event.port());
+ });
+ } else if (event.port().isEnabled()) {
+ deviceEventExecutor.execute(() -> {
+ log.debug("Instance Port {} is detected from {}",
+ event.port().annotations().value(PORT_NAME),
+ event.subject().id());
+ processPortAdded(event.port());
+ });
+ }
+ break;
+ case PORT_ADDED:
+ deviceEventExecutor.execute(() -> {
+ log.debug("Instance port {} is detected from {}",
+ event.port().annotations().value(PORT_NAME),
+ event.subject().id());
+ processPortAdded(event.port());
+ });
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private class InternalOpenstackNodeListener implements OpenstackNodeListener {
+
+ @Override
+ public void event(OpenstackNodeEvent event) {
+ OpenstackNode osNode = event.subject();
+ // TODO check leadership of the node and make only the leader process
+
+ switch (event.type()) {
+ case COMPLETE:
+ deviceEventExecutor.execute(() -> {
+ log.info("COMPLETE node {} is detected", osNode.hostname());
+ processCompleteNode(event.subject());
+ });
+ break;
+ case INCOMPLETE:
+ log.warn("{} is changed to INCOMPLETE state", osNode);
+ break;
+ case INIT:
+ case DEVICE_CREATED:
+ default:
+ break;
+ }
+ }
+
+ private void processCompleteNode(OpenstackNode osNode) {
+ deviceService.getPorts(osNode.intBridge()).stream()
+ .filter(port -> port.annotations().value(PORT_NAME)
+ .startsWith(PORT_NAME_PREFIX_VM) &&
+ port.isEnabled())
+ .forEach(port -> {
+ log.debug("Instance port {} is detected from {}",
+ port.annotations().value(PORT_NAME),
+ osNode.hostname());
+ processPortAdded(port);
+ });
+
+ Tools.stream(hostService.getHosts())
+ .filter(host -> deviceService.getPort(
+ host.location().deviceId(),
+ host.location().port()) == null)
+ .forEach(host -> {
+ log.info("Remove stale host {}", host.id());
+ hostProvider.hostVanished(host.id());
+ });
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java
deleted file mode 100644
index d9efb3f..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
-* 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.impl;
-
-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.apache.felix.scr.annotations.Service;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-
-
-/**
- * Populates switching flow rules.
- */
-@Service
-@Component(immediate = true)
-public final class OpenstackSwitchingManager extends AbstractVmHandler
- implements OpenstackSwitchingService {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected FlowObjectiveService flowObjectiveService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OpenstackNodeService nodeService;
-
- private ApplicationId appId;
-
- @Activate
- protected void activate() {
- super.activate();
- appId = coreService.registerApplication(SWITCHING_APP_ID);
- }
-
- @Deactivate
- protected void deactivate() {
- super.deactivate();
- }
-
- private void populateSwitchingRules(Host host) {
- setFlowRulesForTunnelTag(host, true);
- setFlowRulesForTrafficToSameCnode(host, true);
- setFlowRulesForTrafficToDifferentCnode(host, true);
-
- log.debug("Populated switching rule for {}", host);
- }
-
- private void removeSwitchingRules(Host host) {
- setFlowRulesForTunnelTag(host, false);
- setFlowRulesForTrafficToSameCnode(host, false);
- removeFlowRuleForVMsInDiffrentCnode(host);
-
- log.debug("Removed switching rule for {}", host);
- }
-
- private void setFlowRulesForTunnelTag(Host host, boolean install) {
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchInPort(host.location().port());
-
- tBuilder.setTunnelId(Long.valueOf(getVni(host)));
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, host.location().deviceId(),
- sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
- TUNNELTAG_RULE_PRIORITY, install);
- }
-
- private void setFlowRulesForTrafficToSameCnode(Host host, boolean install) {
- //For L2 Switching Case
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(getIp(host).toIpPrefix())
- .matchTunnelId(Long.valueOf(getVni(host)));
-
- // Destination setting is required for routing cases.
- // We do not believe the rule would not degrade the forwarding performance.
- // But, if it does, we need to move the rule in a separate routing table.
- tBuilder.setEthDst(host.mac())
- .setOutput(host.location().port());
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, host.location().deviceId(),
- sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
- SWITCHING_RULE_PRIORITY, install);
- }
-
- private void setFlowRulesForTrafficToDifferentCnode(Host host, boolean install) {
- Ip4Address localVmIp = getIp(host);
- DeviceId localDeviceId = host.location().deviceId();
- Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
-
- if (!localDataIp.isPresent()) {
- log.debug("Failed to get data IP for device {}",
- host.location().deviceId());
- return;
- }
-
- String vni = getVni(host);
- getVmsInDifferentCnode(host).forEach(remoteVm -> {
- Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
- if (remoteDataIp.isPresent()) {
- setVxLanFlowRule(vni,
- localDeviceId,
- remoteDataIp.get().getIp4Address(),
- getIp(remoteVm), install);
-
- setVxLanFlowRule(vni,
- remoteVm.location().deviceId(),
- localDataIp.get().getIp4Address(),
- localVmIp, install);
- }
- });
- }
-
- private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
- Ip4Address vmIp, boolean install) {
- Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
- if (!tunnelPort.isPresent()) {
- log.warn("Failed to get tunnel port from {}", deviceId);
- return;
- }
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchTunnelId(Long.parseLong(vni))
- .matchIPDst(vmIp.toIpPrefix());
- tBuilder.extension(buildExtension(deviceService, deviceId, remoteIp), deviceId)
- .setOutput(tunnelPort.get());
-
- RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId,
- sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
- SWITCHING_RULE_PRIORITY, install);
- }
-
- private void removeFlowRuleForVMsInDiffrentCnode(Host host) {
- DeviceId deviceId = host.location().deviceId();
- final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId)
- .stream()
- .filter(this::isValidHost)
- .anyMatch(h -> Objects.equals(getVni(h), getVni(host)));
-
- getVmsInDifferentCnode(host).forEach(h -> {
- setVxLanFlowRule(getVni(host), h.location().deviceId(), Ip4Address.valueOf(0), getIp(host), false);
- if (!anyPortRemainedInSameCnode) {
- setVxLanFlowRule(getVni(host), deviceId, Ip4Address.valueOf(0), getIp(h), false);
- }
- });
- }
-
- @Override
- protected void hostDetected(Host host) {
- populateSwitchingRules(host);
- log.info("Added new virtual machine to switching service {}", host);
- }
-
- @Override
- protected void hostRemoved(Host host) {
- removeSwitchingRules(host);
- log.info("Removed virtual machine from switching service {}", host);
- }
-
- @Override
- public void reinstallVmFlow(Host host) {
- if (host == null) {
- hostService.getHosts().forEach(h -> {
- populateSwitchingRules(h);
- log.info("Re-Install data plane flow of virtual machine {}", h);
- });
- } else {
- populateSwitchingRules(host);
- log.info("Re-Install data plane flow of virtual machine {}", host);
- }
- }
-
- @Override
- public void purgeVmFlow(Host host) {
- if (host == null) {
- hostService.getHosts().forEach(h -> {
- removeSwitchingRules(h);
- log.info("Purge data plane flow of virtual machine {}", h);
- });
- } else {
- removeSwitchingRules(host);
- log.info("Purge data plane flow of virtual machine {}", host);
- }
- }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
index c39afd6..d58cc61 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
@@ -15,14 +15,13 @@
*/
package org.onosproject.openstacknetworking.web;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackRouterInterface;
-import org.onosproject.openstackinterface.web.OpenstackRouterCodec;
-import org.onosproject.openstackinterface.web.OpenstackRouterInterfaceCodec;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,10 +32,17 @@
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import java.io.InputStream;
-import static com.google.common.base.Preconditions.checkNotNull;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
/**
* Handles REST API call of Neturon L3 plugin.
@@ -46,142 +52,140 @@
public class OpensatckRouterWebResource extends AbstractWebResource {
private final Logger log = LoggerFactory.getLogger(getClass());
- private static final OpenstackRouterInterfaceCodec ROUTER_INTERFACE_CODEC
- = new OpenstackRouterInterfaceCodec();
- private static final OpenstackRouterCodec ROUTER_CODEC
- = new OpenstackRouterCodec();
+ private static final String MESSAGE_ROUTER = "Received router %s request";
+ private static final String MESSAGE_ROUTER_IFACE = "Received router interface %s request";
+ private static final String ROUTERS = "routers";
+ private final OpenstackRouterAdminService adminService =
+ DefaultServiceDirectory.getService(OpenstackRouterAdminService.class);
+
+ @Context
+ private UriInfo uriInfo;
+
+ /**
+ * Creates a router from the JSON input stream.
+ *
+ * @param input router JSON input stream
+ * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+ * is invalid or duplicated router already exists
+ */
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createRouter(InputStream input) {
- checkNotNull(input);
- try {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode routerNode = (ObjectNode) mapper.readTree(input);
+ log.trace(String.format(MESSAGE_ROUTER, "CREATE"));
- OpenstackRouter openstackRouter
- = ROUTER_CODEC.decode(routerNode, this);
+ final NeutronRouter osRouter = readRouter(input);
+ adminService.createRouter(osRouter);
- OpenstackRoutingService routingService
- = getService(OpenstackRoutingService.class);
- routingService.createRouter(openstackRouter);
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path(ROUTERS)
+ .path(osRouter.getId());
- log.debug("REST API CREATE router is called {}", input.toString());
- return Response.status(Response.Status.OK).build();
- } catch (Exception e) {
- log.error("Create Router failed because of exception {}",
- e.toString());
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
- .build();
- }
+ return created(locationBuilder.build()).build();
}
+ /**
+ * Updates the router with the specified identifier.
+ *
+ * @param id router identifier
+ * @param input router JSON input stream
+ * @return 200 OK with the updated router, 400 BAD_REQUEST if the requested
+ * router does not exist
+ */
@PUT
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateRouter(@PathParam("id") String id, InputStream input) {
- checkNotNull(input);
- try {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode routerNode = (ObjectNode) mapper.readTree(input);
+ log.trace(String.format(MESSAGE_ROUTER, "UPDATE " + id));
- OpenstackRouter or = ROUTER_CODEC.decode(routerNode, this);
+ final NeutronRouter osRouter = readRouter(input);
+ osRouter.setId(id);
+ adminService.updateRouter(osRouter);
- OpenstackRouter.Builder osBuilder = new OpenstackRouter.Builder()
- .tenantId(or.tenantId())
- .id(id)
- .name(or.name())
- .status(OpenstackRouter.RouterStatus.ACTIVE)
- .adminStateUp(Boolean.valueOf(or.adminStateUp()))
- .gatewayExternalInfo(or.gatewayExternalInfo());
-
- OpenstackRoutingService routingService
- = getService(OpenstackRoutingService.class);
- routingService.updateRouter(osBuilder.build());
-
- log.debug("REST API UPDATE router is called from router {}", input.toString());
- return Response.status(Response.Status.OK).build();
- } catch (Exception e) {
- log.error("Updates Router failed because of exception {}",
- e.toString());
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
- .build();
- }
+ return status(Response.Status.OK).build();
}
+ /**
+ * Updates the router with the specified router interface.
+ *
+ * @param id router identifier
+ * @param input router interface JSON input stream
+ * @return 200 OK with the updated router interface, 400 BAD_REQUEST if the
+ * requested router does not exist
+ */
@PUT
@Path("{id}/add_router_interface")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response addRouterInterface(@PathParam("id") String id, InputStream input) {
- checkNotNull(input);
- try {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input);
+ log.trace(String.format(MESSAGE_ROUTER_IFACE, "UPDATE " + id));
- OpenstackRouterInterface openstackRouterInterface
- = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this);
+ final NeutronRouterInterface osRouterIface = readRouterInterface(input);
+ adminService.addRouterInterface(osRouterIface);
- OpenstackRoutingService routingService
- = getService(OpenstackRoutingService.class);
- routingService.addRouterInterface(openstackRouterInterface);
-
- log.debug("REST API AddRouterInterface is called from router {} portId: {}, subnetId: {}, tenantId: {}",
- openstackRouterInterface.id(), openstackRouterInterface.portId(),
- openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId());
-
- return Response.status(Response.Status.OK).build();
- } catch (Exception e) {
- log.error("AddRouterInterface failed because of exception {}",
- e.toString());
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
- .build();
- }
+ return status(Response.Status.OK).build();
}
- @DELETE
- @Path("{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- public Response deleteRouter(@PathParam("id") String id) {
- checkNotNull(id);
- OpenstackRoutingService routingService
- = getService(OpenstackRoutingService.class);
- routingService.removeRouter(id);
-
- log.debug("REST API DELETE routers is called {}", id);
- return Response.noContent().build();
- }
-
+ /**
+ * Updates the router with the specified router interface.
+ *
+ * @param id router identifier
+ * @param input router interface JSON input stream
+ * @return 200 OK with the updated router interface, 400 BAD_REQUEST if the
+ * requested router does not exist
+ */
@PUT
@Path("{id}/remove_router_interface")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response removeRouterInterface(@PathParam("id") String id, InputStream input) {
- checkNotNull(id);
- checkNotNull(input);
+ log.trace(String.format(MESSAGE_ROUTER_IFACE, "DELETE " + id));
+
+ final NeutronRouterInterface osRouterIface = readRouterInterface(input);
+ adminService.removeRouterInterface(osRouterIface.getPortId());
+
+ return status(Response.Status.OK).build();
+ }
+
+ /**
+ * Removes the router.
+ *
+ * @param id router identifier
+ * @return 204 NO_CONTENT, 400 BAD_REQUEST if the router does not exist
+ */
+ @DELETE
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response deleteRouter(@PathParam("id") String id) {
+ log.trace(String.format(MESSAGE_ROUTER, "DELETE " + id));
+
+ adminService.removeRouter(id);
+ return noContent().build();
+ }
+
+ private NeutronRouter readRouter(InputStream input) {
try {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input);
-
- OpenstackRouterInterface openstackRouterInterface
- = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this);
-
- OpenstackRoutingService routingService
- = getService(OpenstackRoutingService.class);
- routingService.removeRouterInterface(openstackRouterInterface);
-
- log.debug("REST API RemoveRouterInterface is called from router {} portId: {}, subnetId: {}," +
- "tenantId: {}", openstackRouterInterface.id(), openstackRouterInterface.portId(),
- openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId());
-
- return Response.status(Response.Status.OK).build();
+ JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+ log.trace(mapper().writeValueAsString(jsonTree));
+ return ObjectMapperSingleton.getContext(NeutronRouter.class)
+ .readerFor(NeutronRouter.class)
+ .readValue(jsonTree);
} catch (Exception e) {
- log.error("RemoveRouterInterface failed because of exception {}",
- e.toString());
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
- .build();
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private NeutronRouterInterface readRouterInterface(InputStream input) {
+ try {
+ JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+ log.trace(mapper().writeValueAsString(jsonTree));
+ return ObjectMapperSingleton.getContext(NeutronRouterInterface.class)
+ .readerFor(NeutronRouterInterface.class)
+ .readValue(jsonTree);
+ } catch (Exception e) {
+ throw new IllegalArgumentException();
}
}
}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
index a6880fd..87a8e1a 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
@@ -16,12 +16,12 @@
package org.onosproject.openstacknetworking.web;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.openstackinterface.OpenstackFloatingIP;
-import org.onosproject.openstackinterface.web.OpenstackFloatingIpCodec;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,11 +32,17 @@
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import java.io.InputStream;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
/**
* Handles REST API call of Neutron L3 plugin.
@@ -45,89 +51,84 @@
public class OpenstackFloatingIpWebResource extends AbstractWebResource {
private final Logger log = LoggerFactory.getLogger(getClass());
- private static final OpenstackFloatingIpCodec FLOATING_IP_CODEC = new OpenstackFloatingIpCodec();
+ private static final String MESSAGE = "Received floating IP %s request";
+ private static final String FLOATING_IPS = "floatingips";
+
+ private final OpenstackRouterAdminService adminService =
+ DefaultServiceDirectory.getService(OpenstackRouterAdminService.class);
+
+ @Context
+ private UriInfo uriInfo;
/**
- * Create FloatingIP.
+ * Creates a floating IP from the JSON input stream.
*
- * @param input JSON data describing FloatingIP
- * @return 200 OK
+ * @param input floating ip JSON input stream
+ * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+ * is invalid or duplicated floating ip already exists
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createFloatingIp(InputStream input) {
- checkNotNull(input);
+ log.trace(String.format(MESSAGE, "CREATE"));
- try {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input);
+ final NeutronFloatingIP floatingIp = readFloatingIp(input);
+ adminService.createFloatingIp(floatingIp);
- OpenstackFloatingIP osFloatingIp = FLOATING_IP_CODEC.decode(floatingIpNode, this);
- OpenstackFloatingIpService floatingIpService =
- getService(OpenstackFloatingIpService.class);
- floatingIpService.createFloatingIp(osFloatingIp);
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path(FLOATING_IPS)
+ .path(floatingIp.getId());
- log.debug("REST API CREATE floatingip called");
- return Response.status(Response.Status.OK).build();
- } catch (Exception e) {
- log.error("createFloatingIp failed with {}", e.toString());
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
- .build();
- }
+ return created(locationBuilder.build()).build();
}
/**
- * Update FloatingIP.
+ * Updates the floating IP with the specified identifier.
*
- * @param id FloatingIP identifier
- * @param input JSON data describing FloatingIP
- * @return 200 OK
+ * @param id floating ip identifier
+ * @param input floating ip JSON input stream
+ * @return 200 OK with the updated floating ip, 400 BAD_REQUEST if the requested
+ * floating ip does not exist
*/
@PUT
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateFloatingIp(@PathParam("id") String id, InputStream input) {
- checkNotNull(id);
- checkNotNull(input);
+ log.trace(String.format(MESSAGE, "UPDATE " + id));
- try {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input);
+ final NeutronFloatingIP floatingIp = readFloatingIp(input);
+ adminService.updateFloatingIp(floatingIp);
- OpenstackFloatingIP osFloatingIp = FLOATING_IP_CODEC.decode(floatingIpNode, this);
- OpenstackFloatingIpService floatingIpService =
- getService(OpenstackFloatingIpService.class);
- floatingIpService.updateFloatingIp(osFloatingIp);
-
- log.debug("REST API UPDATE floatingip called {}", id);
- return Response.status(Response.Status.OK).build();
- } catch (Exception e) {
- log.error("updateFloatingIp failed with {}", e.toString());
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
- .build();
- }
+ return status(Response.Status.OK).build();
}
/**
- * Delete FloatingIP.
+ * Removes the floating IP.
*
- * @param id FloatingIP identifier
- * @return 204 OK
+ * @param id floating ip identifier
+ * @return 204 NO_CONTENT, 400 BAD_REQUEST if the floating ip does not exist
*/
@DELETE
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response deleteFloatingIp(@PathParam("id") String id) {
- checkNotNull(id);
+ log.trace(String.format(MESSAGE, "DELETE " + id));
- OpenstackFloatingIpService floatingIpService =
- getService(OpenstackFloatingIpService.class);
- floatingIpService.deleteFloatingIp(id);
-
- log.debug("REST API DELETE floatingip is called {}", id);
- return Response.noContent().build();
+ adminService.removeFloatingIp(id);
+ return noContent().build();
}
+ private NeutronFloatingIP readFloatingIp(InputStream input) {
+ try {
+ JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+ log.trace(mapper().writeValueAsString(jsonTree));
+ return ObjectMapperSingleton.getContext(NeutronFloatingIP.class)
+ .readerFor(NeutronFloatingIP.class)
+ .readValue(jsonTree);
+ } catch (Exception e) {
+ throw new IllegalArgumentException();
+ }
+ }
}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
index c547b2e..6428e60 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
@@ -15,7 +15,12 @@
*/
package org.onosproject.openstacknetworking.web;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,41 +29,106 @@
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import java.io.InputStream;
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
+
/**
* Handles REST API call of Neutron ML2 plugin.
*/
@Path("networks")
public class OpenstackNetworkWebResource extends AbstractWebResource {
+ protected final Logger log = LoggerFactory.getLogger(getClass());
- private final Logger log = LoggerFactory.getLogger(getClass());
+ private static final String MESSAGE = "Received networks %s request";
+ private static final String NETWORKS = "networks";
+ private final OpenstackNetworkAdminService adminService =
+ DefaultServiceDirectory.getService(OpenstackNetworkAdminService.class);
+
+ @Context
+ private UriInfo uriInfo;
+
+ /**
+ * Creates a network from the JSON input stream.
+ *
+ * @param input network JSON input stream
+ * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+ * is invalid or duplicated network already exists
+ */
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createNetwork(InputStream input) {
- log.debug("REST API networks is called {}", input.toString());
- return Response.status(Response.Status.OK).build();
+ log.trace(String.format(MESSAGE, "CREATE"));
+
+ final NeutronNetwork net = readNetwork(input);
+ adminService.createNetwork(net);
+
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path(NETWORKS)
+ .path(net.getId());
+
+ return created(locationBuilder.build()).build();
}
+ /**
+ * Updates the network with the specified identifier.
+ *
+ * @param id network identifier
+ * @param input network JSON input stream
+ * @return 200 OK with the updated network, 400 BAD_REQUEST if the requested
+ * network does not exist
+ */
@PUT
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
- public Response updateNetwork(InputStream input) {
- log.debug("REST API networks is called {}", input.toString());
- return Response.status(Response.Status.OK).build();
+ public Response updateNetwork(@PathParam("id") String id, InputStream input) {
+ log.trace(String.format(MESSAGE, "UPDATE " + id));
+
+ final NeutronNetwork net = readNetwork(input);
+ adminService.updateNetwork(net);
+
+ return status(Response.Status.OK).build();
}
+ /**
+ * Removes the service network.
+ *
+ * @param id network identifier
+ * @return 204 NO_CONTENT, 400 BAD_REQUEST if the network does not exist
+ */
@DELETE
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
- public Response deleteNetwork(InputStream input) {
- log.debug("REST API networks is called {}", input.toString());
- return Response.noContent().build();
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response deleteNetwork(@PathParam("id") String id) {
+ log.trace(String.format(MESSAGE, "DELETE " + id));
+
+ adminService.removeNetwork(id);
+ return noContent().build();
+ }
+
+ private NeutronNetwork readNetwork(InputStream input) {
+ try {
+ JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+ log.trace(mapper().writeValueAsString(jsonTree));
+ return ObjectMapperSingleton.getContext(NeutronNetwork.class)
+ .readerFor(NeutronNetwork.class)
+ .readValue(jsonTree);
+ } catch (Exception e) {
+ throw new IllegalArgumentException();
+ }
}
}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
index 7683b0c..c5bb33c 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
@@ -15,12 +15,12 @@
*/
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.api.OpenstackSecurityGroupService;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,60 +31,103 @@
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.io.IOException;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import java.io.InputStream;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
/**
* Handles Rest API call from Neutron ML2 plugin.
*/
@Path("ports")
public class OpenstackPortWebResource extends AbstractWebResource {
- private final Logger log = LoggerFactory.getLogger(getClass());
+ protected final Logger log = LoggerFactory.getLogger(getClass());
- private static final OpenstackPortCodec PORT_CODEC
- = new OpenstackPortCodec();
+ private static final String MESSAGE = "Received ports %s request";
+ private static final String PORTS = "ports";
+ private final OpenstackNetworkAdminService adminService =
+ DefaultServiceDirectory.getService(OpenstackNetworkAdminService.class);
+
+ @Context
+ private UriInfo uriInfo;
+
+ /**
+ * Creates a port from the JSON input stream.
+ *
+ * @param input port JSON input stream
+ * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+ * is invalid or duplicated port already exists
+ */
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createPorts(InputStream input) {
- return Response.status(Response.Status.OK).build();
+ log.trace(String.format(MESSAGE, "CREATE"));
+
+ final NeutronPort port = readPort(input);
+ adminService.createPort(port);
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path(PORTS)
+ .path(port.getId());
+
+ return created(locationBuilder.build()).build();
}
- @Path("{portUUID}")
- @DELETE
- @Consumes(MediaType.APPLICATION_JSON)
- public Response deletePorts(@PathParam("portUUID") String id) {
- return Response.noContent().build();
- }
-
+ /**
+ * Updates the port with the specified identifier.
+ *
+ * @param id port identifier
+ * @param input port JSON input stream
+ * @return 200 OK with the updated port, 400 BAD_REQUEST if the requested
+ * port does not exist
+ */
@PUT
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
- public Response updatePorts(@PathParam("id") String id, InputStream input) {
- checkNotNull(input);
- checkNotNull(id);
+ public Response updatePort(@PathParam("id") String id, InputStream input) {
+ log.trace(String.format(MESSAGE, "UPDATE " + id));
+ final NeutronPort port = readPort(input);
+ adminService.updatePort(port);
+
+ return status(Response.Status.OK).build();
+ }
+
+ /**
+ * Removes the port with the given id.
+ *
+ * @param id port identifier
+ * @return 204 NO_CONTENT, 400 BAD_REQUEST if the port does not exist
+ */
+ @DELETE
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response deletePorts(@PathParam("id") String id) {
+ log.trace(String.format(MESSAGE, "DELETE " + id));
+
+ adminService.removePort(id);
+ return noContent().build();
+ }
+
+ private NeutronPort readPort(InputStream input) {
try {
- ObjectMapper mapper = new ObjectMapper();
- ObjectNode portNode = (ObjectNode) mapper.readTree(input);
- OpenstackPort osPort = PORT_CODEC.decode(portNode, this);
-
- OpenstackSecurityGroupService sgService
- = getService(OpenstackSecurityGroupService.class);
- sgService.updateSecurityGroup(osPort);
-
- return Response.status(Response.Status.OK).build();
- } catch (IOException e) {
- log.error("UpdatePort post process failed due to {}", e.getMessage());
-
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage())
- .build();
+ JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+ log.trace(mapper().writeValueAsString(jsonTree));
+ return ObjectMapperSingleton.getContext(NeutronPort.class)
+ .readerFor(NeutronPort.class)
+ .readValue(jsonTree);
+ } catch (Exception e) {
+ throw new IllegalArgumentException();
}
}
}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
index ca9f3ab..1897a3b 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
@@ -18,7 +18,13 @@
/**
* Handles Rest API call from Neutron ML2 plugin.
*/
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronSubnet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,36 +35,101 @@
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import java.io.InputStream;
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
+
@Path("subnets")
public class OpenstackSubnetWebResource extends AbstractWebResource {
- protected static final Logger log = LoggerFactory
- .getLogger(OpenstackSubnetWebResource.class);
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+ private static final String MESSAGE = "Received subnets %s request";
+ private static final String SUBNETS = "subnets";
+
+ private final OpenstackNetworkAdminService adminService =
+ DefaultServiceDirectory.getService(OpenstackNetworkAdminService.class);
+
+ @Context
+ private UriInfo uriInfo;
+
+ /**
+ * Creates a subnet from the JSON input stream.
+ *
+ * @param input subnet JSON input stream
+ * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+ * is invalid or duplicated subnet already exists
+ */
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createSubnet(InputStream input) {
- return Response.status(Response.Status.OK).build();
+ log.trace(String.format(MESSAGE, "CREATE"));
+
+ final NeutronSubnet subnet = readSubnet(input);
+ adminService.createSubnet(subnet);
+ UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+ .path(SUBNETS)
+ .path(subnet.getId());
+
+ // TODO fix networking-onos to send Network UPDATE when subnet created
+ return created(locationBuilder.build()).build();
}
-
+ /**
+ * Updates the subnet with the specified identifier.
+ *
+ * @param id subnet identifier
+ * @param input subnet JSON input stream
+ * @return 200 OK with the updated subnet, 400 BAD_REQUEST if the requested
+ * subnet does not exist
+ */
@PUT
- @Path("{subnetId}")
+ @Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
- public Response updateSubnet(@PathParam("subnetId") String id,
- final InputStream input) {
- return Response.status(Response.Status.OK).build();
+ public Response updateSubnet(@PathParam("id") String id, InputStream input) {
+ log.trace(String.format(MESSAGE, "UPDATE " + id));
+
+ final NeutronSubnet subnet = readSubnet(input);
+ adminService.updateSubnet(subnet);
+
+ return status(Response.Status.OK).build();
}
+ /**
+ * Removes the subnet.
+ *
+ * @param id subnet identifier
+ * @return 204 NO_CONTENT, 400 BAD_REQUEST if the subnet does not exist
+ */
@DELETE
- @Path("{subnetId}")
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
- public Response deleteSubnet(@PathParam("subnetId") String id) {
- return Response.noContent().build();
+ public Response deleteSubnet(@PathParam("id") String id) {
+ log.trace(String.format(MESSAGE, "DELETE " + id));
+
+ adminService.removeSubnet(id);
+ return noContent().build();
+ }
+
+ private NeutronSubnet readSubnet(InputStream input) {
+ try {
+ JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+ log.trace(mapper().writeValueAsString(jsonTree));
+ return ObjectMapperSingleton.getContext(NeutronSubnet.class)
+ .readerFor(NeutronSubnet.class)
+ .readValue(jsonTree);
+ } catch (Exception e) {
+ throw new IllegalArgumentException();
+ }
}
}
diff --git a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 93076d8..e14985d 100644
--- a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -1,5 +1,5 @@
<!--
-~ Copyright 2015-present Open Networking Laboratory
+~ Copyright 2017-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.
@@ -16,10 +16,16 @@
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
<command>
- <action class="org.onosproject.openstacknetworking.cli.OpenstackInstancePurgeFlowsCommand"/>
+ <action class="org.onosproject.openstacknetworking.cli.OpenstackNetworkListCommand"/>
</command>
<command>
- <action class="org.onosproject.openstacknetworking.cli.OpenstackInstanceReInstallFlowCommand"/>
+ <action class="org.onosproject.openstacknetworking.cli.OpenstackPortListCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.openstacknetworking.cli.OpenstackRouterListCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.openstacknetworking.cli.OpenstackFloatingIpListCommand"/>
</command>
</command-bundle>
</blueprint>