Smart choice of host for UDP link

This commit is contained in:
Julien Duponchelle committed 2016-08-25 19:14:29 +02:00
1 parent c01bb2436e
commit 134fed8fc5
4 files changed
+169 -5

No files matched your search

+59
View File
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import ipaddress
import aiohttp
import asyncio
import socket
@@ -96,6 +97,9 @@ class Compute:
# Websocket for notifications
self._ws = None
# Cache of interfaces on remote host
self._interfaces_cache = None
def _session(self):
if self._http_session is None or self._http_session.closed is True:
self._http_session = aiohttp.ClientSession()
@@ -122,6 +126,16 @@ class Compute:
self._password = None
self._auth = aiohttp.BasicAuth(self._user, "")
@asyncio.coroutine
def interfaces(self):
"""
Get the list of network on compute
"""
if not self._interfaces_cache:
response = yield from self.get("/network/interfaces")
self._interfaces_cache = response.json
return self._interfaces_cache
@asyncio.coroutine
def update(self, **kwargs):
for kw in kwargs:
@@ -192,6 +206,13 @@ class Compute:
"""
return self._host
@property
def host_ip(self):
"""
Return the IP associated to the host
"""
return socket.gethostbyname(self._host)
@host.setter
def host(self, host):
self._host = host
@@ -491,3 +512,41 @@ class Compute:
path = "/projects/{}/files".format(project.id)
res = yield from self.http_query("GET", path, timeout=120)
return res.json
@asyncio.coroutine
def get_ip_on_same_subnet(self, other_compute):
"""
Try to found the best ip for communication from one compute
to another
:returns: Tuple (ip_for_this_compute, ip_for_other_compute)
"""
if other_compute == self:
return (self.host_ip, self.host_ip)
this_compute_interfaces = yield from self.interfaces()
other_compute_interfaces = yield from other_compute.interfaces()
# Sort interface to put the compute host in first position
# we guess that if user specified this host it could have a reason (VMware Nat / Host only interface)
this_compute_interfaces = sorted(this_compute_interfaces, key=lambda i: i["ip_address"] != self.host_ip)
other_compute_interfaces = sorted(other_compute_interfaces, key=lambda i: i["ip_address"] != other_compute.host_ip)
for this_interface in this_compute_interfaces:
if len(this_interface["ip_address"]) == 0:
continue
this_network = ipaddress.ip_network("{}/{}".format(this_interface["ip_address"], this_interface["netmask"]), strict=False)
for other_interface in other_compute_interfaces:
if len(other_interface["ip_address"]) == 0:
continue
# Avoid stuff like 127.0.0.1
if other_interface["ip_address"] == this_interface["ip_address"]:
continue
other_network = ipaddress.ip_network("{}/{}".format(other_interface["ip_address"], other_interface["netmask"]), strict=False)
if this_network.overlaps(other_network):
return (this_interface["ip_address"], other_interface["ip_address"])
raise ValueError("No common subnet for compute {} and {}".format(self.name, other_compute.name))
+9 -3
View File
@@ -41,7 +41,13 @@ class UDPLink(Link):
adapter_number2 = self._nodes[1]["adapter_number"]
port_number2 = self._nodes[1]["port_number"]
# Reserve a UDP port on both side
# Get an IP allowing communication between both host
try:
(node1_host, node2_host) = yield from node1.compute.get_ip_on_same_subnet(node2.compute)
except ValueError as e:
raise aiohttp.web.HTTPConflict(text=str(e))
# Reserve a UDP port on both side
response = yield from node1.compute.post("/projects/{}/ports/udp".format(self._project.id))
self._node1_port = response.json["udp_port"]
response = yield from node2.compute.post("/projects/{}/ports/udp".format(self._project.id))
@@ -50,7 +56,7 @@ class UDPLink(Link):
# Create the tunnel on both side
data = {
"lport": self._node1_port,
"rhost": node2.compute.host,
"rhost": node2_host,
"rport": self._node2_port,
"type": "nio_udp"
}
@@ -58,7 +64,7 @@ class UDPLink(Link):
data = {
"lport": self._node2_port,
"rhost": node1.compute.host,
"rhost": node1_host,
"rport": self._node1_port,
"type": "nio_udp"
}