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 # Generic resource templates
109 class GenericResourceBundle(models.Model):
110 id = models.AutoField(primary_key=True)
111 name = models.CharField(max_length=300, unique=True)
112 xml = models.TextField()
113 owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
114 lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL)
115 description = models.CharField(max_length=1000, default="")
119 for genericResource in self.generic_resources.all():
120 return_hosts.append(genericResource.getHost())
128 class Network(models.Model):
129 id = models.AutoField(primary_key=True)
130 name = models.CharField(max_length=100)
131 bundle = models.ForeignKey(GenericResourceBundle, on_delete=models.CASCADE, related_name="networks")
132 is_public = models.BooleanField()
138 class NetworkConnection(models.Model):
139 network = models.ForeignKey(Network, on_delete=models.CASCADE)
140 vlan_is_tagged = models.BooleanField()
143 class Vlan(models.Model):
144 id = models.AutoField(primary_key=True)
145 vlan_id = models.IntegerField()
146 tagged = models.BooleanField()
147 public = models.BooleanField(default=False)
148 network = models.ForeignKey(Network, on_delete=models.DO_NOTHING, null=True)
151 return str(self.vlan_id) + ("_T" if self.tagged else "")
154 class GenericResource(models.Model):
155 bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.CASCADE)
156 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 (_)")
157 name = models.CharField(max_length=200, validators=[hostname_validchars])
160 return self.generic_host
166 validname = re.compile(r'(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))')
167 if not validname.match(self.name):
168 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 (_)"
174 class GenericHost(models.Model):
175 id = models.AutoField(primary_key=True)
176 profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
177 resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.CASCADE)
180 return self.resource.name
183 # Physical, actual resources
184 class ResourceBundle(models.Model):
185 id = models.AutoField(primary_key=True)
186 template = models.ForeignKey(GenericResourceBundle, on_delete=models.SET_NULL, null=True)
189 if self.template is None:
190 return "Resource bundle " + str(self.id) + " with no template"
191 return "instance of " + str(self.template)
193 def get_host(self, role="Jumphost"):
194 return Host.objects.filter(bundle=self, config__opnfvRole__name=role).first()
197 class GenericInterface(models.Model):
198 id = models.AutoField(primary_key=True)
199 profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
200 host = models.ForeignKey(GenericHost, on_delete=models.CASCADE, related_name='generic_interfaces')
201 connections = models.ManyToManyField(NetworkConnection)
204 return "type " + str(self.profile) + " on host " + str(self.host)
207 class Scenario(models.Model):
208 id = models.AutoField(primary_key=True)
209 name = models.CharField(max_length=300)
215 class Installer(models.Model):
216 id = models.AutoField(primary_key=True)
217 name = models.CharField(max_length=200)
218 sup_scenarios = models.ManyToManyField(Scenario, blank=True)
224 class Opsys(models.Model):
225 id = models.AutoField(primary_key=True)
226 name = models.CharField(max_length=100)
227 sup_installers = models.ManyToManyField(Installer, blank=True)
233 class NetworkRole(models.Model):
234 name = models.CharField(max_length=100)
235 network = models.ForeignKey(Network, on_delete=models.CASCADE)
238 class ConfigBundle(models.Model):
239 id = models.AutoField(primary_key=True)
240 owner = models.ForeignKey(User, on_delete=models.CASCADE)
241 name = models.CharField(max_length=200, unique=True)
242 description = models.CharField(max_length=1000, default="")
243 bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE)
249 class OPNFVConfig(models.Model):
250 id = models.AutoField(primary_key=True)
251 installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
252 scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
253 bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
254 networks = models.ManyToManyField(NetworkRole)
257 return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario)
260 class OPNFVRole(models.Model):
261 id = models.AutoField(primary_key=True)
262 name = models.CharField(max_length=200)
263 description = models.TextField()
269 class Image(models.Model):
271 model for representing OS images / snapshots of hosts
273 id = models.AutoField(primary_key=True)
274 lab_id = models.IntegerField() # ID the lab who holds this image knows
275 from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
276 name = models.CharField(max_length=200)
277 owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
278 public = models.BooleanField(default=True)
279 host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
280 description = models.TextField()
281 os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE)
287 def get_sentinal_opnfv_role():
288 return OPNFVRole.objects.get_or_create(name="deleted", description="Role was deleted.")
291 class HostConfiguration(models.Model):
293 model to represent a complete configuration for a single
296 id = models.AutoField(primary_key=True)
297 host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE)
298 image = models.ForeignKey(Image, on_delete=models.PROTECT)
299 bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE)
300 opnfvRole = models.ForeignKey(OPNFVRole, on_delete=models.SET(get_sentinal_opnfv_role))
303 return "config with " + str(self.host) + " and image " + str(self.image)
306 class RemoteInfo(models.Model):
307 address = models.CharField(max_length=15)
308 mac_address = models.CharField(max_length=17)
309 password = models.CharField(max_length=100)
310 user = models.CharField(max_length=100)
311 management_type = models.CharField(max_length=50, default="ipmi")
312 versions = models.CharField(max_length=100) # json serialized list of floats
315 def get_default_remote_info():
316 return RemoteInfo.objects.get_or_create(
318 mac_address="default",
321 management_type="default",
326 # Concrete host, actual machine in a lab
327 class Host(models.Model):
328 id = models.AutoField(primary_key=True)
329 template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True)
330 booked = models.BooleanField(default=False)
331 name = models.CharField(max_length=200, unique=True)
332 bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True)
333 config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL)
334 labid = models.CharField(max_length=200, default="default_id")
335 profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE)
336 lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
337 working = models.BooleanField(default=True)
338 vendor = models.CharField(max_length=100, default="unknown")
339 model = models.CharField(max_length=150, default="unknown")
340 remote_management = models.ForeignKey(RemoteInfo, default=get_default_remote_info, on_delete=models.SET(get_default_remote_info))
346 class Interface(models.Model):
347 id = models.AutoField(primary_key=True)
348 mac_address = models.CharField(max_length=17)
349 bus_address = models.CharField(max_length=50)
350 name = models.CharField(max_length=100, default="eth0")
351 config = models.ManyToManyField(Vlan)
352 host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces')
355 return self.mac_address + " on host " + str(self.host)