Revision 2522e489

b/Changelog
41 41
* Compute quotas for CPU and memory of running vms.
42 42
* Obsolete PUBLIC_USE_POOL setting, since Cyclades manages IP pool for all
43 43
  type of networks.
44
* Extend servers info API respones with 'SNF:fqdn' attribute, and introduce
44
* Extend servers info API response with 'SNF:fqdn' attribute, and introduce
45 45
  CYCLADES_SERVERS_FQDN to set the template for servers FDQN.
46
* Extend servers info API response with 'SNF:port_forwarding' attribute,
47
  describing port fowarding rules (DNAT) that are applied to vms. The
48
  description of such rules is done via the new CYCLADES_PORT_FORWARDING
49
  setting.
46 50

  
47 51
Pithos
48 52
------
b/snf-cyclades-app/conf/20-snf-cyclades-app-api.conf
112 112
## the id of the VM. If set to 'None' the first public IPv4 or IPv6 address
113 113
## of the VM will be used.
114 114
#CYCLADES_SERVERS_FQDN = 'snf-%(id)s.vm.example.synnefo.org'
115
#
116
## Description of applied port forwarding rules (DNAT) for Cyclades VMs. This
117
## setting contains a mapping from the port of each VM to a tuple contaning the
118
## destination IP/hostname and the new port: (host, port). Instead of a tuple a
119
## python callable object may be used which must return such a tuple. The caller
120
## will pass to the callable the following positional arguments, in the
121
## following order:
122
## * server_id: The ID of the VM in the DB
123
## * ip_address: The IPv4 address of the public VM NIC
124
## * fqdn: The FQDN of the VM
125
## * user: The UUID of the owner of the VM
126
##
127
## Here is an example describing the mapping of the SSH port of all VMs to
128
## the external address 'gate.example.synnefo.org' and port 60000+server_id.
129
## e.g. iptables -t nat -A prerouting -d gate.example.synnefo.org \
130
## --dport (61000 # $(VM_ID)) -j DNAT --to-destination $(VM_IP):22
131
##CYCLADES_PORT_FORWARDING = {
132
##    22: lambda ip_address, server_id, fqdn, user:
133
##               ("gate.example.synnefo.org", 61000 + server_id),
134
##}
135
#CYCLADES_PORT_FORWARDING = {}
b/snf-cyclades-app/synnefo/api/servers.py
170 170
        d["config_drive"] = ""
171 171
        d["accessIPv4"] = ""
172 172
        d["accessIPv6"] = ""
173
        d["SNF:fqdn"] = get_server_fqdn(vm)
173
        fqdn = get_server_fqdn(vm)
174
        d["SNF:fqdn"] = fqdn
175
        d["SNF:port_forwarding"] = get_server_port_forwarding(vm, fqdn)
174 176

  
175 177
    return d
176 178

  
......
196 198
        raise faults.InternalServerError(msg)
197 199

  
198 200

  
201
def get_server_port_forwarding(vm, fqdn):
202
    """Create API 'port_forwarding' attribute from corresponding setting.
203

  
204
    Create the 'port_forwarding' API vm attribute based on the corresponding
205
    setting (CYCLADES_PORT_FORWARDING), which can be either a tuple
206
    of the form (host, port) or a callable object returning such tuple. In
207
    case of callable object, must be called with the following arguments:
208
    * ip_address
209
    * server_id
210
    * fqdn
211
    * owner UUID
212

  
213
    """
214
    port_forwarding = {}
215
    for dport, to_dest in settings.CYCLADES_PORT_FORWARDING.items():
216
        if hasattr(to_dest, "__call__"):
217
            public_nics = vm.nics.filter(network__public=True, state="ACTIVE")\
218
                                 .exclude(ipv4=None).order_by('index')
219
            if public_nics:
220
                vm_ipv4 = public_nics[0].ipv4
221
            else:
222
                vm_ipv4 = None
223
            to_dest = to_dest(vm_ipv4, vm.id, fqdn, vm.userid)
224
        msg = ("Invalid setting: CYCLADES_PORT_FOWARDING."
225
               " Value must be a tuple of two elements (host, port).")
226
        if to_dest is None:
227
            continue
228
        if not isinstance(to_dest, tuple) or len(to_dest) != 2:
229
                raise faults.InternalServerError(msg)
230
        else:
231
            try:
232
                host, port = to_dest
233
            except (TypeError, ValueError):
234
                raise faults.InternalServerError(msg)
235

  
236
        port_forwarding[dport] = {"host": host, "port": str(port)}
237
    return port_forwarding
238

  
239

  
199 240
def diagnostics_to_dict(diagnostics):
200 241
    """
201 242
    Extract api data from diagnostics QuerySet.
b/snf-cyclades-app/synnefo/api/tests/servers.py
202 202
            server = json.loads(response.content)['server']
203 203
            self.assertEqual(server["SNF:fqdn"], nic.ipv4)
204 204

  
205
    def test_server_port_forwarding(self):
206
        vm = mfactory.VirtualMachineFactory()
207
        ports = {
208
            22: ("foo", 61000),
209
            80: lambda ip, id, fqdn, user: ("bar", 61001)}
210
        with override_settings(settings,
211
                               CYCLADES_PORT_FORWARDING=ports):
212
            response = self.myget("servers/%d" % vm.id, vm.userid)
213
            server = json.loads(response.content)['server']
214
            self.assertEqual(server["SNF:port_forwarding"],
215
                             {"22": {"host": "foo", "port": "61000"},
216
                              "80": {"host": "bar", "port": "61001"}})
217

  
218
        def _port_from_ip(ip, base):
219
            fields = ip.split('.', 4)
220
            return (base + 256*int(fields[2]) + int(fields[3]))
221

  
222
        ports = {
223
            22: lambda ip, id, fqdn, user:
224
            ip and ("gate", _port_from_ip(ip, 10000)) or None}
225
        with override_settings(settings,
226
                               CYCLADES_PORT_FORWARDING=ports):
227
            response = self.myget("servers/%d" % vm.id, vm.userid)
228
            server = json.loads(response.content)['server']
229
            self.assertEqual(server["SNF:port_forwarding"], {})
230

  
231
        mfactory.NetworkInterfaceFactory(machine=vm, ipv4="192.168.2.2",
232
                                         network__public=True)
233
        with override_settings(settings,
234
                               CYCLADES_PORT_FORWARDING=ports):
235
            response = self.myget("servers/%d" % vm.id, vm.userid)
236
            server = json.loads(response.content)['server']
237
            self.assertEqual(server["SNF:port_forwarding"],
238
                             {"22": {"host": "gate", "port": "10514"}})
239

  
205 240
    def test_server_building_nics(self):
206 241
        db_vm = self.vm2
207 242
        user = self.vm2.userid
b/snf-cyclades-app/synnefo/app_settings/default/api.py
112 112
# the id of the VM. If set to 'None' the first public IPv4 or IPv6 address
113 113
# of the VM will be used.
114 114
CYCLADES_SERVERS_FQDN = 'snf-%(id)s.vm.example.synnefo.org'
115

  
116
# Description of applied port forwarding rules (DNAT) for Cyclades VMs. This
117
# setting contains a mapping from the port of each VM to a tuple contaning the
118
# destination IP/hostname and the new port: (host, port). Instead of a tuple a
119
# python callable object may be used which must return such a tuple. The caller
120
# will pass to the callable the following positional arguments, in the
121
# following order:
122
# * server_id: The ID of the VM in the DB
123
# * ip_address: The IPv4 address of the public VM NIC
124
# * fqdn: The FQDN of the VM
125
# * user: The UUID of the owner of the VM
126
#
127
# Here is an example describing the mapping of the SSH port of all VMs to
128
# the external address 'gate.example.synnefo.org' and port 60000+server_id.
129
# e.g. iptables -t nat -A prerouting -d gate.example.synnefo.org \
130
# --dport (61000 + $(VM_ID)) -j DNAT --to-destination $(VM_IP):22
131
#CYCLADES_PORT_FORWARDING = {
132
#    22: lambda ip_address, server_id, fqdn, user:
133
#               ("gate.example.synnefo.org", 61000 + server_id),
134
#}
135
CYCLADES_PORT_FORWARDING = {}

Also available in: Unified diff