diff options
author | Parker Berberian <pberberian@iol.unh.edu> | 2018-10-10 16:06:47 -0400 |
---|---|---|
committer | Parker Berberian <pberberian@iol.unh.edu> | 2018-10-15 13:16:11 -0400 |
commit | 1f3a770d2547848590f39e9d9b9bdffeb94eec14 (patch) | |
tree | 97222e5facd1a242d951c38482315057b5790d51 /src/resource_inventory/models.py | |
parent | 6d4019e59eda897384e9c00d1daf8b2ce87d128f (diff) |
Lab as a Service 2.0
See changes here:
https://wiki.opnfv.org/display/INF/Pharos+Laas
Change-Id: I59ada5f98e70a28d7f8c14eab3239597e236ca26
Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu>
Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
Diffstat (limited to 'src/resource_inventory/models.py')
-rw-r--r-- | src/resource_inventory/models.py | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py new file mode 100644 index 0000000..b71748e --- /dev/null +++ b/src/resource_inventory/models.py @@ -0,0 +1,299 @@ +############################################################################## +# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from django.contrib.auth.models import User +from django.db import models +from django.core.validators import RegexValidator + +import re + +from account.models import Lab + + +# profile of resources hosted by labs +class HostProfile(models.Model): + id = models.AutoField(primary_key=True) + host_type = models.PositiveSmallIntegerField(default=0) + name = models.CharField(max_length=200, unique=True) + description = models.TextField() + labs = models.ManyToManyField(Lab, related_name="hostprofiles") + + def validate(self): + validname = re.compile("^[A-Za-z0-9\-\_\.\/\, ]+$") + if not validname.match(self.name): + return "Invalid host profile name given. Name must only use A-Z, a-z, 0-9, hyphens, underscores, dots, commas, or spaces." + else: + return None + + def __str__(self): + return self.name + + +class InterfaceProfile(models.Model): + id = models.AutoField(primary_key=True) + speed = models.IntegerField() + name = models.CharField(max_length=100) + host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='interfaceprofile') + nic_type = models.CharField(max_length=50, choices=[ + ("onboard", "onboard"), + ("pcie", "pcie") + ], default="onboard") + + def __str__(self): + return self.name + " for " + str(self.host) + + +class DiskProfile(models.Model): + id = models.AutoField(primary_key=True) + size = models.IntegerField() + media_type = models.CharField(max_length=50, choices=[ + ("SSD", "SSD"), + ("HDD", "HDD") + ]) + name = models.CharField(max_length=50) + host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='storageprofile') + rotation = models.IntegerField(default=0) + interface = models.CharField(max_length=50, choices=[ + ("sata", "sata"), + ("sas", "sas"), + ("ssd", "ssd"), + ("nvme", "nvme"), + ("scsi", "scsi"), + ("iscsi", "iscsi"), + ], default="sata") + + def __str__(self): + return self.name + " for " + str(self.host) + + +class CpuProfile(models.Model): + id = models.AutoField(primary_key=True) + cores = models.IntegerField() + architecture = models.CharField(max_length=50, choices=[ + ("x86_64", "x86_64"), + ("aarch64", "aarch64") + ]) + cpus = models.IntegerField() + host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='cpuprofile') + cflags = models.TextField(null=True) + + def __str__(self): + return str(self.architecture) + " " + str(self.cpus) + "S" + str(self.cores) + " C for " + str(self.host) + + +class RamProfile(models.Model): + id = models.AutoField(primary_key=True) + amount = models.IntegerField() + channels = models.IntegerField() + host = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING, related_name='ramprofile') + + def __str__(self): + return str(self.amount) + "G for " + str(self.host) + + +##Networking -- located here due to import order requirements +class Network(models.Model): + id = models.AutoField(primary_key=True) + vlan_id = models.IntegerField() + name = models.CharField(max_length=100) + + def __str__(self): + return self.name + +class Vlan(models.Model): + id = models.AutoField(primary_key=True) + vlan_id = models.IntegerField() + tagged = models.BooleanField() + public = models.BooleanField(default=False) + + def __str__(self): + return str(self.vlan_id) + ("_T" if self.tagged else "") + + +# Generic resource templates +class GenericResourceBundle(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=300, unique=True) + xml = models.TextField() + owner = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING) + lab = models.ForeignKey(Lab, null=True, on_delete=models.DO_NOTHING) + description = models.CharField(max_length=1000, default="") + + def getHosts(self): + return_hosts = [] + for genericResource in self.generic_resources.all(): + return_hosts.append(genericResource.getHost()) + + return return_hosts + + def __str__(self): + return self.name + + +class GenericResource(models.Model): + bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.DO_NOTHING) + 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 (_)") + name = models.CharField(max_length=200, validators=[hostname_validchars]) + + def getHost(self): + return self.generic_host + + def __str__(self): + return self.name + + def validate(self): + validname = re.compile('(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))') + if not validname.match(self.name): + 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 (_)" + else: + return None + + +# Host template +class GenericHost(models.Model): + id = models.AutoField(primary_key=True) + profile = models.ForeignKey(HostProfile, on_delete=models.DO_NOTHING) + resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.DO_NOTHING) + + def __str__(self): + return self.resource.name + + +# Physical, actual resources +class ResourceBundle(models.Model): + id = models.AutoField(primary_key=True) + template = models.ForeignKey(GenericResourceBundle, on_delete=models.DO_NOTHING) + + def __str__(self): + return "instance of " + str(self.template) + + +# Networking + + +class GenericInterface(models.Model): + id = models.AutoField(primary_key=True) + vlans = models.ManyToManyField(Vlan) + profile = models.ForeignKey(InterfaceProfile, on_delete=models.DO_NOTHING) + host = models.ForeignKey(GenericHost, on_delete=models.DO_NOTHING, related_name='generic_interfaces') + + def __str__(self): + return "type " + str(self.profile) + " on host " + str(self.host) + + +class Scenario(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=300) + + def __str__(self): + return self.name + +class Installer(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=200) + sup_scenarios = models.ManyToManyField(Scenario, blank=True) + + def __str__(self): + return self.name + +class Opsys(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=100) + sup_installers = models.ManyToManyField(Installer, blank=True) + + def __str__(self): + return self.name + +class ConfigBundle(models.Model): + id = models.AutoField(primary_key=True) + owner = models.ForeignKey(User, on_delete=models.CASCADE) #consider setting to root user? + name = models.CharField(max_length=200, unique=True) + description = models.CharField(max_length=1000, default="") + bundle = models.ForeignKey(GenericResourceBundle, null=True, on_delete=models.CASCADE) + + def __str__(self): + return self.name + +class OPNFVConfig(models.Model): + id = models.AutoField(primary_key=True) + installer = models.ForeignKey(Installer, on_delete=models.CASCADE) + scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE) + bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE) + + def __str__(self): + return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario) + +class OPNFVRole(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=200) + description = models.TextField() + + def __str__(self): + return self.name + +class Image(models.Model): + """ + model for representing OS images / snapshots of hosts + """ + id = models.AutoField(primary_key=True) + lab_id = models.IntegerField() # ID the lab who holds this image knows + from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE) + name = models.CharField(max_length=200) + owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) + public = models.BooleanField(default=True) + host_type = models.ForeignKey(HostProfile, on_delete=models.CASCADE) #may need to change to models.SET() once images are transferrable between compatible host types + description = models.TextField() + + def __str__(self): + return self.name + +class HostConfiguration(models.Model): + """ + model to represent a complete configuration for a single + physical host + """ + id = models.AutoField(primary_key=True) + host = models.ForeignKey(GenericHost, related_name="configuration", on_delete=models.CASCADE) + image = models.ForeignKey(Image, on_delete=models.PROTECT) + bundle = models.ForeignKey(ConfigBundle, related_name="hostConfigurations", null=True, on_delete=models.CASCADE) + opnfvRole = models.ForeignKey(OPNFVRole, on_delete=models.PROTECT) #need protocol for phasing out a role if we are going to allow that to happen + + def __str__(self): + return "config with " + str(self.host) + " and image " + str(self.image) + + +# Concrete host, actual machine in a lab +class Host(models.Model): + id = models.AutoField(primary_key=True) + template = models.ForeignKey(GenericHost, on_delete=models.SET_NULL, null=True) + booked = models.BooleanField(default=False) + name = models.CharField(max_length=200, unique=True) + bundle = models.ForeignKey(ResourceBundle, related_name='hosts', on_delete=models.SET_NULL, null=True) + config = models.ForeignKey(HostConfiguration, null=True, related_name="configuration", on_delete=models.SET_NULL) + labid = models.CharField(max_length=200, default="default_id") + profile = models.ForeignKey(HostProfile, on_delete=models.CASCADE) + lab = models.ForeignKey(Lab, on_delete=models.CASCADE) + working = models.BooleanField(default=True) + vendor = models.CharField(max_length=100, default="unknown") + model = models.CharField(max_length=150, default="unknown") + + def __str__(self): + return self.name + + +class Interface(models.Model): + id = models.AutoField(primary_key=True) + mac_address = models.CharField(max_length=17) + bus_address = models.CharField(max_length=50) + name = models.CharField(max_length=100, default="eth0") + config = models.ManyToManyField(Vlan) + host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces') + + def __str__(self): + return self.mac_address + " on host " + str(self.host) |