aboutsummaryrefslogtreecommitdiffstats
path: root/src/resource_inventory/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/resource_inventory/models.py')
-rw-r--r--src/resource_inventory/models.py131
1 files changed, 125 insertions, 6 deletions
diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py
index 7fe479a..aefd5ce 100644
--- a/src/resource_inventory/models.py
+++ b/src/resource_inventory/models.py
@@ -9,10 +9,12 @@
##############################################################################
from django.contrib.auth.models import User
+
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
import traceback
+import json
import re
from collections import Counter
@@ -20,7 +22,6 @@ from collections import Counter
from account.models import Lab
from dashboard.utils import AbstractModelQuery
-
"""
Profiles of resources hosted by labs.
@@ -33,6 +34,10 @@ Profile models (e.g. an x86 server profile and armv8 server profile.
class ResourceProfile(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200, unique=True)
+ architecture = models.CharField(max_length=50, choices=[
+ ("x86_64", "x86_64"),
+ ("aarch64", "aarch64")
+ ])
description = models.TextField()
labs = models.ManyToManyField(Lab, related_name="resourceprofiles")
@@ -147,6 +152,24 @@ with varying degrees of abstraction.
"""
+class CloudInitFile(models.Model):
+ text = models.TextField()
+
+ # higher priority is applied later, so "on top" of existing files
+ priority = models.IntegerField()
+
+ @classmethod
+ def merge_strategy(cls):
+ return [
+ {'name': 'list', 'settings': ['append']},
+ {'name': 'dict', 'settings': ['recurse_list', 'replace']},
+ ]
+
+ @classmethod
+ def create(cls, text="", priority=0):
+ return CloudInitFile.objects.create(priority=priority, text=text)
+
+
class ResourceTemplate(models.Model):
"""
Models a "template" of a complete, configured collection of resources that can be booked.
@@ -167,6 +190,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)
@@ -235,9 +276,14 @@ class ResourceConfiguration(models.Model):
is_head_node = models.BooleanField(default=False)
name = models.CharField(max_length=3000, default="opnfv_host")
+ cloud_init_files = models.ManyToManyField(CloudInitFile, blank=True)
+
def __str__(self):
return str(self.name)
+ def ci_file_list(self):
+ return list(self.cloud_init_files.order_by("priority").all())
+
def get_default_remote_info():
return RemoteInfo.objects.get_or_create(
@@ -369,10 +415,43 @@ class Server(Resource):
return isinstance(other, Server) and other.name == self.name
+def is_serializable(data):
+ try:
+ json.dumps(data)
+ return True
+ except Exception:
+ return False
+
+
class Opsys(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
- sup_installers = models.ManyToManyField("Installer", blank=True)
+ lab_id = models.CharField(max_length=100)
+ obsolete = models.BooleanField(default=False)
+ available = models.BooleanField(default=True) # marked true by Cobbler if it exists there
+ from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+
+ indexes = [
+ models.Index(fields=['cobbler_id'])
+ ]
+
+ def new_from_data(data):
+ opsys = Opsys()
+ opsys.update(data)
+ return opsys
+
+ def serialize(self):
+ d = {}
+ for field in vars(self):
+ attr = getattr(self, field)
+ if is_serializable(attr):
+ d[field] = attr
+ return d
+
+ def update(self, data):
+ for field in vars(self):
+ if field in data:
+ setattr(self, field, data[field] if data[field] else getattr(self, field))
def __str__(self):
return self.name
@@ -382,18 +461,51 @@ class Image(models.Model):
"""Model for representing OS images / snapshots of hosts."""
id = models.AutoField(primary_key=True)
- lab_id = models.IntegerField() # ID the lab who holds this image knows
from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
- name = models.CharField(max_length=200)
+ architecture = models.CharField(max_length=50, choices=[
+ ("x86_64", "x86_64"),
+ ("aarch64", "aarch64"),
+ ("unknown", "unknown"),
+ ])
+ lab_id = models.CharField(max_length=100)
+ name = models.CharField(max_length=100)
owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
public = models.BooleanField(default=True)
- host_type = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE)
description = models.TextField()
os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE)
+ available = models.BooleanField(default=True) # marked True by cobbler if it exists there
+ obsolete = models.BooleanField(default=False)
+
+ indexes = [
+ models.Index(fields=['architecture']),
+ models.Index(fields=['cobbler_id'])
+ ]
+
def __str__(self):
return self.name
+ def is_obsolete(self):
+ return self.obsolete or self.os.obsolete
+
+ def serialize(self):
+ d = {}
+ for field in vars(self):
+ attr = getattr(self, field)
+ if is_serializable(attr):
+ d[field] = attr
+ return d
+
+ def update(self, data):
+ for field in vars(self):
+ if field in data:
+ setattr(self, field, data[field] if data[field] else getattr(self, field))
+
+ def new_from_data(data):
+ img = Image()
+ img.update(data)
+ return img
+
def in_use(self):
for resource in ResourceQuery.filter(config__image=self):
if resource.is_reserved():
@@ -409,7 +521,7 @@ Networking configuration models
class Network(models.Model):
id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100)
+ name = models.CharField(max_length=200)
bundle = models.ForeignKey(ResourceTemplate, on_delete=models.CASCADE, related_name="networks")
is_public = models.BooleanField()
@@ -507,6 +619,13 @@ class NetworkRole(models.Model):
network = models.ForeignKey(Network, on_delete=models.CASCADE)
+def create_resource_ref_string(for_hosts: [str]) -> str:
+ # need to sort the list, then do dump
+ for_hosts.sort()
+
+ return json.dumps(for_hosts)
+
+
class OPNFVConfig(models.Model):
id = models.AutoField(primary_key=True)
installer = models.ForeignKey(Installer, on_delete=models.CASCADE)