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(max_length=50, choices=[
44 ("onboard", "onboard"),
49 return self.name + " for " + str(self.host)
52 class DiskProfile(models.Model):
53 id = models.AutoField(primary_key=True)
54 size = models.IntegerField()
55 media_type = models.CharField(max_length=50, choices=[
59 name = models.CharField(max_length=50)
60 host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='storageprofile')
61 rotation = models.IntegerField(default=0)
62 interface = models.CharField(max_length=50, choices=[
72 return self.name + " for " + str(self.host)
75 class CpuProfile(models.Model):
76 id = models.AutoField(primary_key=True)
77 cores = models.IntegerField()
78 architecture = models.CharField(max_length=50, choices=[
80 ("aarch64", "aarch64")
82 cpus = models.IntegerField()
83 host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='cpuprofile')
84 cflags = models.TextField(null=True)
87 return str(self.architecture) + " " + str(self.cpus) + "S" + str(self.cores) + " C for " + str(self.host)
90 class RamProfile(models.Model):
91 id = models.AutoField(primary_key=True)
92 amount = models.IntegerField()
93 channels = models.IntegerField()
94 host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='ramprofile')
97 return str(self.amount) + "G for " + str(self.host)
100 ##Networking -- located here due to import order requirements
101 class Network(models.Model):
102 id = models.AutoField(primary_key=True)
103 vlan_id = models.IntegerField()
104 name = models.CharField(max_length=100)
109 class Vlan(models.Model):
110 id = models.AutoField(primary_key=True)
111 vlan_id = models.IntegerField()
112 tagged = models.BooleanField()
113 public = models.BooleanField(default=False)
116 return str(self.vlan_id) + ("_T" if self.tagged else "")
119 # Generic resource templates
120 class GenericResourceBundle(models.Model):
121 id = models.AutoField(primary_key=True)
122 name = models.CharField(max_length=300, unique=True)
123 xml = models.TextField()
124 owner = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING)
125 lab = models.ForeignKey(Lab, null=True, on_delete=models.DO_NOTHING)
126 description = models.CharField(max_length=1000, default="")
130 for genericResource in self.generic_resources.all():
131 return_hosts.append(genericResource.getHost())
139 class GenericResource(models.Model):
140 bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.DO_NOTHING)
141 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 (_)")
142 name = models.CharField(max_length=200, validators=[hostname_validchars])
145 return self.generic_host
151 validname = re.compile('(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))')
152 if not validname.match(self.name):
153 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 (_)"
159 class GenericHost(models.Model):
160 id = models.AutoField(primary_key=True)
161 profile = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING)
162 resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.DO_NOTHING)
165 return self.resource.name
168 # Physical, actual resources
169 class ResourceBundle(models.Model):
170 id = models.AutoField(primary_key=True)
171 template = models.ForeignKey(GenericResourceBundle, on_delete=models.DO_NOTHING)
174 return "instance of " + str(self.template)
180 class GenericInterface(models.Model):
181 id = models.AutoField(primary_key=True)
182 vlans = models.ManyToManyField(Vlan)
183 profile = models.ForeignKey(InterfaceProfile, on_delete=models.DO_NOTHING)
184 host = models.ForeignKey(GenericHost, on_delete=models.DO_NOTHING, related_name='generic_interfaces')
187 return "type " + str(self.profile) + " on host " + str(self.host)
190 class Scenario(models.Model):
191 id = models.AutoField(primary_key=True)
192 name = models.CharField(max_length=300)
197 class Installer(models.Model):
198 id = models.AutoField(primary_key=True)
199 name = models.CharField(max_length=200)
200 sup_scenarios = models.ManyToManyField(Scenario, blank=True)
205 class Opsys(models.Model):
206 id = models.AutoField(primary_key=True)
207 name = models.CharField(max_length=100)
208 sup_installers = models.ManyToManyField(Installer, blank=True)
213 class ConfigBundle(models.Model):
214 id = models.AutoField(primary_key=True)
215 owner = models.ForeignKey(User, on_delete=models.CASCADE) #consider setting to root user?
216 name = models.CharField(max_length=200, unique=True)
217 description = models.CharField(max_length=1000, default="")
218 bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE)
223 class OPNFVConfig(models.Model):
224 id = models.AutoField(primary_key=True)
225 installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
226 scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
227 bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
230 return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario)
232 class OPNFVRole(models.Model):
233 id = models.AutoField(primary_key=True)
234 name = models.CharField(max_length=200)
235 description = models.TextField()
240 class Image(models.Model):
242 model for representing OS images / snapshots of hosts
244 id = models.AutoField(primary_key=True)
245 lab_id = models.IntegerField() # ID the lab who holds this image knows
246 from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
247 name = models.CharField(max_length=200)
248 owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
249 public = models.BooleanField(default=True)
250 host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE) #may need to change to models.SET() once images are transferrable between compatible host types
251 description = models.TextField()
256 class HostConfiguration(models.Model):
258 model to represent a complete configuration for a single
261 id = models.AutoField(primary_key=True)
262 host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE)
263 image = models.ForeignKey(Image, on_delete=models.PROTECT)
264 bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE)
265 opnfvRole = models.ForeignKey(OPNFVRole, on_delete=models.PROTECT) #need protocol for phasing out a role if we are going to allow that to happen
268 return "config with " + str(self.host) + " and image " + str(self.image)
271 # Concrete host, actual machine in a lab
272 class Host(models.Model):
273 id = models.AutoField(primary_key=True)
274 template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True)
275 booked = models.BooleanField(default=False)
276 name = models.CharField(max_length=200, unique=True)
277 bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True)
278 config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL)
279 labid = models.CharField(max_length=200, default="default_id")
280 profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
281 lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
282 working = models.BooleanField(default=True)
283 vendor = models.CharField(max_length=100, default="unknown")
284 model = models.CharField(max_length=150, default="unknown")
290 class Interface(models.Model):
291 id = models.AutoField(primary_key=True)
292 mac_address = models.CharField(max_length=17)
293 bus_address = models.CharField(max_length=50)
294 name = models.CharField(max_length=100, default="eth0")
295 config = models.ManyToManyField(Vlan)
296 host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces')
299 return self.mac_address + " on host " + str(self.host)