Revision b3a43976 snf-cyclades-app/synnefo/api/servers.py

b/snf-cyclades-app/synnefo/api/servers.py
43 43
from snf_django.lib.api import faults, utils
44 44

  
45 45
from synnefo.api import util
46
from synnefo.db import query as db_query
47 46
from synnefo.db.models import (VirtualMachine, VirtualMachineMetadata)
48 47
from synnefo.logic import servers, utils as logic_utils
49 48

  
......
105 104
        return api.api_method_not_allowed(request)
106 105

  
107 106

  
108
def nic_to_dict(nic):
107
def nic_to_attachments(nic):
108
    """Convert a NIC object to 'attachments attribute.
109

  
110
    Convert a NIC object to match the format of 'attachments' attribute of the
111
    response to the /servers API call.
112

  
113
    NOTE: The 'ips' of the NIC object have been prefetched in order to avoid DB
114
    queries. No subsequent queries for 'ips' (like filtering) should be
115
    performed because this will return in a new DB query.
116

  
117
    """
109 118
    d = {'id': nic.id,
110
         'network_id': str(nic.network.id),
119
         'network_id': str(nic.network_id),
111 120
         'mac_address': nic.mac,
112 121
         'ipv4': '',
113 122
         'ipv6': ''}
114
    for ip in nic.ips.filter(deleted=False).select_related("subnet"):
115
        ip_type = "floating" if ip.floating_ip else "fixed"
116
        if ip.subnet.ipversion == 4:
117
            d["ipv4"] = ip.address
118
            d["OS-EXT-IPS:type"] = ip_type
119
        else:
120
            d["ipv6"] = ip.address
121
            d["OS-EXT-IPS:type"] = ip_type
122 123

  
123 124
    if nic.firewall_profile:
124 125
        d['firewallProfile'] = nic.firewall_profile
126

  
127
    for ip in nic.ips.all():
128
        if not ip.deleted:
129
            ip_type = "floating" if ip.floating_ip else "fixed"
130
            if ip.ipversion == 4:
131
                d["ipv4"] = ip.address
132
                d["OS-EXT-IPS:type"] = ip_type
133
            else:
134
                d["ipv6"] = ip.address
135
                d["OS-EXT-IPS:type"] = ip_type
125 136
    return d
126 137

  
127 138

  
128 139
def attachments_to_addresses(attachments):
140
    """Convert 'attachments' attribute to 'addresses'.
141

  
142
    Convert a a list of 'attachments' attribute to a list of 'addresses'
143
    attribute, as expected in the response to /servers API call.
144

  
145
    """
129 146
    addresses = {}
130 147
    for nic in attachments:
131
        net_nics = []
148
        net_addrs = []
132 149
        if nic["ipv4"]:
133
            net_nics.append({"version": 4,
134
                             "addr": nic["ipv4"],
135
                             "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
150
            net_addrs.append({"version": 4,
151
                              "addr": nic["ipv4"],
152
                              "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
136 153
        if nic["ipv6"]:
137
            net_nics.append({"version": 6,
138
                             "addr": nic["ipv6"],
139
                             "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
140
        addresses[nic["network_id"]] = net_nics
154
            net_addrs.append({"version": 6,
155
                              "addr": nic["ipv6"],
156
                              "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
157
        addresses[nic["network_id"]] = net_addrs
141 158
    return addresses
142 159

  
143 160

  
......
162 179
        metadata = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
163 180
        d['metadata'] = metadata
164 181

  
165
        vm_nics = vm.nics.filter(state="ACTIVE").order_by("id")
166
        attachments = map(nic_to_dict, vm_nics)
182
        nics = vm.nics.all()
183
        active_nics = filter(lambda nic: nic.state == "ACTIVE", nics)
184
        active_nics.sort(key=lambda nic: nic.id)
185
        attachments = map(nic_to_attachments, active_nics)
167 186
        d['attachments'] = attachments
168 187
        d['addresses'] = attachments_to_addresses(attachments)
169 188

  
......
179 198
        d["config_drive"] = ""
180 199
        d["accessIPv4"] = ""
181 200
        d["accessIPv6"] = ""
182
        fqdn = get_server_fqdn(vm)
201
        fqdn = get_server_fqdn(vm, active_nics)
183 202
        d["SNF:fqdn"] = fqdn
184
        d["SNF:port_forwarding"] = get_server_port_forwarding(vm, fqdn)
185

  
203
        d["SNF:port_forwarding"] = get_server_port_forwarding(vm, active_nics,
204
                                                              fqdn)
186 205
    return d
187 206

  
188 207

  
189
def get_server_fqdn(vm):
208
def get_server_public_ip(vm_nics, version=4):
209
    """Get the first public IP address of a server.
210

  
211
    NOTE: 'vm_nics' objects have prefetched the ips
212
    """
213
    for version in [4, 6]:
214
        for nic in vm_nics:
215
            for ip in nic.ips.all():
216
                if ip.ipversion == version and ip.public:
217
                    return ip
218
    return None
219

  
220

  
221
def get_server_fqdn(vm, vm_nics):
222
    public_ip = get_server_public_ip(vm_nics)
223
    if public_ip is None:
224
        return ""
225

  
190 226
    fqdn_setting = settings.CYCLADES_SERVERS_FQDN
191 227
    if fqdn_setting is None:
192
        # Return the first public IPv4 address if exists
193
        address = db_query.get_server_public_ip(server=vm, version=4)
194
        if address is None:
195
            # Else return the first public IPv6 address if exists
196
            address = db_query.get_server_public_ip(server=vm, version=6)
197
        if address is None:
198
            return ""
199
        else:
200
            return address
228
        return public_ip.address
201 229
    elif isinstance(fqdn_setting, basestring):
202 230
        return fqdn_setting % {"id": vm.id}
203 231
    else:
......
206 234
        raise faults.InternalServerError(msg)
207 235

  
208 236

  
209
def get_server_port_forwarding(vm, fqdn):
237
def get_server_port_forwarding(vm, vm_nics, fqdn):
210 238
    """Create API 'port_forwarding' attribute from corresponding setting.
211 239

  
212 240
    Create the 'port_forwarding' API vm attribute based on the corresponding
......
218 246
    * fqdn
219 247
    * owner UUID
220 248

  
249
    NOTE: 'vm_nics' objects have prefetched the ips
221 250
    """
222 251
    port_forwarding = {}
252
    public_ip = get_server_public_ip(vm_nics)
253
    if public_ip is None:
254
        return port_forwarding
223 255
    for dport, to_dest in settings.CYCLADES_PORT_FORWARDING.items():
224 256
        if hasattr(to_dest, "__call__"):
225
            address = db_query.get_server_public_ip(server=vm, version=4)
226
            to_dest = to_dest(address, vm.id, fqdn, vm.userid)
257
            to_dest = to_dest(public_ip.address, vm.id, fqdn, vm.userid)
227 258
        msg = ("Invalid setting: CYCLADES_PORT_FOWARDING."
228 259
               " Value must be a tuple of two elements (host, port).")
229
        if to_dest is None:
230
            continue
231 260
        if not isinstance(to_dest, tuple) or len(to_dest) != 2:
232 261
                raise faults.InternalServerError(msg)
233 262
        else:
......
307 336

  
308 337
    log.debug('list_servers detail=%s', detail)
309 338
    user_vms = VirtualMachine.objects.filter(userid=request.user_uniq)
339
    if detail:
340
        user_vms = user_vms.prefetch_related("nics__ips")
310 341

  
311 342
    user_vms = utils.filter_modified_since(request, objects=user_vms)
312 343

  
......
388 419
    #                       overLimit (413)
389 420

  
390 421
    log.debug('get_server_details %s', server_id)
391
    vm = util.get_vm(server_id, request.user_uniq)
422
    vm = util.get_vm(server_id, request.user_uniq,
423
                     prefetch_related="nics__ips")
392 424
    server = vm_to_dict(vm, detail=True)
393 425
    return render_server(request, server)
394 426

  
......
492 524
    #                       overLimit (413)
493 525

  
494 526
    log.debug('list_addresses %s', server_id)
495
    vm = util.get_vm(server_id, request.user_uniq)
496
    attachments = [nic_to_dict(nic) for nic in vm.nics.filter(state="ACTIVE")]
527
    vm = util.get_vm(server_id, request.user_uniq, prefetch_related="nic__ips")
528
    attachments = [nic_to_attachments(nic)
529
                   for nic in vm.nics.filter(state="ACTIVE")]
497 530
    addresses = attachments_to_addresses(attachments)
498 531

  
499 532
    if request.serialization == 'xml':
......
518 551
    machine = util.get_vm(server_id, request.user_uniq)
519 552
    network = util.get_network(network_id, request.user_uniq)
520 553
    nics = machine.nics.filter(network=network, state="ACTIVE")
521
    addresses = attachments_to_addresses(map(nic_to_dict, nics))
554
    addresses = attachments_to_addresses(map(nic_to_attachments, nics))
522 555

  
523 556
    if request.serialization == 'xml':
524 557
        data = render_to_string('address.xml', {'addresses': addresses})

Also available in: Unified diff