allocate subnets and networks automatically
[plstackapi.git] / planetstack / core / models / network.py
1 import os
2 import socket
3 from django.db import models
4 from core.models import PlCoreBase, Site, Slice, Sliver
5 from django.contrib.contenttypes.models import ContentType
6 from django.contrib.contenttypes import generic
7
8 # Create your models here.
9
10 SUBNET_BASE = "10.0.0.0"
11 SUBNET_NODE_BITS = 12     # enough for 4096 bits per subnet
12 SUBNET_SUBNET_BITS = 12   # enough for 4096 private networks
13
14 def ip_to_int(ip):
15     return int(socket.inet_aton(ip).encode('hex'),16)
16
17 def int_to_ip(i):
18     return socket.inet_ntoa(hex(i)[2:].zfill(8).decode('hex'))
19
20 def find_unused_subnet(base, subnet_bits, node_bits, existing_subnets):
21     # enumerate possible subnets until we find one that isn't used
22     i=1
23     while True:
24         subnet_i = ip_to_int(base) | (i<<node_bits)
25         subnet = int_to_ip(subnet_i) + "/" + str(32-node_bits)
26         if (subnet not in existing_subnets):
27             return subnet
28         i=i+1
29         # TODO: we could run out...
30
31 def find_unused_address(subnet, existingAddresses):
32     # enumerate possible addresses until we find one that isn't used
33     (network, bits) = subnet.split("/")
34     network_i = ip_to_int(network)
35     max_addr = 1<<(32-int(bits))
36     i = 1
37     while True:
38         if i>=max_addr:
39             raise ValueError("No more ips available")
40         ip = int_to_ip(network_i | i)
41         if not (ip in existingAddresses):
42             return ip
43         i=i+1
44
45 class NetworkTemplate(PlCoreBase):
46     VISIBILITY_CHOICES = (('public', 'public'), ('private', 'private'))
47
48     name = models.CharField(max_length=32)
49     guaranteedBandwidth = models.IntegerField(default=0)
50     visibility = models.CharField(max_length=30, choices=VISIBILITY_CHOICES, default="private")
51
52     def __unicode__(self):  return u'%s' % (self.name)
53
54 class Network(PlCoreBase):
55     name = models.CharField(max_length=32)
56     template = models.ForeignKey(NetworkTemplate)
57     subnet = models.CharField(max_length=32, blank=True, null=True)
58     ports = models.CharField(max_length=1024)
59     labels = models.CharField(max_length=1024)
60     slice = models.ForeignKey(Slice, related_name="networks")
61
62     guaranteedBandwidth = models.IntegerField(default=0)
63     permittedSlices = models.ManyToManyField(Slice, blank=True, related_name="permittedNetworks")
64     slivers = models.ManyToManyField(Sliver, blank=True, related_name="boundNetworks", through="NetworkSliver")
65
66     def __unicode__(self):  return u'%s' % (self.name)
67
68     def allocateSubnet(self):
69         existingSubnets = [x.subnet for x in Network.objects.all()]
70         return find_unused_subnet(SUBNET_BASE, SUBNET_SUBNET_BITS, SUBNET_NODE_BITS, existingSubnets)
71
72     def allocateAddress(self):
73         existingAddresses = [x.ip for x in self.networksliver_set.all()]
74         return find_unused_address(self.subnet, existingAddresses)
75
76     def save(self, *args, **kwds):
77         if not self.subnet:
78             self.subnet = self.allocateSubnet()
79         super(Network, self).save(*args, **kwds)
80
81 class NetworkSliver(PlCoreBase):
82     network = models.ForeignKey(Network)
83     sliver = models.ForeignKey(Sliver)
84     ip = models.GenericIPAddressField(help_text="Sliver ip address", blank=True, null=True)
85
86     def save(self, *args, **kwds):
87         if not self.ip:
88             self.ip = self.network.allocateAddress()
89         super(NetworkSliver, self).save(*args, **kwds)
90
91     def __unicode__(self):  return u'foo!'
92
93 class Router(PlCoreBase):
94     name = models.CharField(max_length=32)
95     owner = models.ForeignKey(Slice, related_name="routers")
96     networks = models.ManyToManyField(Network, blank=True, related_name="routers")
97
98     def __unicode__(self):  return u'%s' % (self.name)
99
100 class NetworkParameterType(PlCoreBase):
101     name = models.SlugField(help_text="The name of this tag", max_length=128)
102
103     def __unicode__(self):  return u'%s' % (self.name)
104
105 class NetworkParameter(PlCoreBase):
106     networkParameterType = models.ForeignKey(NetworkParameterType, related_name="parameters", help_text="The name of the parameter")
107     value = models.CharField(help_text="The value of this parameter", max_length=1024)
108
109     # The required fields to do a ObjectType lookup, and object_id assignment
110     content_type = models.ForeignKey(ContentType)
111     object_id = models.PositiveIntegerField()
112     content_object = generic.GenericForeignKey('content_type', 'object_id')
113
114     def __unicode__(self):
115         return self.networkParameterType.name
116
117