Revision bfb3f9c2

b/snf-cyclades-app/synnefo/db/models.py
567 567
    def get_subnet(self, version=4):
568 568
        for subnet in self.subnets.all():
569 569
            if subnet.ipversion == version:
570
                return subnet.cidr
570
                return subnet
571
        return None
571 572

  
572 573
    def ip_count(self):
573 574
        """Return the total and free IPv4 addresses of the network."""
b/snf-cyclades-app/synnefo/logic/backend.py
37 37
from synnefo.db.models import (Backend, VirtualMachine, Network,
38 38
                               BackendNetwork, BACKEND_STATUSES,
39 39
                               pooled_rapi_client, VirtualMachineDiagnostic,
40
                               Flavor, IPAddressLog)
40
                               Flavor, IPAddress, IPAddressLog)
41 41
from synnefo.logic import utils, ips
42 42
from synnefo import quotas
43 43
from synnefo.api.util import release_resource
......
266 266
                # Update the NIC in DB with the values from Ganeti NIC
267 267
                setattr(db_nic, f, ganeti_nic[f])
268 268
                db_nic.save()
269

  
269 270
            # Special case where the IPv4 address has changed, because you
270 271
            # need to release the old IPv4 address and reserve the new one
271 272
            ipv4_address = ganeti_nic["ipv4_address"]
272 273
            if db_nic.ipv4_address != ipv4_address:
273
                remove_nic_ips(db_nic)
274
                if ipv4_address:
275
                    network = ganeti_nic["network"]
276
                    ipaddress = ips.allocate_ip(network, vm.userid,
277
                                                address=ipv4_address)
278
                    ipaddress.nic = nic
279
                    ipaddress.save()
274
                change_address_of_port(db_nic, vm.userid,
275
                                       old_address=db_nic.ipv4_address,
276
                                       new_address=ipv4_address,
277
                                       version=4)
278

  
279
            ipv6_address = ganeti_nic["ipv6_address"]
280
            if db_nic.ipv6_address != ipv6_address:
281
                change_address_of_port(db_nic, vm.userid,
282
                                       old_address=db_nic.ipv6_address,
283
                                       new_address=ipv6_address,
284
                                       version=6)
280 285

  
281 286
    vm.backendtime = etime
282 287
    vm.save()
283 288

  
284 289

  
290
def change_address_of_port(port, userid, old_address, new_address, version):
291
    """Change."""
292
    if old_address is not None:
293
        msg = ("IPv%s Address of server '%s' changed from '%s' to '%s'"
294
               % (version, port.machine_id, old_address, new_address))
295
        log.critical(msg)
296

  
297
    # Remove the old IP address
298
    remove_nic_ips(port, version=version)
299

  
300
    if version == 4:
301
        ipaddress = ips.allocate_ip(port.network, userid, address=new_address)
302
        ipaddress.nic = port
303
        ipaddress.save()
304
    elif version == 6:
305
        subnet6 = port.network.subnet6
306
        ipaddress = IPAddress.objects.create(userid=userid,
307
                                             network=port.network,
308
                                             subnet=subnet6,
309
                                             nic=port,
310
                                             address=new_address)
311
    else:
312
        raise ValueError("Unknown version: %s" % version)
313

  
314
    # New address log
315
    ip_log = IPAddressLog.objects.create(server_id=port.machine_id,
316
                                         network_id=port.network_id,
317
                                         address=new_address,
318
                                         active=True)
319
    log.info("Created IP log entry '%s' for address '%s' to server '%s'",
320
             ip_log.id, new_address, port.machine_id)
321

  
322
    return ipaddress
323

  
324

  
285 325
def nics_are_equal(db_nic, gnt_nic):
286 326
    for field in NIC_FIELDS:
287 327
        if getattr(db_nic, field) != gnt_nic[field]:
......
308 348
        mac = gnic.get('mac')
309 349
        ipv4 = gnic.get('ip')
310 350
        subnet6 = network.subnet6
311
        ipv6 = mac2eui64(mac, subnet6) if subnet6 else None
351
        ipv6 = mac2eui64(mac, subnet6.cidr) if subnet6 else None
312 352

  
313 353
        firewall = gnic.get('firewall')
314 354
        firewall_profile = _reverse_tags.get(firewall)
......
328 368
    return dict(new_nics)
329 369

  
330 370

  
331
def remove_nic_ips(nic):
371
def remove_nic_ips(nic, version=None):
332 372
    """Remove IP addresses associated with a NetworkInterface.
333 373

  
334 374
    Remove all IP addresses that are associated with the NetworkInterface
335 375
    object, by returning them to the pool and deleting the IPAddress object. If
336 376
    the IP is a floating IP, then it is just disassociated from the NIC.
377
    If version is specified, then only IP addressses of that version will be
378
    removed.
337 379

  
338 380
    """
339

  
340 381
    for ip in nic.ips.all():
382
        if version and ip.ipversion != version:
383
            continue
384

  
341 385
        # Update the DB table holding the logging of all IP addresses
342
        update_ip_address_log(nic, ip)
386
        terminate_active_ipaddress_log(nic, ip)
343 387

  
344
        if ip.ipversion == 4:
345
            if ip.floating_ip:
346
                ip.nic = None
347
                ip.save()
348
            else:
349
                ip.release_address()
350
        if not ip.floating_ip:
388
        if ip.floating_ip:
389
            ip.nic = None
390
            ip.save()
391
        else:
392
            # Release the IPv4 address
393
            ip.release_address()
351 394
            ip.delete()
352 395

  
353 396

  
354
def update_ip_address_log(nic, ip):
397
def terminate_active_ipaddress_log(nic, ip):
355 398
    """Update DB logging entry for this IP address."""
356 399
    if not ip.network.public or nic.machine is None:
357 400
        return
b/snf-cyclades-app/synnefo/logic/tests/networks.py
45 45
        kwargs = {
46 46
            "name": "test",
47 47
            "userid": "user",
48
            "subnet": "192.168.20.0/24",
49 48
            "flavor": "CUSTOM",
50 49
        }
51
        # wrong gateway
52
        kw = copy(kwargs)
53
        kw["gateway"] = "192.168.3.1"
54
        self.assertRaises(faults.BadRequest, networks.create, **kw)
55
        # wrong subnet
56
        kw = copy(kwargs)
57
        kw["subnet"] = "192.168.2.0"
58
        self.assertRaises(faults.OverLimit, networks.create, **kw)
59
        kw["subnet"] = "192.168.0.0/16"
60
        self.assertRaises(faults.OverLimit, networks.create, **kw)
61
        kw["subnet"] = "192.168.0.3/24"
62
        self.assertRaises(faults.BadRequest, networks.create, **kw)
63 50
        # wrong flavor
64 51
        kw = copy(kwargs)
65 52
        kw["flavor"] = "UNKNOWN"
66 53
        self.assertRaises(faults.BadRequest, networks.create, **kw)
67 54
        # Test create objet
68
        kwargs["gateway"] = "192.168.20.1"
69 55
        kwargs["public"] = True
70
        kwargs["dhcp"] = False
71 56
        with mocked_quotaholder():
72 57
            net = networks.create(**kwargs)
73
        self.assertEqual(net.subnet4, "192.168.20.0/24")
74
        self.assertEqual(net.subnets.get(ipversion=4).gateway, "192.168.20.1")
75 58
        self.assertEqual(net.public, True)
76 59
        self.assertEqual(net.flavor, "CUSTOM")
77
        self.assertEqual(net.subnets.get(ipversion=4).dhcp, False)
78 60
        self.assertEqual(net.action, "CREATE")
79 61
        self.assertEqual(net.state, "ACTIVE")
80 62
        self.assertEqual(net.name, "test")
......
126 108
        self.assertEqual(net.mac_prefix, settings.DEFAULT_MAC_PREFIX)
127 109
        self.assertEqual(net.link, settings.DEFAULT_BRIDGE)
128 110
        self.assertEqual(net.backend_tag, [])
129

  
130
    def test_create_network_ipv6(self):
131
        kwargs = {
132
            "name": "test",
133
            "userid": "user",
134
            "flavor": "CUSTOM",
135
            "subnet6": "2001:648:2ffc:1112::/64",
136
        }
137
        # Wrong subnet
138
        kw = copy(kwargs)
139
        kw["subnet6"] = "2001:64q:2ffc:1112::/64"
140
        self.assertRaises(faults.BadRequest, networks.create, **kw)
141
        # Wrong gateway
142
        kw = copy(kwargs)
143
        kw["gateway6"] = "2001:64q:2ffc:1119::1"
144
        self.assertRaises(faults.BadRequest, networks.create, **kw)
145
        # floating_ip_pools cannot be ipv6 only
146
        kw = copy(kwargs)
147
        kw["floating_ip_pool"] = True
148
        self.assertRaises(faults.BadRequest, networks.create, **kw)
149
        kwargs["gateway6"] = "2001:648:2ffc:1112::1"
150
        with mocked_quotaholder():
151
            net = networks.create(**kwargs)
152
        subnet6 = net.subnets.get(ipversion=6)
153
        self.assertEqual(subnet6.cidr, "2001:648:2ffc:1112::/64")
154
        self.assertEqual(subnet6.gateway, "2001:648:2ffc:1112::1")
155
        self.assertEqual(net.get_ip_pools(), [])
b/snf-cyclades-app/synnefo/logic/tests/reconciliation.py
239 239
                                             "group_list": [["default",
240 240
                                                             "bridged",
241 241
                                                             "prv0"]],
242
                                             "network": net1.subnet4,
242
                                             "network": net1.subnet4.cidr,
243 243
                                             "map": "....",
244 244
                                             "external_reservations": ""}]
245 245
        self.reconciler.reconcile_networks()
......
308 308
                                            operstate="PENDING")
309 309
        mrapi().GetNetworks.return_value = [{"name": net.backend_id,
310 310
                                             "group_list": [],
311
                                             "network": net.subnet4,
311
                                             "network": net.subnet4.cidr,
312 312
                                             "map": "....",
313 313
                                             "external_reservations": ""}]
314 314
        self.assertEqual(bn.operstate, "PENDING")
......
321 321
                                                deleted=True)
322 322
        mrapi().GetNetworks.return_value = [{"name": net.backend_id,
323 323
                                             "group_list": [],
324
                                             "network": net.subnet4,
324
                                             "network": net.subnet4.cidr,
325 325
                                             "map": "....",
326 326
                                             "external_reservations": ""}]
327 327
        self.reconciler.reconcile_networks()

Also available in: Unified diff