Commit 05d32a11 authored by Christos Stavrakakis's avatar Christos Stavrakakis

cyclades: extend VM info with 'port_forwarding'

Extend servers info API response with 'SNF:port_forwarding' attribute,
describing port fowarding rules (DNAT) that are applied to vms. The
description of such rules is done via the new CYCLADES_PORT_FORWARDING
setting. This setting can be either a dictionary mapping to ports to
tuples of (host, port), or a callable object that must return such a
tuple.   The caller will pass to the callable the following positional
arguments, in the following order:
* server_id: The ID of the VM in the DB
* ip_address: The IPv4 address of the public VM NIC
* fqdn: The FQDN of the VM
* user: The UUID of the owner of the VM
parent 9070fb1d
......@@ -41,8 +41,12 @@ Cyclades
* Compute quotas for CPU and memory of running vms.
* Obsolete PUBLIC_USE_POOL setting, since Cyclades manages IP pool for all
type of networks.
* Extend servers info API respones with 'SNF:fqdn' attribute, and introduce
* Extend servers info API response with 'SNF:fqdn' attribute, and introduce
CYCLADES_SERVERS_FQDN to set the template for servers FDQN.
* Extend servers info API response with 'SNF:port_forwarding' attribute,
describing port fowarding rules (DNAT) that are applied to vms. The
description of such rules is done via the new CYCLADES_PORT_FORWARDING
setting.
Pithos
------
......
......@@ -112,3 +112,24 @@
## the id of the VM. If set to 'None' the first public IPv4 or IPv6 address
## of the VM will be used.
#CYCLADES_SERVERS_FQDN = 'snf-%(id)s.vm.example.synnefo.org'
#
## Description of applied port forwarding rules (DNAT) for Cyclades VMs. This
## setting contains a mapping from the port of each VM to a tuple contaning the
## destination IP/hostname and the new port: (host, port). Instead of a tuple a
## python callable object may be used which must return such a tuple. The caller
## will pass to the callable the following positional arguments, in the
## following order:
## * server_id: The ID of the VM in the DB
## * ip_address: The IPv4 address of the public VM NIC
## * fqdn: The FQDN of the VM
## * user: The UUID of the owner of the VM
##
## Here is an example describing the mapping of the SSH port of all VMs to
## the external address 'gate.example.synnefo.org' and port 60000+server_id.
## e.g. iptables -t nat -A prerouting -d gate.example.synnefo.org \
## --dport (61000 # $(VM_ID)) -j DNAT --to-destination $(VM_IP):22
##CYCLADES_PORT_FORWARDING = {
## 22: lambda ip_address, server_id, fqdn, user:
## ("gate.example.synnefo.org", 61000 + server_id),
##}
#CYCLADES_PORT_FORWARDING = {}
......@@ -170,7 +170,9 @@ def vm_to_dict(vm, detail=False):
d["config_drive"] = ""
d["accessIPv4"] = ""
d["accessIPv6"] = ""
d["SNF:fqdn"] = get_server_fqdn(vm)
fqdn = get_server_fqdn(vm)
d["SNF:fqdn"] = fqdn
d["SNF:port_forwarding"] = get_server_port_forwarding(vm, fqdn)
return d
......@@ -196,6 +198,45 @@ def get_server_fqdn(vm):
raise faults.InternalServerError(msg)
def get_server_port_forwarding(vm, fqdn):
"""Create API 'port_forwarding' attribute from corresponding setting.
Create the 'port_forwarding' API vm attribute based on the corresponding
setting (CYCLADES_PORT_FORWARDING), which can be either a tuple
of the form (host, port) or a callable object returning such tuple. In
case of callable object, must be called with the following arguments:
* ip_address
* server_id
* fqdn
* owner UUID
"""
port_forwarding = {}
for dport, to_dest in settings.CYCLADES_PORT_FORWARDING.items():
if hasattr(to_dest, "__call__"):
public_nics = vm.nics.filter(network__public=True, state="ACTIVE")\
.exclude(ipv4=None).order_by('index')
if public_nics:
vm_ipv4 = public_nics[0].ipv4
else:
vm_ipv4 = None
to_dest = to_dest(vm_ipv4, vm.id, fqdn, vm.userid)
msg = ("Invalid setting: CYCLADES_PORT_FOWARDING."
" Value must be a tuple of two elements (host, port).")
if to_dest is None:
continue
if not isinstance(to_dest, tuple) or len(to_dest) != 2:
raise faults.InternalServerError(msg)
else:
try:
host, port = to_dest
except (TypeError, ValueError):
raise faults.InternalServerError(msg)
port_forwarding[dport] = {"host": host, "port": str(port)}
return port_forwarding
def diagnostics_to_dict(diagnostics):
"""
Extract api data from diagnostics QuerySet.
......
......@@ -202,6 +202,41 @@ class ServerAPITest(ComputeAPITest):
server = json.loads(response.content)['server']
self.assertEqual(server["SNF:fqdn"], nic.ipv4)
def test_server_port_forwarding(self):
vm = mfactory.VirtualMachineFactory()
ports = {
22: ("foo", 61000),
80: lambda ip, id, fqdn, user: ("bar", 61001)}
with override_settings(settings,
CYCLADES_PORT_FORWARDING=ports):
response = self.myget("servers/%d" % vm.id, vm.userid)
server = json.loads(response.content)['server']
self.assertEqual(server["SNF:port_forwarding"],
{"22": {"host": "foo", "port": "61000"},
"80": {"host": "bar", "port": "61001"}})
def _port_from_ip(ip, base):
fields = ip.split('.', 4)
return (base + 256*int(fields[2]) + int(fields[3]))
ports = {
22: lambda ip, id, fqdn, user:
ip and ("gate", _port_from_ip(ip, 10000)) or None}
with override_settings(settings,
CYCLADES_PORT_FORWARDING=ports):
response = self.myget("servers/%d" % vm.id, vm.userid)
server = json.loads(response.content)['server']
self.assertEqual(server["SNF:port_forwarding"], {})
mfactory.NetworkInterfaceFactory(machine=vm, ipv4="192.168.2.2",
network__public=True)
with override_settings(settings,
CYCLADES_PORT_FORWARDING=ports):
response = self.myget("servers/%d" % vm.id, vm.userid)
server = json.loads(response.content)['server']
self.assertEqual(server["SNF:port_forwarding"],
{"22": {"host": "gate", "port": "10514"}})
def test_server_building_nics(self):
db_vm = self.vm2
user = self.vm2.userid
......
......@@ -112,3 +112,24 @@ CYCLADES_PROXY_USER_SERVICES = True
# the id of the VM. If set to 'None' the first public IPv4 or IPv6 address
# of the VM will be used.
CYCLADES_SERVERS_FQDN = 'snf-%(id)s.vm.example.synnefo.org'
# Description of applied port forwarding rules (DNAT) for Cyclades VMs. This
# setting contains a mapping from the port of each VM to a tuple contaning the
# destination IP/hostname and the new port: (host, port). Instead of a tuple a
# python callable object may be used which must return such a tuple. The caller
# will pass to the callable the following positional arguments, in the
# following order:
# * server_id: The ID of the VM in the DB
# * ip_address: The IPv4 address of the public VM NIC
# * fqdn: The FQDN of the VM
# * user: The UUID of the owner of the VM
#
# Here is an example describing the mapping of the SSH port of all VMs to
# the external address 'gate.example.synnefo.org' and port 60000+server_id.
# e.g. iptables -t nat -A prerouting -d gate.example.synnefo.org \
# --dport (61000 + $(VM_ID)) -j DNAT --to-destination $(VM_IP):22
#CYCLADES_PORT_FORWARDING = {
# 22: lambda ip_address, server_id, fqdn, user:
# ("gate.example.synnefo.org", 61000 + server_id),
#}
CYCLADES_PORT_FORWARDING = {}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment