1 ##############################################################################
2 # Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
10 from django.contrib.auth.models import User
11 from django.db import models
12 from django.core.validators import RegexValidator
16 from account.models import Lab
19 # profile of resources hosted by labs
20 class HostProfile(models.Model):
21 id = models.AutoField(primary_key=True)
22 host_type = models.PositiveSmallIntegerField(default=0)
23 name = models.CharField(max_length=200, unique=True)
24 description = models.TextField()
25 labs = models.ManyToManyField(Lab, related_name="hostprofiles")
28 validname = re.compile(r"^[A-Za-z0-9\-\_\.\/\, ]+$")
29 if not validname.match(self.name):
30 return "Invalid host profile name given. Name must only use A-Z, a-z, 0-9, hyphens, underscores, dots, commas, or spaces."
38 class InterfaceProfile(models.Model):
39 id = models.AutoField(primary_key=True)
40 speed = models.IntegerField()
41 name = models.CharField(max_length=100)
42 host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='interfaceprofile')
43 nic_type = models.CharField(
46 ("onboard", "onboard"),
53 return self.name + " for " + str(self.host)
56 class DiskProfile(models.Model):
57 id = models.AutoField(primary_key=True)
58 size = models.IntegerField()
59 media_type = models.CharField(max_length=50, choices=[
63 name = models.CharField(max_length=50)
64 host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='storageprofile')
65 rotation = models.IntegerField(default=0)
66 interface = models.CharField(
80 return self.name + " for " + str(self.host)
83 class CpuProfile(models.Model):
84 id = models.AutoField(primary_key=True)
85 cores = models.IntegerField()
86 architecture = models.CharField(max_length=50, choices=[
88 ("aarch64", "aarch64")
90 cpus = models.IntegerField()
91 host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='cpuprofile')
92 cflags = models.TextField(null=True)
95 return str(self.architecture) + " " + str(self.cpus) + "S" + str(self.cores) + " C for " + str(self.host)
98 class RamProfile(models.Model):
99 id = models.AutoField(primary_key=True)
100 amount = models.IntegerField()
101 channels = models.IntegerField()
102 host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='ramprofile')
105 return str(self.amount) + "G for " + str(self.host)
108 # Networking -- located here due to import order requirements
109 class Network(models.Model):
110 id = models.AutoField(primary_key=True)
111 vlan_id = models.IntegerField()
112 name = models.CharField(max_length=100)
118 class Vlan(models.Model):
119 id = models.AutoField(primary_key=True)
120 vlan_id = models.IntegerField()
121 tagged = models.BooleanField()
122 public = models.BooleanField(default=False)
125 return str(self.vlan_id) + ("_T" if self.tagged else "")
128 # Generic resource templates
129 class GenericResourceBundle(models.Model):
130 id = models.AutoField(primary_key=True)
131 name = models.CharField(max_length=300, unique=True)
132 xml = models.TextField()
133 owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
134 lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL)
135 description = models.CharField(max_length=1000, default="")
139 for genericResource in self.generic_resources.all():
140 return_hosts.append(genericResource.getHost())
148 class GenericResource(models.Model):
149 bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.CASCADE)
150 hostname_validchars = RegexValidator(regex=r'(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))', message="Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)")
151 name = models.CharField(max_length=200, validators=[hostname_validchars])
154 return self.generic_host
160 validname = re.compile(r'(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))')
161 if not validname.match(self.name):
162 return "Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)"
168 class GenericHost(models.Model):
169 id = models.AutoField(primary_key=True)
170 profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
171 resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.CASCADE)
174 return self.resource.name
177 # Physical, actual resources
178 class ResourceBundle(models.Model):
179 id = models.AutoField(primary_key=True)
180 template = models.ForeignKey(GenericResourceBundle, on_delete=models.SET_NULL, null=True)
183 if self.template is None:
184 return "Resource bundle " + str(self.id) + " with no template"
185 return "instance of " + str(self.template)
191 class GenericInterface(models.Model):
192 id = models.AutoField(primary_key=True)
193 vlans = models.ManyToManyField(Vlan)
194 profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
195 host = models.ForeignKey(GenericHost, on_delete=models.CASCADE, related_name='generic_interfaces')
198 return "type " + str(self.profile) + " on host " + str(self.host)
201 class Scenario(models.Model):
202 id = models.AutoField(primary_key=True)
203 name = models.CharField(max_length=300)
209 class Installer(models.Model):
210 id = models.AutoField(primary_key=True)
211 name = models.CharField(max_length=200)
212 sup_scenarios = models.ManyToManyField(Scenario, blank=True)
218 class Opsys(models.Model):
219 id = models.AutoField(primary_key=True)
220 name = models.CharField(max_length=100)
221 sup_installers = models.ManyToManyField(Installer, blank=True)
227 class ConfigBundle(models.Model):
228 id = models.AutoField(primary_key=True)
229 owner = models.ForeignKey(User, on_delete=models.CASCADE)
230 name = models.CharField(max_length=200, unique=True)
231 description = models.CharField(max_length=1000, default="")
232 bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE)
238 class OPNFVConfig(models.Model):
239 id = models.AutoField(primary_key=True)
240 installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
241 scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
242 bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
245 return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario)
248 class OPNFVRole(models.Model):
249 id = models.AutoField(primary_key=True)
250 name = models.CharField(max_length=200)
251 description = models.TextField()
257 class Image(models.Model):
259 model for representing OS images / snapshots of hosts
261 id = models.AutoField(primary_key=True)
262 lab_id = models.IntegerField() # ID the lab who holds this image knows
263 from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
264 name = models.CharField(max_length=200)
265 owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
266 public = models.BooleanField(default=True)
267 host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
268 description = models.TextField()
269 os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE) #sentinal?
275 def get_sentinal_opnfv_role():
276 return OPNFVRole.objects.get_or_create(name="deleted", description="Role was deleted.")
279 class HostConfiguration(models.Model):
281 model to represent a complete configuration for a single
284 id = models.AutoField(primary_key=True)
285 host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE)
286 image = models.ForeignKey(Image, on_delete=models.PROTECT)
287 bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE)
288 opnfvRole = models.ForeignKey(OPNFVRole, on_delete=models.SET(get_sentinal_opnfv_role))
291 return "config with " + str(self.host) + " and image " + str(self.image)
294 # Concrete host, actual machine in a lab
295 class Host(models.Model):
296 id = models.AutoField(primary_key=True)
297 template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True)
298 booked = models.BooleanField(default=False)
299 name = models.CharField(max_length=200, unique=True)
300 bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True)
301 config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL)
302 labid = models.CharField(max_length=200, default="default_id")
303 profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
304 lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
305 working = models.BooleanField(default=True)
306 vendor = models.CharField(max_length=100, default="unknown")
307 model = models.CharField(max_length=150, default="unknown")
313 class Interface(models.Model):
314 id = models.AutoField(primary_key=True)
315 mac_address = models.CharField(max_length=17)
316 bus_address = models.CharField(max_length=50)
317 name = models.CharField(max_length=100, default="eth0")
318 config = models.ManyToManyField(Vlan)
319 host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces')
322 return self.mac_address + " on host " + str(self.host)