blob: b51ff2760ccd02dce4f21f1dbf5dd78d5ceddea0 [file] [log] [blame]
Scott Baker58a9c7a2013-07-29 15:43:07 -07001import os
2import socket
Scott Baker723fd252015-01-06 15:11:29 -08003import sys
Scott Bakera6a5b292016-02-12 18:05:34 -08004from django.db import models, transaction
Scott Baker09a3f642016-04-12 16:29:40 -07005from core.models import PlCoreBase, Site, Slice, Instance, Controller, Service
Tony Mack51c4a7d2014-11-30 15:33:35 -05006from core.models import ControllerLinkManager,ControllerLinkDeletionManager
Scott Baker58a9c7a2013-07-29 15:43:07 -07007from django.contrib.contenttypes.models import ContentType
8from django.contrib.contenttypes import generic
Scott Bakera289ed72014-10-17 16:22:20 -07009from django.core.exceptions import ValidationError
Scott Baker34d16d42015-10-07 22:50:21 -070010from django.db.models import Q
Scott Baker58a9c7a2013-07-29 15:43:07 -070011
12# If true, then IP addresses will be allocated by the model. If false, then
13# we will assume the observer handles it.
Scott Baker026bfe72013-07-29 16:03:50 -070014NO_OBSERVER=False
Scott Baker58a9c7a2013-07-29 15:43:07 -070015
Scott Bakera289ed72014-10-17 16:22:20 -070016def ParseNatList(ports):
17 """ Support a list of ports in the format "protocol:port, protocol:port, ..."
18 examples:
19 tcp 123
20 tcp 123:133
21 tcp 123, tcp 124, tcp 125, udp 201, udp 202
22
23 User can put either a "/" or a " " between protocol and ports
24 Port ranges can be specified with "-" or ":"
25 """
26 nats = []
27 if ports:
28 parts = ports.split(",")
29 for part in parts:
30 part = part.strip()
31 if "/" in part:
32 (protocol, ports) = part.split("/",1)
33 elif " " in part:
34 (protocol, ports) = part.split(None,1)
35 else:
36 raise TypeError('malformed port specifier %s, format example: "tcp 123, tcp 201:206, udp 333"' % part)
37
38 protocol = protocol.strip()
39 ports = ports.strip()
40
41 if not (protocol in ["udp", "tcp"]):
42 raise ValueError('unknown protocol %s' % protocol)
43
44 if "-" in ports:
45 (first, last) = ports.split("-")
46 first = int(first.strip())
47 last = int(last.strip())
48 portStr = "%d:%d" % (first, last)
49 elif ":" in ports:
50 (first, last) = ports.split(":")
51 first = int(first.strip())
52 last = int(last.strip())
53 portStr = "%d:%d" % (first, last)
54 else:
55 portStr = "%d" % int(ports)
56
57 nats.append( {"l4_protocol": protocol, "l4_port": portStr} )
58
59 return nats
60
61def ValidateNatList(ports):
62 try:
63 ParseNatList(ports)
64 except Exception,e:
65 raise ValidationError(str(e))
66
Scott Bakerd0f74ec2015-11-16 15:07:43 -080067class ParameterMixin(object):
68 # helper classes for dealing with NetworkParameter
69
70 def get_parameters(self):
71 parameter_dict = {}
72
73 instance_type = ContentType.objects.get_for_model(self)
74 for param in NetworkParameter.objects.filter(content_type__pk=instance_type.id, object_id=self.id):
75 parameter_dict[param.parameter.name] = param.value
76
77 return parameter_dict
78
79 def set_parameter(self, name, value):
80 instance_type = ContentType.objects.get_for_model(self)
81 existing_params = NetworkParameter.objects.filter(parameter__name=name, content_type__pk=instance_type.id, object_id=self.id)
82 if existing_params:
83 p=existing_params[0]
84 p.value = value
85 p.save()
86 else:
87 pt = NetworkParameterType.objects.get(name=name)
88 p = NetworkParameter(parameter=pt, content_type=instance_type, object_id=self.id, value=value)
89 p.save()
90
91 def unset_parameter(self, name):
92 instance_type = ContentType.objects.get_for_model(self)
93 existing_params = NetworkParameter.objects.filter(parameter__name=name, content_type__pk=instance_type.id, object_id=self.id)
94 for p in existing_params:
95 p.delete()
96
97
98class NetworkTemplate(PlCoreBase, ParameterMixin):
Scott Baker58a9c7a2013-07-29 15:43:07 -070099 VISIBILITY_CHOICES = (('public', 'public'), ('private', 'private'))
Scott Baker87e5e092013-08-07 18:58:10 -0700100 TRANSLATION_CHOICES = (('none', 'none'), ('NAT', 'NAT'))
Scott Baker59078f82014-11-17 16:03:49 -0800101 TOPOLOGY_CHOICES = (('bigswitch', 'BigSwitch'), ('physical', 'Physical'), ('custom', 'Custom'))
102 CONTROLLER_CHOICES = ((None, 'None'), ('onos', 'ONOS'), ('custom', 'Custom'))
Scott Bakera36a9902015-12-09 15:44:55 -0800103 ACCESS_CHOICES = ((None, 'None'), ('indirect', 'Indirect'), ('direct', 'Direct'))
Scott Baker58a9c7a2013-07-29 15:43:07 -0700104
105 name = models.CharField(max_length=32)
106 description = models.CharField(max_length=1024, blank=True, null=True)
Scott Baker369f9b92015-01-03 12:03:38 -0800107 guaranteed_bandwidth = models.IntegerField(default=0)
Scott Baker58a9c7a2013-07-29 15:43:07 -0700108 visibility = models.CharField(max_length=30, choices=VISIBILITY_CHOICES, default="private")
Scott Baker87e5e092013-08-07 18:58:10 -0700109 translation = models.CharField(max_length=30, choices=TRANSLATION_CHOICES, default="none")
Scott Bakera36a9902015-12-09 15:44:55 -0800110 access = models.CharField(max_length=30, null=True, blank=True, choices=ACCESS_CHOICES, help_text="Advertise this network as a means for other slices to contact this slice")
Scott Baker369f9b92015-01-03 12:03:38 -0800111 shared_network_name = models.CharField(max_length=30, blank=True, null=True)
112 shared_network_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum network")
Scott Bakere60c73b2015-12-09 16:31:26 -0800113 topology_kind = models.CharField(null=False, blank=False, max_length=30, choices=TOPOLOGY_CHOICES, default="bigswitch")
Scott Baker369f9b92015-01-03 12:03:38 -0800114 controller_kind = models.CharField(null=True, blank=True, max_length=30, choices=CONTROLLER_CHOICES, default=None)
Scott Baker58a9c7a2013-07-29 15:43:07 -0700115
Scott Baker201f7da2014-12-23 10:56:06 -0800116 def __init__(self, *args, **kwargs):
117 super(NetworkTemplate, self).__init__(*args, **kwargs)
118
119 # somehow these got set wrong inside of the live database. Remove this
120 # code after all is well...
Scott Baker369f9b92015-01-03 12:03:38 -0800121 if (self.topology_kind=="BigSwitch"):
Scott Baker723fd252015-01-06 15:11:29 -0800122 print >> sys.stderr, "XXX warning: topology_kind invalid case"
Scott Baker369f9b92015-01-03 12:03:38 -0800123 self.topology_kind="bigswitch"
124 elif (self.topology_kind=="Physical"):
Scott Baker723fd252015-01-06 15:11:29 -0800125 print >> sys.stderr, "XXX warning: topology_kind invalid case"
Scott Baker369f9b92015-01-03 12:03:38 -0800126 self.topology_kind="physical"
127 elif (self.topology_kind=="Custom"):
Scott Baker723fd252015-01-06 15:11:29 -0800128 print >> sys.stderr, "XXX warning: topology_kind invalid case"
Scott Baker369f9b92015-01-03 12:03:38 -0800129 self.toplogy_kind="custom"
Scott Baker201f7da2014-12-23 10:56:06 -0800130
Scott Bakere60c73b2015-12-09 16:31:26 -0800131 def save(self, *args, **kwargs):
132 self.enforce_choices(self.access, self.ACCESS_CHOICES)
133 super(NetworkTemplate, self).save(*args, **kwargs)
134
Scott Baker58a9c7a2013-07-29 15:43:07 -0700135 def __unicode__(self): return u'%s' % (self.name)
136
Scott Bakerd0f74ec2015-11-16 15:07:43 -0800137class Network(PlCoreBase, ParameterMixin):
Scott Baker58a9c7a2013-07-29 15:43:07 -0700138 name = models.CharField(max_length=32)
139 template = models.ForeignKey(NetworkTemplate)
140 subnet = models.CharField(max_length=32, blank=True)
Scott Bakera289ed72014-10-17 16:22:20 -0700141 ports = models.CharField(max_length=1024, blank=True, null=True, validators=[ValidateNatList])
Scott Baker58a9c7a2013-07-29 15:43:07 -0700142 labels = models.CharField(max_length=1024, blank=True, null=True)
Siobhan Tullyce652d02013-10-08 21:52:35 -0400143 owner = models.ForeignKey(Slice, related_name="ownedNetworks", help_text="Slice that owns control of this Network")
Scott Baker58a9c7a2013-07-29 15:43:07 -0700144
Scott Baker549aa252015-01-03 12:29:29 -0800145 guaranteed_bandwidth = models.IntegerField(default=0)
146 permit_all_slices = models.BooleanField(default=False)
147 permitted_slices = models.ManyToManyField(Slice, blank=True, related_name="availableNetworks")
Scott Baker87191e72013-08-06 08:55:07 -0700148 slices = models.ManyToManyField(Slice, blank=True, related_name="networks", through="NetworkSlice")
Scott Bakerc6822922015-09-14 11:41:05 -0700149 instances = models.ManyToManyField(Instance, blank=True, related_name="networks", through="Port")
Scott Baker58a9c7a2013-07-29 15:43:07 -0700150
Scott Baker549aa252015-01-03 12:29:29 -0800151 topology_parameters = models.TextField(null=True, blank=True)
152 controller_url = models.CharField(null=True, blank=True, max_length=1024)
153 controller_parameters = models.TextField(null=True, blank=True)
Scott Baker59078f82014-11-17 16:03:49 -0800154
Scott Baker87191e72013-08-06 08:55:07 -0700155 # for observer/manager
156 network_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum network")
157 router_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum router id")
158 subnet_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum subnet id")
159
Scott Baker3789cb22015-08-21 16:40:53 -0700160 autoconnect = models.BooleanField(default=True, help_text="This network can be autoconnected to the slice that owns it")
161
Scott Baker58a9c7a2013-07-29 15:43:07 -0700162 def __unicode__(self): return u'%s' % (self.name)
163
164 def save(self, *args, **kwds):
165 if (not self.subnet) and (NO_OBSERVER):
166 from util.network_subnet_allocator import find_unused_subnet
167 self.subnet = find_unused_subnet(existing_subnets=[x.subnet for x in Network.objects.all()])
168 super(Network, self).save(*args, **kwds)
169
Tony Mack5b061472014-02-04 07:57:10 -0500170 def can_update(self, user):
Tony Mack3428e6e2015-02-08 21:38:41 -0500171 return user.can_update_slice(self.owner)
Tony Mack5b061472014-02-04 07:57:10 -0500172
Scott Baker5bbaa232014-08-14 17:23:15 -0700173 @property
174 def nat_list(self):
Scott Bakera289ed72014-10-17 16:22:20 -0700175 return ParseNatList(self.ports)
Scott Baker5bbaa232014-08-14 17:23:15 -0700176
Tony Mack5b061472014-02-04 07:57:10 -0500177 @staticmethod
178 def select_by_user(user):
179 if user.is_admin:
180 qs = Network.objects.all()
181 else:
Tony Mack5efa1332014-04-02 15:45:48 -0400182 slices = Slice.select_by_user(user)
183 #slice_ids = [s.id for s in Slice.select_by_user(user)]
184 qs = Network.objects.filter(owner__in=slices)
Tony Mack5b061472014-02-04 07:57:10 -0500185 return qs
186
Scott Bakerd0f74ec2015-11-16 15:07:43 -0800187 def get_parameters(self):
188 # returns parameters from the template, updated by self.
189 p={}
190 if self.template:
191 p = self.template.get_parameters()
192 p.update(ParameterMixin.get_parameters(self))
193 return p
194
Tony Mack3066a952015-01-05 22:48:11 -0500195class ControllerNetwork(PlCoreBase):
Tony Mack51c4a7d2014-11-30 15:33:35 -0500196 objects = ControllerLinkManager()
197 deleted_objects = ControllerLinkDeletionManager()
Sapan Bhatiaed7b83b2014-09-22 14:52:59 -0400198
Tony Mack51c4a7d2014-11-30 15:33:35 -0500199 # Stores the openstack ids at various controllers
200 network = models.ForeignKey(Network, related_name='controllernetworks')
201 controller = models.ForeignKey(Controller, related_name='controllernetworks')
Tony Mack457c84c2014-04-08 16:37:56 -0400202 net_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum network")
Tony Macke9b08692014-04-07 19:38:28 -0400203 router_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum router id")
Scott Baker95d81c72014-08-12 18:29:27 -0700204 subnet_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum subnet id")
205 subnet = models.CharField(max_length=32, blank=True)
Scott Baker39278862016-05-18 08:56:50 -0700206 segmentation_id = models.IntegerField(null=True, blank=True)
Scott Bakere4ea8952015-10-26 15:12:13 -0700207
Tony Mack5d93a9e2015-04-11 12:17:59 -0400208 class Meta:
209 unique_together = ('network', 'controller')
Scott Bakerd0f74ec2015-11-16 15:07:43 -0800210
Sapan Bhatiad0e8ed82016-04-06 19:30:24 +0200211 def tologdict(self):
212 d=super(ControllerNetwork,self).tologdict()
213 try:
214 d['network_name']=self.network.name
215 d['controller_name']=self.controller.name
216 except:
217 pass
218 return d
219
Tony Macke9b08692014-04-07 19:38:28 -0400220 @staticmethod
221 def select_by_user(user):
222 if user.is_admin:
Scott Baker7d85a032015-02-23 17:22:33 -0800223 qs = ControllerNetwork.objects.all()
Tony Macke9b08692014-04-07 19:38:28 -0400224 else:
225 slices = Slice.select_by_user(user)
226 networks = Network.objects.filter(owner__in=slices)
Scott Baker7d85a032015-02-23 17:22:33 -0800227 qs = ControllerNetwork.objects.filter(network__in=networks)
Scott Baker95d81c72014-08-12 18:29:27 -0700228 return qs
Tony Macke9b08692014-04-07 19:38:28 -0400229
Scott Baker87191e72013-08-06 08:55:07 -0700230class NetworkSlice(PlCoreBase):
231 # This object exists solely so we can implement the permission check when
232 # adding slices to networks. It adds no additional fields to the relation.
233
Sapan Bhatia6bfa2ca2014-11-11 21:47:45 -0500234 network = models.ForeignKey(Network,related_name='networkslices')
235 slice = models.ForeignKey(Slice,related_name='networkslices')
Scott Baker87191e72013-08-06 08:55:07 -0700236
Tony Mack5d93a9e2015-04-11 12:17:59 -0400237 class Meta:
238 unique_together = ('network', 'slice')
Tony Mackc8836df2015-03-09 17:13:14 -0400239
Scott Baker87191e72013-08-06 08:55:07 -0700240 def save(self, *args, **kwds):
241 slice = self.slice
Scott Bakerb3c363e2015-04-13 17:23:28 -0700242 if (slice not in self.network.permitted_slices.all()) and (slice != self.network.owner) and (not self.network.permit_all_slices):
Tony Mackd8515472015-08-19 11:58:18 -0400243 # to add a instance to the network, then one of the following must be true:
244 # 1) instance's slice is in network's permittedSlices list,
245 # 2) instance's slice is network's owner, or
Scott Baker87191e72013-08-06 08:55:07 -0700246 # 3) network's permitAllSlices is true
247 raise ValueError("Slice %s is not allowed to connect to network %s" % (str(slice), str(self.network)))
248
249 super(NetworkSlice, self).save(*args, **kwds)
250
251 def __unicode__(self): return u'%s-%s' % (self.network.name, self.slice.name)
252
Tony Mack5b061472014-02-04 07:57:10 -0500253 def can_update(self, user):
Tony Mack3428e6e2015-02-08 21:38:41 -0500254 return user.can_update_slice(self.slice)
Tony Mack5b061472014-02-04 07:57:10 -0500255
Tony Mack5b061472014-02-04 07:57:10 -0500256 @staticmethod
257 def select_by_user(user):
258 if user.is_admin:
259 qs = NetworkSlice.objects.all()
260 else:
261 slice_ids = [s.id for s in Slice.select_by_user(user)]
Scott Baker34d16d42015-10-07 22:50:21 -0700262 network_ids = [network.id for network in Network.select_by_user(user)]
263 qs = NetworkSlice.objects.filter(Q(slice__in=slice_ids) | Q(network__in=network_ids))
Tony Mack5b061472014-02-04 07:57:10 -0500264 return qs
265
Scott Bakerd0f74ec2015-11-16 15:07:43 -0800266class Port(PlCoreBase, ParameterMixin):
Scott Bakerc6822922015-09-14 11:41:05 -0700267 network = models.ForeignKey(Network,related_name='links')
268 instance = models.ForeignKey(Instance, null=True, blank=True, related_name='ports')
Tony Mackd8515472015-08-19 11:58:18 -0400269 ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
Scott Bakere50115b2015-11-16 20:15:41 -0800270 port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Neutron port id")
Scott Bakere553b472015-09-08 18:22:15 -0700271 mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
Scott Bakerc5dfb5f2016-02-16 11:59:07 -0800272 xos_created = models.BooleanField(default=False) # True if XOS created this port in Neutron, False if port created by Neutron and observed by XOS
Scott Baker58a9c7a2013-07-29 15:43:07 -0700273
Tony Mack5d93a9e2015-04-11 12:17:59 -0400274 class Meta:
Tony Mackd8515472015-08-19 11:58:18 -0400275 unique_together = ('network', 'instance')
Tony Mackc8836df2015-03-09 17:13:14 -0400276
Scott Baker58a9c7a2013-07-29 15:43:07 -0700277 def save(self, *args, **kwds):
Tony Mackd8515472015-08-19 11:58:18 -0400278 if self.instance:
279 slice = self.instance.slice
Scott Bakera92af1f2015-08-18 17:04:01 -0700280 if (slice not in self.network.permitted_slices.all()) and (slice != self.network.owner) and (not self.network.permit_all_slices):
Tony Mackd8515472015-08-19 11:58:18 -0400281 # to add a instance to the network, then one of the following must be true:
282 # 1) instance's slice is in network's permittedSlices list,
283 # 2) instance's slice is network's owner, or
Scott Bakera92af1f2015-08-18 17:04:01 -0700284 # 3) network's permitAllSlices is true
285 raise ValueError("Slice %s is not allowed to connect to network %s" % (str(slice), str(self.network)))
286
Scott Bakerc6822922015-09-14 11:41:05 -0700287 super(Port, self).save(*args, **kwds)
Scott Baker58a9c7a2013-07-29 15:43:07 -0700288
Scott Bakera92af1f2015-08-18 17:04:01 -0700289 def __unicode__(self):
Tony Mackd8515472015-08-19 11:58:18 -0400290 if self.instance:
291 return u'%s-%s' % (self.network.name, self.instance.instance_name)
Scott Bakera92af1f2015-08-18 17:04:01 -0700292 else:
Scott Bakerd0ca5972015-08-25 23:24:36 -0700293 return u'%s-unboundport-%s' % (self.network.name, self.id)
Scott Baker58a9c7a2013-07-29 15:43:07 -0700294
Tony Mack5b061472014-02-04 07:57:10 -0500295 def can_update(self, user):
Tony Mackd8515472015-08-19 11:58:18 -0400296 if self.instance:
297 return user.can_update_slice(self.instance.slice)
Scott Bakera92af1f2015-08-18 17:04:01 -0700298 if self.network:
299 return user.can_update_slice(self.network.owner)
300 return False
Tony Mack5b061472014-02-04 07:57:10 -0500301
Tony Mack5b061472014-02-04 07:57:10 -0500302 @staticmethod
303 def select_by_user(user):
304 if user.is_admin:
Scott Bakerc6822922015-09-14 11:41:05 -0700305 qs = Port.objects.all()
Tony Mack5b061472014-02-04 07:57:10 -0500306 else:
Scott Baker34d16d42015-10-07 22:50:21 -0700307 instances = Instance.select_by_user(user)
308 instance_ids = [instance.id for instance in instances]
309 networks = Network.select_by_user(user)
310 network_ids = [network.id for network in networks]
311 qs = Port.objects.filter(Q(instance__in=instance_ids) | Q(network__in=network_ids))
Tony Mack5b061472014-02-04 07:57:10 -0500312 return qs
313
Scott Bakerd0f74ec2015-11-16 15:07:43 -0800314 def get_parameters(self):
315 # returns parameters from the network, updated by self.
316 p={}
317 if self.network:
318 p = self.network.get_parameters()
319 p.update(ParameterMixin.get_parameters(self))
320 return p
321
Scott Baker58a9c7a2013-07-29 15:43:07 -0700322class Router(PlCoreBase):
323 name = models.CharField(max_length=32)
324 owner = models.ForeignKey(Slice, related_name="routers")
325 permittedNetworks = models.ManyToManyField(Network, blank=True, related_name="availableRouters")
326 networks = models.ManyToManyField(Network, blank=True, related_name="routers")
327
328 def __unicode__(self): return u'%s' % (self.name)
329
Tony Mack3428e6e2015-02-08 21:38:41 -0500330 def can_update(self, user):
331 return user.can_update_slice(self.owner)
332
Scott Baker58a9c7a2013-07-29 15:43:07 -0700333class NetworkParameterType(PlCoreBase):
334 name = models.SlugField(help_text="The name of this parameter", max_length=128)
335 description = models.CharField(max_length=1024)
336
337 def __unicode__(self): return u'%s' % (self.name)
338
339class NetworkParameter(PlCoreBase):
Sapan Bhatia6bfa2ca2014-11-11 21:47:45 -0500340 parameter = models.ForeignKey(NetworkParameterType, related_name="networkparameters", help_text="The type of the parameter")
Scott Baker58a9c7a2013-07-29 15:43:07 -0700341 value = models.CharField(help_text="The value of this parameter", max_length=1024)
342
343 # The required fields to do a ObjectType lookup, and object_id assignment
344 content_type = models.ForeignKey(ContentType)
345 object_id = models.PositiveIntegerField()
346 content_object = generic.GenericForeignKey('content_type', 'object_id')
347
348 def __unicode__(self):
349 return self.parameter.name
350
Scott Baker6167c922016-02-12 17:31:04 -0800351class AddressPool(PlCoreBase):
352 name = models.CharField(max_length=32)
353 addresses = models.TextField(blank=True, null=True)
Scott Baker09a3f642016-04-12 16:29:40 -0700354 gateway_ip = models.CharField(max_length=32, null=True)
355 gateway_mac = models.CharField(max_length=32, null=True)
Scott Bakerfca52df2016-04-13 16:40:50 -0700356 cidr = models.CharField(max_length=32, null=True)
Scott Baker2e608602016-02-12 17:41:52 -0800357 inuse = models.TextField(blank=True, null=True)
Scott Baker09a3f642016-04-12 16:29:40 -0700358 service = models.ForeignKey(Service, related_name="addresspools", null=True, blank=True)
Scott Baker6167c922016-02-12 17:31:04 -0800359
360 def __unicode__(self): return u'%s' % (self.name)
361
362 def get_address(self):
363 with transaction.atomic():
364 ap = AddressPool.objects.get(pk=self.pk)
365 if ap.addresses:
Scott Baker272949f2016-02-18 12:21:09 -0800366 avail_ips = ap.addresses.split()
Scott Baker6167c922016-02-12 17:31:04 -0800367 else:
Scott Baker272949f2016-02-18 12:21:09 -0800368 avail_ips = []
369
370 if ap.inuse:
371 inuse_ips = ap.inuse.split()
372 else:
373 inuse_ips = []
374
375 while avail_ips:
376 addr = avail_ips.pop(0)
377
378 if addr in inuse_ips:
379 # This may have happened if someone re-ran the tosca
380 # recipe and 'refilled' the AddressPool while some addresses
381 # were still in use.
382 continue
383
384 inuse_ips.insert(0,addr)
385
386 ap.inuse = " ".join(inuse_ips)
387 ap.addresses = " ".join(avail_ips)
388 ap.save()
389 return addr
390
391 addr = None
Scott Baker6167c922016-02-12 17:31:04 -0800392 return addr
393
394 def put_address(self, addr):
395 with transaction.atomic():
396 ap = AddressPool.objects.get(pk=self.pk)
Scott Bakera6a5b292016-02-12 18:05:34 -0800397 addresses = ap.addresses or ""
398 parts = addresses.split()
Scott Baker6167c922016-02-12 17:31:04 -0800399 if addr not in parts:
Scott Bakera6a5b292016-02-12 18:05:34 -0800400 parts.insert(0,addr)
Scott Baker6167c922016-02-12 17:31:04 -0800401 ap.addresses = " ".join(parts)
Scott Bakera6a5b292016-02-12 18:05:34 -0800402
403 inuse = ap.inuse or ""
404 parts = inuse.split()
405 if addr in parts:
406 parts.remove(addr)
407 ap.inuse = " ".join(parts)
408
409 ap.save()
410
Scott Baker58a9c7a2013-07-29 15:43:07 -0700411