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)
187 def get_host(self, role="Jumphost"):
188 return Host.objects.filter(bundle=self, config__opnfvRole__name=role).first()
194 class GenericInterface(models.Model):
195 id = models.AutoField(primary_key=True)
196 vlans = models.ManyToManyField(Vlan)
197 profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
198 host = models.ForeignKey(GenericHost, on_delete=models.CASCADE, related_name='generic_interfaces')
201 return "type " + str(self.profile) + " on host " + str(self.host)
204 class Scenario(models.Model):
205 id = models.AutoField(primary_key=True)
206 name = models.CharField(max_length=300)
212 class Installer(models.Model):
213 id = models.AutoField(primary_key=True)
214 name = models.CharField(max_length=200)
215 sup_scenarios = models.ManyToManyField(Scenario, blank=True)
221 class Opsys(models.Model):
222 id = models.AutoField(primary_key=True)
223 name = models.CharField(max_length=100)
224 sup_installers = models.ManyToManyField(Installer, blank=True)
230 class ConfigBundle(models.Model):
231 id = models.AutoField(primary_key=True)
232 owner = models.ForeignKey(User, on_delete=models.CASCADE)
233 name = models.CharField(max_length=200, unique=True)
234 description = models.CharField(max_length=1000, default="")
235 bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE)
241 class OPNFVConfig(models.Model):
242 id = models.AutoField(primary_key=True)
243 installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
244 scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
245 bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
248 return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario)
251 class OPNFVRole(models.Model):
252 id = models.AutoField(primary_key=True)
253 name = models.CharField(max_length=200)
254 description = models.TextField()
260 class Image(models.Model):
262 model for representing OS images / snapshots of hosts
264 id = models.AutoField(primary_key=True)
265 lab_id = models.IntegerField() # ID the lab who holds this image knows
266 from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
267 name = models.CharField(max_length=200)
268 owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
269 public = models.BooleanField(default=True)
270 host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
271 description = models.TextField()
272 os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE)
278 def get_sentinal_opnfv_role():
279 return OPNFVRole.objects.get_or_create(name="deleted", description="Role was deleted.")
282 class HostConfiguration(models.Model):
284 model to represent a complete configuration for a single
287 id = models.AutoField(primary_key=True)
288 host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE)
289 image = models.ForeignKey(Image, on_delete=models.PROTECT)
290 bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE)
291 opnfvRole = models.ForeignKey(OPNFVRole, on_delete=models.SET(get_sentinal_opnfv_role))
294 return "config with " + str(self.host) + " and image " + str(self.image)
297 class RemoteInfo(models.Model):
298 address = models.CharField(max_length=15)
299 mac_address = models.CharField(max_length=17)
300 password = models.CharField(max_length=100)
301 user = models.CharField(max_length=100)
302 management_type = models.CharField(max_length=50, default="ipmi")
303 versions = models.CharField(max_length=100) # json serialized list of floats
306 def get_default_remote_info():
307 return RemoteInfo.objects.get_or_create(
309 mac_address="default",
312 management_type="default",
317 # Concrete host, actual machine in a lab
318 class Host(models.Model):
319 id = models.AutoField(primary_key=True)
320 template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True)
321 booked = models.BooleanField(default=False)
322 name = models.CharField(max_length=200, unique=True)
323 bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True)
324 config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL)
325 labid = models.CharField(max_length=200, default="default_id")
326 profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
327 lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
328 working = models.BooleanField(default=True)
329 vendor = models.CharField(max_length=100, default="unknown")
330 model = models.CharField(max_length=150, default="unknown")
331 remote_management = models.ForeignKey(RemoteInfo, default=get_default_remote_info, on_delete=models.SET(get_default_remote_info))
337 class Interface(models.Model):
338 id = models.AutoField(primary_key=True)
339 mac_address = models.CharField(max_length=17)
340 bus_address = models.CharField(max_length=50)
341 name = models.CharField(max_length=100, default="eth0")
342 config = models.ManyToManyField(Vlan)
343 host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces')
346 return self.mac_address + " on host " + str(self.host)