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("^[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.DO_NOTHING, 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.DO_NOTHING, 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.DO_NOTHING, 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.DO_NOTHING, 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.DO_NOTHING)
134 lab = models.ForeignKey(Lab, null=True, on_delete=models.DO_NOTHING)
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.DO_NOTHING)
150 hostname_validchars = RegexValidator(regex='(?=^.{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('(?=^.{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.DO_NOTHING)
171 resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.DO_NOTHING)
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.DO_NOTHING)
183 return "instance of " + str(self.template)
189 class GenericInterface(models.Model):
190 id = models.AutoField(primary_key=True)
191 vlans = models.ManyToManyField(Vlan)
192 profile = models.ForeignKey(InterfaceProfile, on_delete=models.DO_NOTHING)
193 host = models.ForeignKey(GenericHost, on_delete=models.DO_NOTHING, related_name='generic_interfaces')
196 return "type " + str(self.profile) + " on host " + str(self.host)
199 class Scenario(models.Model):
200 id = models.AutoField(primary_key=True)
201 name = models.CharField(max_length=300)
207 class Installer(models.Model):
208 id = models.AutoField(primary_key=True)
209 name = models.CharField(max_length=200)
210 sup_scenarios = models.ManyToManyField(Scenario, blank=True)
216 class Opsys(models.Model):
217 id = models.AutoField(primary_key=True)
218 name = models.CharField(max_length=100)
219 sup_installers = models.ManyToManyField(Installer, blank=True)
225 class ConfigBundle(models.Model):
226 id = models.AutoField(primary_key=True)
227 owner = models.ForeignKey(User, on_delete=models.CASCADE) # consider setting to root user?
228 name = models.CharField(max_length=200, unique=True)
229 description = models.CharField(max_length=1000, default="")
230 bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE)
236 class OPNFVConfig(models.Model):
237 id = models.AutoField(primary_key=True)
238 installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
239 scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
240 bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
243 return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario)
246 class OPNFVRole(models.Model):
247 id = models.AutoField(primary_key=True)
248 name = models.CharField(max_length=200)
249 description = models.TextField()
255 class Image(models.Model):
257 model for representing OS images / snapshots of hosts
259 id = models.AutoField(primary_key=True)
260 lab_id = models.IntegerField() # ID the lab who holds this image knows
261 from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
262 name = models.CharField(max_length=200)
263 owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
264 public = models.BooleanField(default=True)
265 # may need to change host_type.on_delete to models.SET() once images are transferrable between compatible host types
266 host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
267 description = models.TextField()
273 class HostConfiguration(models.Model):
275 model to represent a complete configuration for a single
278 id = models.AutoField(primary_key=True)
279 host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE)
280 image = models.ForeignKey(Image, on_delete=models.PROTECT)
281 bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE)
282 opnfvRole = models.ForeignKey(OPNFVRole, on_delete=models.PROTECT)
285 return "config with " + str(self.host) + " and image " + str(self.image)
288 # Concrete host, actual machine in a lab
289 class Host(models.Model):
290 id = models.AutoField(primary_key=True)
291 template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True)
292 booked = models.BooleanField(default=False)
293 name = models.CharField(max_length=200, unique=True)
294 bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True)
295 config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL)
296 labid = models.CharField(max_length=200, default="default_id")
297 profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
298 lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
299 working = models.BooleanField(default=True)
300 vendor = models.CharField(max_length=100, default="unknown")
301 model = models.CharField(max_length=150, default="unknown")
307 class Interface(models.Model):
308 id = models.AutoField(primary_key=True)
309 mac_address = models.CharField(max_length=17)
310 bus_address = models.CharField(max_length=50)
311 name = models.CharField(max_length=100, default="eth0")
312 config = models.ManyToManyField(Vlan)
313 host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces')
316 return self.mac_address + " on host " + str(self.host)