Commit bdf75ba2 authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

cyclades: Create IPv6 Addresses from dispatcher

When ports are created from the API they do not have an IPv6 address.
The IPv6 address is only available after the MAC address of the
NIC(port) is known which is only available after the message from the
Ganeti backend has been received. However, dispatcher did not created
correctly the IPv6 addresses. This commit fixes this issue along with
some small code refactoring.
parent 70f2c3d5
......@@ -567,7 +567,8 @@ class Network(models.Model):
def get_subnet(self, version=4):
for subnet in self.subnets.all():
if subnet.ipversion == version:
return subnet.cidr
return subnet
return None
def ip_count(self):
"""Return the total and free IPv4 addresses of the network."""
......
......@@ -37,7 +37,7 @@ from datetime import datetime, timedelta
from synnefo.db.models import (Backend, VirtualMachine, Network,
BackendNetwork, BACKEND_STATUSES,
pooled_rapi_client, VirtualMachineDiagnostic,
Flavor, IPAddressLog)
Flavor, IPAddress, IPAddressLog)
from synnefo.logic import utils, ips
from synnefo import quotas
from synnefo.api.util import release_resource
......@@ -266,22 +266,62 @@ def _process_net_status(vm, etime, nics):
# Update the NIC in DB with the values from Ganeti NIC
setattr(db_nic, f, ganeti_nic[f])
db_nic.save()
# Special case where the IPv4 address has changed, because you
# need to release the old IPv4 address and reserve the new one
ipv4_address = ganeti_nic["ipv4_address"]
if db_nic.ipv4_address != ipv4_address:
remove_nic_ips(db_nic)
if ipv4_address:
network = ganeti_nic["network"]
ipaddress = ips.allocate_ip(network, vm.userid,
address=ipv4_address)
ipaddress.nic = nic
ipaddress.save()
change_address_of_port(db_nic, vm.userid,
old_address=db_nic.ipv4_address,
new_address=ipv4_address,
version=4)
ipv6_address = ganeti_nic["ipv6_address"]
if db_nic.ipv6_address != ipv6_address:
change_address_of_port(db_nic, vm.userid,
old_address=db_nic.ipv6_address,
new_address=ipv6_address,
version=6)
vm.backendtime = etime
vm.save()
def change_address_of_port(port, userid, old_address, new_address, version):
"""Change."""
if old_address is not None:
msg = ("IPv%s Address of server '%s' changed from '%s' to '%s'"
% (version, port.machine_id, old_address, new_address))
log.critical(msg)
# Remove the old IP address
remove_nic_ips(port, version=version)
if version == 4:
ipaddress = ips.allocate_ip(port.network, userid, address=new_address)
ipaddress.nic = port
ipaddress.save()
elif version == 6:
subnet6 = port.network.subnet6
ipaddress = IPAddress.objects.create(userid=userid,
network=port.network,
subnet=subnet6,
nic=port,
address=new_address)
else:
raise ValueError("Unknown version: %s" % version)
# New address log
ip_log = IPAddressLog.objects.create(server_id=port.machine_id,
network_id=port.network_id,
address=new_address,
active=True)
log.info("Created IP log entry '%s' for address '%s' to server '%s'",
ip_log.id, new_address, port.machine_id)
return ipaddress
def nics_are_equal(db_nic, gnt_nic):
for field in NIC_FIELDS:
if getattr(db_nic, field) != gnt_nic[field]:
......@@ -308,7 +348,7 @@ def process_ganeti_nics(ganeti_nics):
mac = gnic.get('mac')
ipv4 = gnic.get('ip')
subnet6 = network.subnet6
ipv6 = mac2eui64(mac, subnet6) if subnet6 else None
ipv6 = mac2eui64(mac, subnet6.cidr) if subnet6 else None
firewall = gnic.get('firewall')
firewall_profile = _reverse_tags.get(firewall)
......@@ -328,30 +368,33 @@ def process_ganeti_nics(ganeti_nics):
return dict(new_nics)
def remove_nic_ips(nic):
def remove_nic_ips(nic, version=None):
"""Remove IP addresses associated with a NetworkInterface.
Remove all IP addresses that are associated with the NetworkInterface
object, by returning them to the pool and deleting the IPAddress object. If
the IP is a floating IP, then it is just disassociated from the NIC.
If version is specified, then only IP addressses of that version will be
removed.
"""
for ip in nic.ips.all():
if version and ip.ipversion != version:
continue
# Update the DB table holding the logging of all IP addresses
update_ip_address_log(nic, ip)
terminate_active_ipaddress_log(nic, ip)
if ip.ipversion == 4:
if ip.floating_ip:
ip.nic = None
ip.save()
else:
ip.release_address()
if not ip.floating_ip:
if ip.floating_ip:
ip.nic = None
ip.save()
else:
# Release the IPv4 address
ip.release_address()
ip.delete()
def update_ip_address_log(nic, ip):
def terminate_active_ipaddress_log(nic, ip):
"""Update DB logging entry for this IP address."""
if not ip.network.public or nic.machine is None:
return
......
......@@ -45,36 +45,18 @@ class NetworkTest(TestCase):
kwargs = {
"name": "test",
"userid": "user",
"subnet": "192.168.20.0/24",
"flavor": "CUSTOM",
}
# wrong gateway
kw = copy(kwargs)
kw["gateway"] = "192.168.3.1"
self.assertRaises(faults.BadRequest, networks.create, **kw)
# wrong subnet
kw = copy(kwargs)
kw["subnet"] = "192.168.2.0"
self.assertRaises(faults.OverLimit, networks.create, **kw)
kw["subnet"] = "192.168.0.0/16"
self.assertRaises(faults.OverLimit, networks.create, **kw)
kw["subnet"] = "192.168.0.3/24"
self.assertRaises(faults.BadRequest, networks.create, **kw)
# wrong flavor
kw = copy(kwargs)
kw["flavor"] = "UNKNOWN"
self.assertRaises(faults.BadRequest, networks.create, **kw)
# Test create objet
kwargs["gateway"] = "192.168.20.1"
kwargs["public"] = True
kwargs["dhcp"] = False
with mocked_quotaholder():
net = networks.create(**kwargs)
self.assertEqual(net.subnet4, "192.168.20.0/24")
self.assertEqual(net.subnets.get(ipversion=4).gateway, "192.168.20.1")
self.assertEqual(net.public, True)
self.assertEqual(net.flavor, "CUSTOM")
self.assertEqual(net.subnets.get(ipversion=4).dhcp, False)
self.assertEqual(net.action, "CREATE")
self.assertEqual(net.state, "ACTIVE")
self.assertEqual(net.name, "test")
......@@ -126,30 +108,3 @@ class NetworkTest(TestCase):
self.assertEqual(net.mac_prefix, settings.DEFAULT_MAC_PREFIX)
self.assertEqual(net.link, settings.DEFAULT_BRIDGE)
self.assertEqual(net.backend_tag, [])
def test_create_network_ipv6(self):
kwargs = {
"name": "test",
"userid": "user",
"flavor": "CUSTOM",
"subnet6": "2001:648:2ffc:1112::/64",
}
# Wrong subnet
kw = copy(kwargs)
kw["subnet6"] = "2001:64q:2ffc:1112::/64"
self.assertRaises(faults.BadRequest, networks.create, **kw)
# Wrong gateway
kw = copy(kwargs)
kw["gateway6"] = "2001:64q:2ffc:1119::1"
self.assertRaises(faults.BadRequest, networks.create, **kw)
# floating_ip_pools cannot be ipv6 only
kw = copy(kwargs)
kw["floating_ip_pool"] = True
self.assertRaises(faults.BadRequest, networks.create, **kw)
kwargs["gateway6"] = "2001:648:2ffc:1112::1"
with mocked_quotaholder():
net = networks.create(**kwargs)
subnet6 = net.subnets.get(ipversion=6)
self.assertEqual(subnet6.cidr, "2001:648:2ffc:1112::/64")
self.assertEqual(subnet6.gateway, "2001:648:2ffc:1112::1")
self.assertEqual(net.get_ip_pools(), [])
......@@ -239,7 +239,7 @@ class NetworkReconciliationTest(TestCase):
"group_list": [["default",
"bridged",
"prv0"]],
"network": net1.subnet4,
"network": net1.subnet4.cidr,
"map": "....",
"external_reservations": ""}]
self.reconciler.reconcile_networks()
......@@ -308,7 +308,7 @@ class NetworkReconciliationTest(TestCase):
operstate="PENDING")
mrapi().GetNetworks.return_value = [{"name": net.backend_id,
"group_list": [],
"network": net.subnet4,
"network": net.subnet4.cidr,
"map": "....",
"external_reservations": ""}]
self.assertEqual(bn.operstate, "PENDING")
......@@ -321,7 +321,7 @@ class NetworkReconciliationTest(TestCase):
deleted=True)
mrapi().GetNetworks.return_value = [{"name": net.backend_id,
"group_list": [],
"network": net.subnet4,
"network": net.subnet4.cidr,
"map": "....",
"external_reservations": ""}]
self.reconciler.reconcile_networks()
......
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