Allow for "pod specific" vlan allocation for LFEDGE allocation case 39/72939/2
authorSawyer Bergeron <sbergeron@iol.unh.edu>
Sat, 25 Sep 2021 20:18:12 +0000 (16:18 -0400)
committerSawyer Bergeron <sbergeron@iol.unh.edu>
Sat, 25 Sep 2021 20:29:13 +0000 (16:29 -0400)
Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu>
Change-Id: I8b75410145027f43eaf6de7bd5f1813af38d3e7f
Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu>
src/account/models.py
src/resource_inventory/migrations/0022_auto_20210925_2028.py [new file with mode: 0644]
src/resource_inventory/models.py
src/resource_inventory/resource_manager.py

index b71f0ac..2501be6 100644 (file)
@@ -82,12 +82,14 @@ class VlanManager(models.Model):
     # if they use QinQ or a vxlan overlay, for example
     allow_overlapping = models.BooleanField()
 
-    def get_vlans(self, count=1):
+    def get_vlans(self, count=1, within=None):
         """
         Return the IDs of available vlans as a list[int], but does not reserve them.
 
         Will throw index exception if not enough vlans are available.
         Always returns a list of ints
+
+        If `within` is not none, will filter against that as a set, requiring that any vlans returned are within that set
         """
         allocated = []
         vlans = json.loads(self.vlans)
@@ -104,17 +106,28 @@ class VlanManager(models.Model):
                 continue
 
             # vlan is available and not reserved, so safe to add
-            allocated.append(i)
+            if within is not None:
+                if i in within:
+                    allocated.append(i)
+            else:
+                allocated.append(i)
             continue
 
         if len(allocated) != count:
-            raise ResourceAvailabilityException("can't allocate the vlans requested")
+            raise ResourceAvailabilityException("There were not enough available private vlans for the allocation. Please contact the administrators.")
 
         return allocated
 
-    def get_public_vlan(self):
+    def get_public_vlan(self, within=None):
         """Return reference to an available public network without reserving it."""
-        return PublicNetwork.objects.filter(lab=self.lab_set.first(), in_use=False).first()
+        r = PublicNetwork.objects.filter(lab=self.lab_set.first(), in_use=False)
+        if within is not None:
+            r = r.filter(vlan__in=within)
+
+        if r.count() < 1:
+            raise ResourceAvailabilityException("There were not enough available public vlans for the allocation. Please contact the administrators.")
+
+        return r.first()
 
     def reserve_public_vlan(self, vlan):
         """Reserves the Public Network that has the given vlan."""
diff --git a/src/resource_inventory/migrations/0022_auto_20210925_2028.py b/src/resource_inventory/migrations/0022_auto_20210925_2028.py
new file mode 100644 (file)
index 0000000..2b0b902
--- /dev/null
@@ -0,0 +1,23 @@
+# Generated by Django 2.2 on 2021-09-25 20:28
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('resource_inventory', '0021_resourceconfiguration_cloud_init_files'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='resourcetemplate',
+            name='private_vlan_pool',
+            field=models.TextField(default=''),
+        ),
+        migrations.AddField(
+            model_name='resourcetemplate',
+            name='public_vlan_pool',
+            field=models.TextField(default=''),
+        ),
+    ]
index 2c631dc..1505f02 100644 (file)
@@ -193,6 +193,24 @@ class ResourceTemplate(models.Model):
     temporary = models.BooleanField(default=False)
     copy_of = models.ForeignKey("ResourceTemplate", blank=True, null=True, on_delete=models.SET_NULL)
 
+    # if these fields are empty ("") then they are implicitly "every vlan",
+    # otherwise we filter any allocations we try to instantiate against this list
+    # they should be represented as a json list of integers
+    private_vlan_pool = models.TextField(default="")
+    public_vlan_pool = models.TextField(default="")
+
+    def private_vlan_pool_set(self):
+        if self.private_vlan_pool != "":
+            return set(json.loads(self.private_vlan_pool))
+        else:
+            return None
+
+    def public_vlan_pool_set(self):
+        if self.private_vlan_pool != "":
+            return set(json.loads(self.public_vlan_pool))
+        else:
+            return None
+
     def getConfigs(self):
         configs = self.resourceConfigurations.all()
         return list(configs)
index 9406977..14a118c 100644 (file)
@@ -74,12 +74,13 @@ class ResourceManager:
         vlan_manager = resourceTemplate.lab.vlan_manager
         for network in resourceTemplate.networks.all():
             if network.is_public:
-                public_net = vlan_manager.get_public_vlan()
+                # already throws if can't get requested count, so can always expect public_net to be Some
+                public_net = vlan_manager.get_public_vlan(within=resourceTemplate.public_vlan_pool_set())
                 vlan_manager.reserve_public_vlan(public_net.vlan)
                 networks[network.name] = public_net.vlan
             else:
                 # already throws if can't get requested count, so can always index in @ 0
-                vlans = vlan_manager.get_vlans(count=1)
+                vlans = vlan_manager.get_vlans(count=1, within=resourceTemplate.private_vlan_pool_set())
                 vlan_manager.reserve_vlans(vlans[0])
                 networks[network.name] = vlans[0]
         return networks