aboutsummaryrefslogtreecommitdiffstats
path: root/src/resource_inventory
diff options
context:
space:
mode:
Diffstat (limited to 'src/resource_inventory')
-rw-r--r--src/resource_inventory/admin.py59
-rw-r--r--src/resource_inventory/forms.py31
-rw-r--r--src/resource_inventory/idf_templater.py148
-rw-r--r--src/resource_inventory/migrations/0023_cloudinitfile_generated.py2
-rw-r--r--src/resource_inventory/migrations/0024_auto_20230608_1913.py262
-rw-r--r--src/resource_inventory/models.py689
-rw-r--r--src/resource_inventory/pdf_templater.py176
-rw-r--r--src/resource_inventory/resource_manager.py197
-rw-r--r--src/resource_inventory/tests/test_managers.py301
-rw-r--r--src/resource_inventory/tests/test_models.py173
-rw-r--r--src/resource_inventory/urls.py27
-rw-r--r--src/resource_inventory/views.py30
12 files changed, 274 insertions, 1821 deletions
diff --git a/src/resource_inventory/admin.py b/src/resource_inventory/admin.py
index 2444a98..da9cba3 100644
--- a/src/resource_inventory/admin.py
+++ b/src/resource_inventory/admin.py
@@ -9,62 +9,3 @@
from django.contrib import admin
-
-from resource_inventory.forms import InterfaceConfigurationForm
-
-from resource_inventory.models import (
- ResourceProfile,
- InterfaceProfile,
- DiskProfile,
- CpuProfile,
- RamProfile,
- ResourceTemplate,
- ResourceConfiguration,
- InterfaceConfiguration,
- Server,
- Interface,
- Network,
- Vlan,
- ResourceBundle,
- Scenario,
- Installer,
- Opsys,
- OPNFVConfig,
- OPNFVRole,
- Image,
- RemoteInfo,
- PhysicalNetwork,
- NetworkConnection,
-)
-
-
-admin.site.register([
- ResourceProfile,
- InterfaceProfile,
- DiskProfile,
- CpuProfile,
- RamProfile,
- ResourceTemplate,
- ResourceConfiguration,
- Server,
- Interface,
- Network,
- Vlan,
- ResourceBundle,
- Scenario,
- Installer,
- Opsys,
- OPNFVConfig,
- OPNFVRole,
- Image,
- PhysicalNetwork,
- NetworkConnection,
- RemoteInfo]
-)
-
-
-class InterfaceConfigurationAdmin(admin.ModelAdmin):
- form = InterfaceConfigurationForm
-
-
-admin.site.register(InterfaceConfiguration, InterfaceConfigurationAdmin)
diff --git a/src/resource_inventory/forms.py b/src/resource_inventory/forms.py
deleted file mode 100644
index fb8c102..0000000
--- a/src/resource_inventory/forms.py
+++ /dev/null
@@ -1,31 +0,0 @@
-##############################################################################
-# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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.core.exceptions import ValidationError
-from django import forms
-
-from resource_inventory.models import Network, InterfaceConfiguration
-
-
-class InterfaceConfigurationForm(forms.ModelForm):
- class Meta:
- model = InterfaceConfiguration
- fields = ['profile', 'resource_config', 'connections']
-
- def clean(self):
- connections = self.cleaned_data.get('connections')
- resource_config = self.cleaned_data.get('resource_config')
-
- valid_nets = set(Network.objects.filter(bundle=resource_config.template))
- curr_nets = set([conn.network for conn in connections])
-
- if not curr_nets.issubset(valid_nets):
- raise ValidationError("Cannot have network connection to network outside pod")
-
- return self.cleaned_data
diff --git a/src/resource_inventory/idf_templater.py b/src/resource_inventory/idf_templater.py
deleted file mode 100644
index 8f0f924..0000000
--- a/src/resource_inventory/idf_templater.py
+++ /dev/null
@@ -1,148 +0,0 @@
-##############################################################################
-# Copyright (c) 2019 Parker Berberian, Sawyer Bergeron, 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.template.loader import render_to_string
-
-from account.models import PublicNetwork
-
-from resource_inventory.models import Vlan
-
-
-class IDFTemplater:
- """Utility class to create a full Installer Descriptor File (IDF) yaml file."""
-
- net_names = ["admin", "mgmt", "private", "public"]
- bridge_names = {
- "admin": "br-admin",
- "mgmt": "br-mgmt",
- "private": "br-private",
- "public": "br-public"
- }
-
- def __init__(self):
- self.networks = {}
- for i, name in enumerate(self.net_names):
- self.networks[name] = {
- "name": name,
- "vlan": -1,
- "interface": i,
- "ip": "10.250." + str(i) + ".0",
- "netmask": 24
- }
-
- def makeIDF(self, booking):
- """Fill the IDF template with info about the resource."""
- template = "dashboard/idf.yaml"
- info = {}
- info['version'] = "0.1"
- info['net_config'] = self.get_net_config(booking)
- info['fuel'] = self.get_fuel_config(booking)
-
- return render_to_string(template, context=info)
-
- def get_net_config(self, booking):
- net_config = {}
- try:
- net_config['oob'] = self.get_oob_net(booking)
- except Exception:
- net_config['oob'] = {}
- try:
- net_config['public'] = self.get_public_net(booking)
- except Exception:
- net_config['public'] = {}
-
- for net in [net for net in self.net_names if net != "public"]:
- try:
- net_config[net] = self.get_single_net_config(booking, net)
- except Exception:
- net_config[net] = {}
-
- return net_config
-
- def get_public_net(self, booking):
- public = {}
- config = booking.opnfv_config
- public_role = config.networks.get(name="public")
- public_vlan = Vlan.objects.filter(network=public_role.network).first()
- public_network = PublicNetwork.objects.get(vlan=public_vlan.vlan_id, lab=booking.lab)
- self.networks['public']['vlan'] = public_vlan.vlan_id
- public['interface'] = self.networks['public']['interface']
- public['vlan'] = public_network.vlan # untagged??
- public['network'] = public_network.cidr.split("/")[0]
- public['mask'] = public_network.cidr.split("/")[1]
- # public['ip_range'] = 4 # necesary?
- public['gateway'] = public_network.gateway
- public['dns'] = ["1.1.1.1", "8.8.8.8"]
-
- return public
-
- def get_oob_net(self, booking):
- net = {}
- hosts = booking.resource.hosts.all()
- addrs = [host.remote_management.address for host in hosts]
- net['ip_range'] = ",".join(addrs)
- net['vlan'] = "native"
- return net
-
- def get_single_net_config(self, booking, net_name):
- config = booking.opnfv_config
- role = config.networks.get(name=net_name)
- vlan = Vlan.objects.filter(network=role.network).first()
- self.networks[net_name]['vlan'] = vlan.vlan_id
- net = {}
- net['interface'] = self.networks[net_name]['interface']
- net['vlan'] = vlan.vlan_id
- net['network'] = self.networks[net_name]['ip']
- net['mask'] = self.networks[net_name]['netmask']
-
- return net
-
- def get_fuel_config(self, booking):
- fuel = {}
- fuel['jumphost'] = {}
- try:
- fuel['jumphost']['bridges'] = self.get_fuel_bridges()
- except Exception:
- fuel['jumphost']['bridges'] = {}
-
- fuel['network'] = {}
- try:
- fuel['network']['nodes'] = self.get_fuel_nodes(booking)
- except Exception:
- fuel['network']['nodes'] = {}
-
- return fuel
-
- def get_fuel_bridges(self):
- return self.bridge_names
-
- def get_fuel_nodes(self, booking):
- jumphost = booking.opnfv_config.host_opnfv_config.get(
- role__name__iexact="jumphost"
- )
- hosts = booking.resource.hosts.exclude(pk=jumphost.pk)
- nodes = []
- for host in hosts:
- node = {}
- ordered_interfaces = self.get_node_interfaces(host)
- node['interfaces'] = [iface['name'] for iface in ordered_interfaces]
- node['bus_addrs'] = [iface['bus'] for iface in ordered_interfaces]
- nodes.append(node)
-
- return nodes
-
- def get_node_interfaces(self, node):
- # TODO: this should sync with pdf ordering
- interfaces = []
-
- for iface in node.interfaces.all():
- interfaces.append({"name": iface.name, "bus": iface.bus_address})
-
- return interfaces
diff --git a/src/resource_inventory/migrations/0023_cloudinitfile_generated.py b/src/resource_inventory/migrations/0023_cloudinitfile_generated.py
index b309753..eb10756 100644
--- a/src/resource_inventory/migrations/0023_cloudinitfile_generated.py
+++ b/src/resource_inventory/migrations/0023_cloudinitfile_generated.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2 on 2021-12-17 18:54
+# Generated by Django 2.2 on 2023-06-07 19:48
from django.db import migrations, models
diff --git a/src/resource_inventory/migrations/0024_auto_20230608_1913.py b/src/resource_inventory/migrations/0024_auto_20230608_1913.py
new file mode 100644
index 0000000..1c8ea47
--- /dev/null
+++ b/src/resource_inventory/migrations/0024_auto_20230608_1913.py
@@ -0,0 +1,262 @@
+# Generated by Django 2.2 on 2023-06-08 19:13
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0023_auto_20230608_1913'),
+ ('booking', '0010_auto_20230608_1913'),
+ ('resource_inventory', '0023_cloudinitfile_generated'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='cpuprofile',
+ name='host',
+ ),
+ migrations.RemoveField(
+ model_name='diskprofile',
+ name='host',
+ ),
+ migrations.RemoveField(
+ model_name='image',
+ name='from_lab',
+ ),
+ migrations.RemoveField(
+ model_name='image',
+ name='os',
+ ),
+ migrations.RemoveField(
+ model_name='image',
+ name='owner',
+ ),
+ migrations.RemoveField(
+ model_name='installer',
+ name='sup_scenarios',
+ ),
+ migrations.RemoveField(
+ model_name='interface',
+ name='acts_as',
+ ),
+ migrations.RemoveField(
+ model_name='interface',
+ name='config',
+ ),
+ migrations.RemoveField(
+ model_name='interface',
+ name='profile',
+ ),
+ migrations.RemoveField(
+ model_name='interfaceconfiguration',
+ name='connections',
+ ),
+ migrations.RemoveField(
+ model_name='interfaceconfiguration',
+ name='profile',
+ ),
+ migrations.RemoveField(
+ model_name='interfaceconfiguration',
+ name='resource_config',
+ ),
+ migrations.RemoveField(
+ model_name='interfaceprofile',
+ name='host',
+ ),
+ migrations.RemoveField(
+ model_name='network',
+ name='bundle',
+ ),
+ migrations.RemoveField(
+ model_name='networkconnection',
+ name='network',
+ ),
+ migrations.RemoveField(
+ model_name='networkrole',
+ name='network',
+ ),
+ migrations.RemoveField(
+ model_name='opnfvconfig',
+ name='installer',
+ ),
+ migrations.RemoveField(
+ model_name='opnfvconfig',
+ name='networks',
+ ),
+ migrations.RemoveField(
+ model_name='opnfvconfig',
+ name='scenario',
+ ),
+ migrations.RemoveField(
+ model_name='opnfvconfig',
+ name='template',
+ ),
+ migrations.RemoveField(
+ model_name='opsys',
+ name='from_lab',
+ ),
+ migrations.RemoveField(
+ model_name='physicalnetwork',
+ name='bundle',
+ ),
+ migrations.RemoveField(
+ model_name='physicalnetwork',
+ name='generic_network',
+ ),
+ migrations.RemoveField(
+ model_name='ramprofile',
+ name='host',
+ ),
+ migrations.RemoveField(
+ model_name='resourcebundle',
+ name='template',
+ ),
+ migrations.RemoveField(
+ model_name='resourceconfiguration',
+ name='cloud_init_files',
+ ),
+ migrations.RemoveField(
+ model_name='resourceconfiguration',
+ name='image',
+ ),
+ migrations.RemoveField(
+ model_name='resourceconfiguration',
+ name='profile',
+ ),
+ migrations.RemoveField(
+ model_name='resourceconfiguration',
+ name='template',
+ ),
+ migrations.RemoveField(
+ model_name='resourceopnfvconfig',
+ name='opnfv_config',
+ ),
+ migrations.RemoveField(
+ model_name='resourceopnfvconfig',
+ name='resource_config',
+ ),
+ migrations.RemoveField(
+ model_name='resourceopnfvconfig',
+ name='role',
+ ),
+ migrations.RemoveField(
+ model_name='resourceprofile',
+ name='labs',
+ ),
+ migrations.RemoveField(
+ model_name='resourcetemplate',
+ name='copy_of',
+ ),
+ migrations.RemoveField(
+ model_name='resourcetemplate',
+ name='lab',
+ ),
+ migrations.RemoveField(
+ model_name='resourcetemplate',
+ name='owner',
+ ),
+ migrations.RemoveField(
+ model_name='server',
+ name='bundle',
+ ),
+ migrations.RemoveField(
+ model_name='server',
+ name='config',
+ ),
+ migrations.RemoveField(
+ model_name='server',
+ name='interfaces',
+ ),
+ migrations.RemoveField(
+ model_name='server',
+ name='lab',
+ ),
+ migrations.RemoveField(
+ model_name='server',
+ name='profile',
+ ),
+ migrations.RemoveField(
+ model_name='server',
+ name='remote_management',
+ ),
+ migrations.RemoveField(
+ model_name='vlan',
+ name='network',
+ ),
+ migrations.DeleteModel(
+ name='CloudInitFile',
+ ),
+ migrations.DeleteModel(
+ name='CpuProfile',
+ ),
+ migrations.DeleteModel(
+ name='DiskProfile',
+ ),
+ migrations.DeleteModel(
+ name='Image',
+ ),
+ migrations.DeleteModel(
+ name='Installer',
+ ),
+ migrations.DeleteModel(
+ name='Interface',
+ ),
+ migrations.DeleteModel(
+ name='InterfaceConfiguration',
+ ),
+ migrations.DeleteModel(
+ name='InterfaceProfile',
+ ),
+ migrations.DeleteModel(
+ name='Network',
+ ),
+ migrations.DeleteModel(
+ name='NetworkConnection',
+ ),
+ migrations.DeleteModel(
+ name='NetworkRole',
+ ),
+ migrations.DeleteModel(
+ name='OPNFVConfig',
+ ),
+ migrations.DeleteModel(
+ name='OPNFVRole',
+ ),
+ migrations.DeleteModel(
+ name='Opsys',
+ ),
+ migrations.DeleteModel(
+ name='PhysicalNetwork',
+ ),
+ migrations.DeleteModel(
+ name='RamProfile',
+ ),
+ migrations.DeleteModel(
+ name='RemoteInfo',
+ ),
+ migrations.DeleteModel(
+ name='ResourceBundle',
+ ),
+ migrations.DeleteModel(
+ name='ResourceConfiguration',
+ ),
+ migrations.DeleteModel(
+ name='ResourceOPNFVConfig',
+ ),
+ migrations.DeleteModel(
+ name='ResourceProfile',
+ ),
+ migrations.DeleteModel(
+ name='ResourceTemplate',
+ ),
+ migrations.DeleteModel(
+ name='Scenario',
+ ),
+ migrations.DeleteModel(
+ name='Server',
+ ),
+ migrations.DeleteModel(
+ name='Vlan',
+ ),
+ ]
diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py
index 5d87430..a143cfd 100644
--- a/src/resource_inventory/models.py
+++ b/src/resource_inventory/models.py
@@ -6,6 +6,14 @@
# 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
+############################################################################################################################################################
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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
@@ -22,684 +30,9 @@ from collections import Counter
from account.models import Lab
from dashboard.utils import AbstractModelQuery
-"""
-Profiles of resources hosted by labs.
-
-These describe hardware attributes of the different Resources a lab hosts.
-A single Resource subclass (e.g. Server) may have instances that point to different
-Profile models (e.g. an x86 server profile and armv8 server profile.
-"""
-
-
-class ResourceProfile(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=200, unique=True)
- architecture = models.CharField(max_length=50, choices=[
- ("x86_64", "x86_64"),
- ("aarch64", "aarch64")
- ])
- description = models.TextField()
- labs = models.ManyToManyField(Lab, related_name="resourceprofiles")
-
- def validate(self):
- validname = re.compile(r"^[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
-
- def get_resources(self, lab=None, working=True, unreserved=False):
- """
- Return a list of Resource objects which have this profile.
-
- If lab is provided, only resources at that lab will be returned.
- If working=True, will only return working hosts
- """
- resources = []
- query = Q(profile=self)
- if lab:
- query = query & Q(lab=lab)
- if working:
- query = query & Q(working=True)
-
- resources = ResourceQuery.filter(query)
-
- if unreserved:
- resources = [r for r in resources if not r.is_reserved()]
-
- return resources
-
-
-class InterfaceProfile(models.Model):
- id = models.AutoField(primary_key=True)
- speed = models.IntegerField()
- name = models.CharField(max_length=100)
- host = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE, related_name='interfaceprofile')
- nic_type = models.CharField(
- max_length=50,
- choices=[
- ("onboard", "onboard"),
- ("pcie", "pcie")
- ],
- default="onboard"
- )
- order = models.IntegerField(default=-1)
-
- 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(ResourceProfile, on_delete=models.CASCADE, 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(ResourceProfile, on_delete=models.CASCADE, related_name='cpuprofile')
- cflags = models.TextField(null=True, blank=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(ResourceProfile, on_delete=models.CASCADE, related_name='ramprofile')
-
- def __str__(self):
- return str(self.amount) + "G for " + str(self.host)
-
-
-"""
-Resource Models
-
-These models represent actual hardware resources
-with varying degrees of abstraction.
-"""
-
-
-class CloudInitFile(models.Model):
- text = models.TextField()
-
- # higher priority is applied later, so "on top" of existing files
- priority = models.IntegerField()
- generated = models.BooleanField(default=False)
-
- @classmethod
- def merge_strategy(cls):
- return [
- {'name': 'list', 'settings': ['append']},
- {'name': 'dict', 'settings': ['recurse_list', 'replace']},
- ]
-
- @classmethod
- def create(cls, text="", priority=0):
- return CloudInitFile.objects.create(priority=priority, text=text)
-
-
-class ResourceTemplate(models.Model):
- """
- Models a "template" of a complete, configured collection of resources that can be booked.
-
- For example, this may represent a Pharos POD. This model is a template of the actual
- resources that will be booked. This model can be "instantiated" into real resource models
- across multiple different bookings.
- """
-
- # TODO: template might not be a good name because this is a collection of lots of configured resources
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=300)
- xml = models.TextField()
- owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
- lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL, related_name="resourcetemplates")
- description = models.CharField(max_length=1000, default="")
- public = models.BooleanField(default=False)
- temporary = models.BooleanField(default=False)
- copy_of = models.ForeignKey("ResourceTemplate", blank=True, null=True, on_delete=models.SET_NULL)
-
- # if these fields are empty ("") then they are implicitly "every vlan",
- # otherwise we filter any allocations we try to instantiate against this list
- # they should be represented as a json list of integers
- private_vlan_pool = models.TextField(default="")
- public_vlan_pool = models.TextField(default="")
-
- def private_vlan_pool_set(self):
- if self.private_vlan_pool != "":
- return set(json.loads(self.private_vlan_pool))
- else:
- return None
-
- def public_vlan_pool_set(self):
- if self.private_vlan_pool != "":
- return set(json.loads(self.public_vlan_pool))
- else:
- return None
-
- def getConfigs(self):
- configs = self.resourceConfigurations.all()
- return list(configs)
-
- def get_required_resources(self):
- profiles = Counter([str(config.profile) for config in self.getConfigs()])
- return dict(profiles)
-
- def __str__(self):
- return self.name
-
-
-class ResourceBundle(models.Model):
- """
- Collection of Resource objects.
-
- This is just a way of aggregating all the resources in a booking into a single model.
- """
-
- template = models.ForeignKey(ResourceTemplate, on_delete=models.SET_NULL, null=True)
-
- def __str__(self):
- if self.template is None:
- return "Resource bundle " + str(self.id) + " with no template"
- return "instance of " + str(self.template)
-
- def get_resources(self):
- return ResourceQuery.filter(bundle=self)
-
- def get_resource_with_role(self, role):
- # TODO
- pass
-
- def release(self):
- for pn in PhysicalNetwork.objects.filter(bundle=self).all():
- try:
- pn.release()
- except Exception as e:
- print("Exception occurred while trying to release resource ", pn.vlan_id)
- print(e)
- traceback.print_exc()
-
- for resource in self.get_resources():
- try:
- resource.release()
- except Exception as e:
- print("Exception occurred while trying to release resource ", resource)
- print(e)
- traceback.print_exc()
-
- def get_template_name(self):
- if not self.template:
- return ""
- if not self.template.temporary:
- return self.template.name
- return self.template.copy_of.name
-
-
-class ResourceConfiguration(models.Model):
- """Model to represent a complete configuration for a single physical Resource."""
-
- id = models.AutoField(primary_key=True)
- profile = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE)
- image = models.ForeignKey("Image", on_delete=models.PROTECT)
- template = models.ForeignKey(ResourceTemplate, related_name="resourceConfigurations", null=True, on_delete=models.CASCADE)
- is_head_node = models.BooleanField(default=False)
- name = models.CharField(max_length=3000, default="opnfv_host")
-
- cloud_init_files = models.ManyToManyField(CloudInitFile, blank=True)
-
- def __str__(self):
- return str(self.name)
-
- def ci_file_list(self):
- return list(self.cloud_init_files.order_by("priority").all())
-
-
+# Keep for now until migrations are made, otherwise django will get angry
def get_default_remote_info():
- return RemoteInfo.objects.get_or_create(
- address="default",
- mac_address="default",
- password="default",
- user="default",
- management_type="default",
- versions="[default]"
- )[0].pk
-
-
-class Resource(models.Model):
- """
- Super class for all hardware resource models.
-
- Defines methods that must be implemented and common database fields.
- Any new kind of Resource a lab wants to host (White box switch, traffic generator, etc)
- should inherit from this class and fulfill the functional interface
- """
-
- class Meta:
- abstract = True
-
- bundle = models.ForeignKey(ResourceBundle, on_delete=models.SET_NULL, blank=True, null=True)
- profile = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE)
- config = models.ForeignKey(ResourceConfiguration, on_delete=models.SET_NULL, blank=True, null=True)
- working = models.BooleanField(default=True)
- vendor = models.CharField(max_length=100, default="unknown")
- model = models.CharField(max_length=150, default="unknown")
- interfaces = models.ManyToManyField("Interface")
- remote_management = models.ForeignKey("RemoteInfo", default=get_default_remote_info, on_delete=models.SET(get_default_remote_info))
- labid = models.CharField(max_length=200, default="default_id", unique=True)
- lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
-
- def get_configuration(self, state):
- """
- Get configuration of Resource.
-
- Returns the desired configuration for this host as a
- JSON object as defined in the rest api spec.
- state is a ConfigState
- """
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def reserve(self):
- """Reserve this resource for its currently assigned booking."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def release(self):
- """Make this resource available again for new boookings."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def get_interfaces(self):
- """
- Return a list of interfaces on this resource.
-
- The ordering of interfaces should be consistent.
- """
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def is_reserved(self):
- """Return True if this Resource is reserved."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def same_instance(self, other):
- """Return True if this Resource is the same instance as other."""
- raise NotImplementedError("Must implement in concrete Resource classes")
-
- def save(self, *args, **kwargs):
- """Assert that labid is unique across all Resource models."""
- res = ResourceQuery.filter(labid=self.labid)
- if len(res) > 1:
- raise ValidationError("Too many resources with labid " + str(self.labid))
-
- if len(res) == 1:
- if not self.same_instance(res[0]):
- raise ValidationError("Too many resources with labid " + str(self.labid))
- super().save(*args, **kwargs)
-
-
-class RemoteInfo(models.Model):
- address = models.CharField(max_length=15)
- mac_address = models.CharField(max_length=17)
- password = models.CharField(max_length=100)
- user = models.CharField(max_length=100)
- management_type = models.CharField(max_length=50, default="ipmi")
- versions = models.CharField(max_length=100) # json serialized list of floats
-
-
-class Server(Resource):
- """Resource subclass - a basic baremetal server."""
-
- booked = models.BooleanField(default=False)
- name = models.CharField(max_length=200, unique=True)
-
- def __str__(self):
- return self.name
-
- def get_configuration(self, state):
- ipmi = state == ConfigState.NEW
- power = "off" if state == ConfigState.CLEAN else "on"
- image = self.config.image.lab_id if self.config else "unknown"
-
- return {
- "id": self.labid,
- "image": image,
- "hostname": self.config.name,
- "power": power,
- "ipmi_create": str(ipmi)
- }
-
- def get_interfaces(self):
- return list(self.interfaces.all().order_by('bus_address'))
-
- def release(self):
- self.bundle = None
- self.booked = False
- self.save()
-
- def reserve(self):
- self.booked = True
- self.save()
-
- def is_reserved(self):
- return self.booked
-
- def same_instance(self, other):
- return isinstance(other, Server) and other.name == self.name
-
-
-def is_serializable(data):
- try:
- json.dumps(data)
- return True
- except Exception:
- return False
-
-
-class Opsys(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=100)
- lab_id = models.CharField(max_length=100)
- obsolete = models.BooleanField(default=False)
- available = models.BooleanField(default=True) # marked true by Cobbler if it exists there
- from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
-
- indexes = [
- models.Index(fields=['cobbler_id'])
- ]
-
- def new_from_data(data):
- opsys = Opsys()
- opsys.update(data)
- return opsys
-
- def serialize(self):
- d = {}
- for field in vars(self):
- attr = getattr(self, field)
- if is_serializable(attr):
- d[field] = attr
- return d
-
- def update(self, data):
- for field in vars(self):
- if field in data:
- setattr(self, field, data[field] if data[field] else getattr(self, field))
-
- def __str__(self):
- return self.name
-
-
-class Image(models.Model):
- """Model for representing OS images / snapshots of hosts."""
-
- id = models.AutoField(primary_key=True)
- from_lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
- architecture = models.CharField(max_length=50, choices=[
- ("x86_64", "x86_64"),
- ("aarch64", "aarch64"),
- ("unknown", "unknown"),
- ])
- lab_id = models.CharField(max_length=100)
- name = models.CharField(max_length=100)
- owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
- public = models.BooleanField(default=True)
- description = models.TextField()
- os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE)
-
- available = models.BooleanField(default=True) # marked True by cobbler if it exists there
- obsolete = models.BooleanField(default=False)
-
- indexes = [
- models.Index(fields=['architecture']),
- models.Index(fields=['cobbler_id'])
- ]
-
- def __str__(self):
- return self.name
-
- def is_obsolete(self):
- return self.obsolete or self.os.obsolete
-
- def serialize(self):
- d = {}
- for field in vars(self):
- attr = getattr(self, field)
- if is_serializable(attr):
- d[field] = attr
- return d
-
- def update(self, data):
- for field in vars(self):
- if field in data:
- setattr(self, field, data[field] if data[field] else getattr(self, field))
-
- def new_from_data(data):
- img = Image()
- img.update(data)
- return img
-
- def in_use(self):
- for resource in ResourceQuery.filter(config__image=self):
- if resource.is_reserved():
- return True
-
- return False
-
-
-"""
-Networking configuration models
-"""
-
-
-class Network(models.Model):
- id = models.AutoField(primary_key=True)
- name = models.CharField(max_length=200)
- bundle = models.ForeignKey(ResourceTemplate, on_delete=models.CASCADE, related_name="networks")
- is_public = models.BooleanField()
-
- def __str__(self):
- return self.name
-
-
-class PhysicalNetwork(models.Model):
- vlan_id = models.IntegerField()
- generic_network = models.ForeignKey(Network, on_delete=models.CASCADE)
- bundle = models.ForeignKey(ResourceBundle, null=True, blank=True, on_delete=models.CASCADE)
-
- def get_configuration(self, state):
- """
- Get the network configuration.
-
- Collects info about each attached network interface and vlan, etc
- """
- return {}
-
- def reserve(self):
- """Reserve vlan(s) associated with this network."""
- return False
-
- def release(self):
- from booking.models import Booking
-
- booking = Booking.objects.get(resource=self.bundle)
- lab = booking.lab
- vlan_manager = lab.vlan_manager
-
- if self.generic_network.is_public:
- vlan_manager.release_public_vlan(self.vlan_id)
- else:
- vlan_manager.release_vlans([self.vlan_id])
- return False
-
- def __str__(self):
- return 'Physical Network for ' + self.generic_network.name
-
-
-class NetworkConnection(models.Model):
- network = models.ForeignKey(Network, on_delete=models.CASCADE)
- vlan_is_tagged = models.BooleanField()
-
- def __str__(self):
- return 'Connection to ' + self.network.name
-
-
-class Vlan(models.Model):
- id = models.AutoField(primary_key=True)
- vlan_id = models.IntegerField()
- tagged = models.BooleanField()
- public = models.BooleanField(default=False)
- network = models.ForeignKey(PhysicalNetwork, on_delete=models.DO_NOTHING, null=True)
-
- def __str__(self):
- return str(self.vlan_id) + ("_T" if self.tagged else "")
-
-
-class InterfaceConfiguration(models.Model):
- id = models.AutoField(primary_key=True)
- profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
- resource_config = models.ForeignKey(ResourceConfiguration, on_delete=models.CASCADE, related_name='interface_configs')
- connections = models.ManyToManyField(NetworkConnection, blank=True)
-
- def __str__(self):
- return "type " + str(self.profile) + " on host " + str(self.resource_config)
-
-
-"""
-OPNFV / Software configuration models
-"""
-
-
-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 NetworkRole(models.Model):
- name = models.CharField(max_length=100)
- network = models.ForeignKey(Network, on_delete=models.CASCADE)
-
-
-def create_resource_ref_string(for_hosts: [str]) -> str:
- # need to sort the list, then do dump
- for_hosts.sort()
-
- return json.dumps(for_hosts)
-
-
-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)
- template = models.ForeignKey(ResourceTemplate, related_name="opnfv_config", on_delete=models.CASCADE)
- networks = models.ManyToManyField(NetworkRole)
- name = models.CharField(max_length=300, blank=True, default="")
- description = models.CharField(max_length=600, blank=True, default="")
-
- 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
-
+ pass
def get_sentinal_opnfv_role():
- return OPNFVRole.objects.get_or_create(name="deleted", description="Role was deleted.")
-
-
-class ResourceOPNFVConfig(models.Model):
- role = models.ForeignKey(OPNFVRole, related_name="resource_opnfv_configs", on_delete=models.CASCADE)
- resource_config = models.ForeignKey(ResourceConfiguration, related_name="resource_opnfv_config", on_delete=models.CASCADE)
- opnfv_config = models.ForeignKey(OPNFVConfig, related_name="resource_opnfv_config", on_delete=models.CASCADE)
-
-
-class Interface(models.Model):
- id = models.AutoField(primary_key=True)
- mac_address = models.CharField(max_length=17)
- bus_address = models.CharField(max_length=50)
- config = models.ManyToManyField(Vlan)
- acts_as = models.OneToOneField(InterfaceConfiguration, blank=True, null=True, on_delete=models.CASCADE)
- profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
-
- def __str__(self):
- return self.mac_address + " on host " + str(self.profile.host.name)
-
- def clean(self, *args, **kwargs):
- if self.acts_as and self.acts_as.profile != self.profile:
- raise ValidationError("Interface Configuration's Interface Profile does not match Interface Profile chosen for Interface.")
- super().clean(*args, **kwargs)
-
- def save(self, *args, **kwargs):
- self.full_clean()
- super().save(*args, **kwargs)
-
-
-"""
-Some Enums for dealing with global constants.
-"""
-
-
-class OPNFV_SETTINGS():
- """This is a static configuration class."""
-
- # all the required network types in PDF/IDF spec
- NETWORK_ROLES = ["public", "private", "admin", "mgmt"]
-
-
-class ConfigState:
- NEW = 0
- RESET = 100
- CLEAN = 200
-
-
-RESOURCE_TYPES = [Server]
-
-
-class ResourceQuery(AbstractModelQuery):
- model_list = [Server]
+ pass
diff --git a/src/resource_inventory/pdf_templater.py b/src/resource_inventory/pdf_templater.py
deleted file mode 100644
index c4b22fe..0000000
--- a/src/resource_inventory/pdf_templater.py
+++ /dev/null
@@ -1,176 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, 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.template.loader import render_to_string
-import booking
-from resource_inventory.models import Server
-
-
-class PDFTemplater:
- """Utility class to create a full PDF yaml file."""
-
- @classmethod
- def makePDF(cls, booking):
- """Fill the pod descriptor file template with info about the resource."""
- template = "dashboard/pdf.yaml"
- info = {}
- info['details'] = cls.get_pdf_details(booking.resource)
- try:
- info['jumphost'] = cls.get_pdf_jumphost(booking)
- except Exception:
- # filling in jumphost info can be optional in some cases, this shouldn't be a hard error
- info['jumphost'] = {}
- info['nodes'] = cls.get_pdf_nodes(booking)
-
- return render_to_string(template, context=info)
-
- @classmethod
- def get_pdf_details(cls, resource):
- """Info for the "details" section."""
- details = {}
- owner = "Anon"
- email = "email@mail.com"
- resource_lab = resource.template.lab
- lab = resource_lab.name
- location = resource_lab.location
- pod_type = "development"
- link = resource_lab.lab_info_link
-
- try:
- # try to get more specific info that may fail, we dont care if it does
- booking_owner = booking.models.Booking.objects.get(resource=resource).owner
- owner = booking_owner.username
- email = booking_owner.userprofile.email_addr
- except Exception:
- pass
-
- details['contact'] = email
- details['lab'] = lab
- details['link'] = link
- details['owner'] = owner
- details['location'] = location
- details['type'] = pod_type
-
- return details
-
- @classmethod
- def get_jumphost(cls, booking):
- """Return the host designated as the Jumphost for the booking."""
- jumphost = None
- if booking.opnfv_config:
- jumphost_opnfv_config = booking.opnfv_config.host_opnfv_config.get(
- role__name__iexact="jumphost"
- )
- jumphost = booking.resource.hosts.get(config=jumphost_opnfv_config.host_config)
- else: # if there is no opnfv config, use headnode
- jumphost = Server.objects.filter(
- bundle=booking.resource,
- config__is_head_node=True
- ).first()
-
- return jumphost
-
- @classmethod
- def get_pdf_jumphost(cls, booking):
- """Return a dict of all the info for the "jumphost" section."""
- jumphost = cls.get_jumphost(booking)
- jumphost_info = cls.get_pdf_host(jumphost)
- jumphost_info['os'] = jumphost.config.image.os.name
- return jumphost_info
-
- @classmethod
- def get_pdf_nodes(cls, booking):
- """Return a list of all the "nodes" (every host except jumphost)."""
- pdf_nodes = []
- nodes = set(Server.objects.filter(bundle=booking.resource))
- nodes.discard(cls.get_jumphost(booking))
-
- for node in nodes:
- pdf_nodes.append(cls.get_pdf_host(node))
-
- return pdf_nodes
-
- @classmethod
- def get_pdf_host(cls, host):
- """
- Gather all needed info about a host.
-
- returns a dictionary
- """
- host_info = {}
- host_info['name'] = host.name
- host_info['node'] = cls.get_pdf_host_node(host)
- host_info['disks'] = []
- for disk in host.profile.storageprofile.all():
- host_info['disks'].append(cls.get_pdf_host_disk(disk))
-
- host_info['interfaces'] = []
- for interface in host.interfaces.all():
- host_info['interfaces'].append(cls.get_pdf_host_iface(interface))
-
- host_info['remote'] = cls.get_pdf_host_remote_management(host)
-
- return host_info
-
- @classmethod
- def get_pdf_host_node(cls, host):
- """Return "node" info for a given host."""
- d = {}
- d['type'] = "baremetal"
- d['vendor'] = host.vendor
- d['model'] = host.model
- d['memory'] = str(host.profile.ramprofile.first().amount) + "G"
-
- cpu = host.profile.cpuprofile.first()
- d['arch'] = cpu.architecture
- d['cpus'] = cpu.cpus
- d['cores'] = cpu.cores
- cflags = cpu.cflags
- if cflags and cflags.strip():
- d['cpu_cflags'] = cflags
- else:
- d['cpu_cflags'] = "none"
-
- return d
-
- @classmethod
- def get_pdf_host_disk(cls, disk):
- """Return a dict describing the given disk."""
- disk_info = {}
- disk_info['name'] = disk.name
- disk_info['capacity'] = str(disk.size) + "G"
- disk_info['type'] = disk.media_type
- disk_info['interface'] = disk.interface
- disk_info['rotation'] = disk.rotation
- return disk_info
-
- @classmethod
- def get_pdf_host_iface(cls, interface):
- """Return a dict describing given interface."""
- iface_info = {}
- iface_info['features'] = "none"
- iface_info['mac_address'] = interface.mac_address
- iface_info['name'] = interface.profile.name
- speed = str(int(interface.profile.speed / 1000)) + "gb"
- iface_info['speed'] = speed
- return iface_info
-
- @classmethod
- def get_pdf_host_remote_management(cls, host):
- """Get the remote params of the host."""
- man = host.remote_management
- mgmt = {}
- mgmt['address'] = man.address
- mgmt['mac_address'] = man.mac_address
- mgmt['pass'] = man.password
- mgmt['type'] = man.management_type
- mgmt['user'] = man.user
- mgmt['versions'] = [man.versions]
- return mgmt
diff --git a/src/resource_inventory/resource_manager.py b/src/resource_inventory/resource_manager.py
deleted file mode 100644
index 16c106e..0000000
--- a/src/resource_inventory/resource_manager.py
+++ /dev/null
@@ -1,197 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, 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 __future__ import annotations # noqa: F407
-
-import re
-from typing import Optional
-from django.db.models import Q
-
-from dashboard.exceptions import ResourceAvailabilityException
-
-from resource_inventory.models import (
- Resource,
- ResourceBundle,
- ResourceTemplate,
- ResourceConfiguration,
- Network,
- Vlan,
- PhysicalNetwork,
- InterfaceConfiguration,
-)
-
-from account.models import Lab
-from django.contrib.auth.models import User
-
-
-class ResourceManager:
-
- instance = None
-
- def __init__(self):
- pass
-
- @staticmethod
- def getInstance() -> ResourceManager:
- if ResourceManager.instance is None:
- ResourceManager.instance = ResourceManager()
- return ResourceManager.instance
-
- def getAvailableResourceTemplates(self, lab: Lab, user: Optional[User] = None) -> list[ResourceTemplate]:
- filter = Q(public=True)
- if user:
- filter = filter | Q(owner=user)
- filter = filter & Q(temporary=False) & Q(lab=lab)
- return ResourceTemplate.objects.filter(filter)
-
- def templateIsReservable(self, resource_template: ResourceTemplate):
- """
- Check if the required resources to reserve this template is available.
-
- No changes to the database
- """
- # count up hosts
- profile_count = {}
- for config in resource_template.getConfigs():
- if config.profile not in profile_count:
- profile_count[config.profile] = 0
- profile_count[config.profile] += 1
-
- # check that all required hosts are available
- for profile in profile_count.keys():
- available = len(profile.get_resources(lab=resource_template.lab, unreserved=True))
- needed = profile_count[profile]
- if available < needed:
- return False
- return True
-
- # public interface
- def deleteResourceBundle(self, resourceBundle: ResourceBundle):
- raise NotImplementedError("Resource Bundle Deletion Not Implemented")
-
- def releaseResourceBundle(self, resourceBundle: ResourceBundle):
- resourceBundle.release()
-
- def get_vlans(self, resourceTemplate: ResourceTemplate) -> dict[str, int]:
- """
- returns: dict from network name to the associated vlan number (backend vlan id)
- """
- networks = {}
- vlan_manager = resourceTemplate.lab.vlan_manager
- for network in resourceTemplate.networks.all():
- if network.is_public:
- # already throws if can't get requested count, so can always expect public_net to be Some
- public_net = vlan_manager.get_public_vlan(within=resourceTemplate.public_vlan_pool_set())
- vlan_manager.reserve_public_vlan(public_net.vlan)
- networks[network.name] = public_net.vlan
- else:
- # already throws if can't get requested count, so can always index in @ 0
- vlans = vlan_manager.get_vlans(count=1, within=resourceTemplate.private_vlan_pool_set())
- vlan_manager.reserve_vlans(vlans[0])
- networks[network.name] = vlans[0]
- return networks
-
- def instantiateTemplate(self, resource_template: ResourceTemplate):
- """
- Convert a ResourceTemplate into a ResourceBundle.
-
- Takes in a ResourceTemplate and reserves all the
- Resources needed and returns a completed ResourceBundle.
- """
- resource_bundle = ResourceBundle.objects.create(template=resource_template)
- res_configs = resource_template.getConfigs()
- resources = []
-
- vlan_map = self.get_vlans(resource_template)
-
- for config in res_configs:
- try:
- phys_res = self.acquireHost(config)
- phys_res.bundle = resource_bundle
- phys_res.config = config
- resources.append(phys_res)
-
- self.configureNetworking(resource_bundle, phys_res, vlan_map)
- phys_res.save()
-
- except Exception as e:
- self.fail_acquire(resources, vlan_map, resource_template)
- raise e
-
- return resource_bundle
-
- def configureNetworking(self, resource_bundle: ResourceBundle, resource: Resource, vlan_map: dict[str, int]):
- """
- @vlan_map: dict from network name to the associated vlan number (backend vlan id)
- """
- for physical_interface in resource.interfaces.all():
-
- # assign interface configs
- iface_config = InterfaceConfiguration.objects.get(
- profile=physical_interface.profile,
- resource_config=resource.config
- )
-
- physical_interface.acts_as = iface_config
- physical_interface.acts_as.save()
-
- physical_interface.config.clear()
- for connection in iface_config.connections.all():
- physicalNetwork = PhysicalNetwork.objects.create(
- vlan_id=vlan_map[connection.network.name],
- generic_network=connection.network,
- bundle=resource_bundle,
- )
- physical_interface.config.add(
- Vlan.objects.create(
- vlan_id=vlan_map[connection.network.name],
- tagged=connection.vlan_is_tagged,
- public=connection.network.is_public,
- network=physicalNetwork
- )
- )
-
- # private interface
- def acquireHost(self, resource_config: ResourceConfiguration) -> Resource:
- resources = resource_config.profile.get_resources(
- lab=resource_config.template.lab,
- unreserved=True
- )
-
- try:
- resource = resources[0] # TODO: should we randomize and 'load balance' the servers?
- resource.config = resource_config
- resource.reserve()
- return resource
- except IndexError:
- raise ResourceAvailabilityException("No available resources of requested type")
-
- def releaseNetworks(self, template, vlans):
- vlan_manager = template.lab.vlan_manager
- for net_name, vlan_id in vlans.items():
- net = Network.objects.get(name=net_name, bundle=template)
- if (net.is_public):
- vlan_manager.release_public_vlan(vlan_id)
- else:
- vlan_manager.release_vlans(vlan_id)
-
- def fail_acquire(self, hosts, vlans, template):
- self.releaseNetworks(template, vlans)
- for host in hosts:
- host.release()
-
-
-class HostNameValidator(object):
- regex = r'^[A-Za-z0-9][A-Za-z0-9-]*$'
- message = "Hostnames can only contain alphanumeric characters and hyphens (-). Hostnames must start with a letter"
- pattern = re.compile(regex)
-
- @classmethod
- def is_valid_hostname(cls, hostname):
- return len(hostname) < 65 and cls.pattern.fullmatch(hostname) is not None
diff --git a/src/resource_inventory/tests/test_managers.py b/src/resource_inventory/tests/test_managers.py
deleted file mode 100644
index 46cee5a..0000000
--- a/src/resource_inventory/tests/test_managers.py
+++ /dev/null
@@ -1,301 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, 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.test import TestCase
-from django.contrib.auth.models import User
-
-from resource.inventory_manager import InventoryManager
-from resource.resource_manager import ResourceManager, HostNameValidator
-from account.models import Lab
-from resource.models import (
- Host,
- Vlan,
- Interface,
- ResourceBundle,
- GenericHost,
- GenericResourceBundle,
- CpuProfile,
- RamProfile,
- DiskProfile,
- HostProfile,
- InterfaceProfile
-)
-
-
-class InventoryManagerTestCase(TestCase):
-
- def test_singleton(self):
- instance = InventoryManager.getInstance()
- self.assertTrue(isinstance(instance, InventoryManager))
- self.assertTrue(instance is InventoryManager.getInstance())
-
- def setUp(self):
- # setup
- # create lab and give it resources
- user = User.objects.create(username="username")
- self.lab = Lab.objects.create(
- lab_user=user,
- name='test lab',
- contact_email='someone@email.com',
- contact_phone='dont call me'
- )
-
- # create hostProfile
- hostProfile = HostProfile.objects.create(
- host_type=0,
- name='Test profile',
- description='a test profile'
- )
- InterfaceProfile.objects.create(
- speed=1000,
- name='eno3',
- host=hostProfile
- )
- DiskProfile.objects.create(
- size=1000,
- media_type="SSD",
- name='/dev/sda',
- host=hostProfile
- )
- CpuProfile.objects.create(
- cores=96,
- architecture="x86_64",
- cpus=2,
- host=hostProfile
- )
- RamProfile.objects.create(
- amount=256,
- channels=4,
- host=hostProfile
- )
-
- # create GenericResourceBundle
- genericBundle = GenericResourceBundle.objects.create()
-
- self.gHost1 = GenericHost.objects.create(
- bundle=genericBundle,
- name='generic host 1',
- profile=hostProfile
- )
- self.gHost2 = GenericHost.objects.create(
- bundle=genericBundle,
- name='generic host 2',
- profile=hostProfile
- )
-
- # actual resource bundle
- bundle = ResourceBundle.objects.create(template=genericBundle)
-
- self.host1 = Host.objects.create(
- template=self.gHost1,
- booked=True,
- name='host1',
- bundle=bundle,
- profile=hostProfile,
- lab=self.lab
- )
-
- self.host2 = Host.objects.create(
- template=self.gHost2,
- booked=True,
- name='host2',
- bundle=bundle,
- profile=hostProfile,
- lab=self.lab
- )
-
- vlan1 = Vlan.objects.create(vlan_id=300, tagged=False)
- vlan2 = Vlan.objects.create(vlan_id=300, tagged=False)
-
- Interface.objects.create(
- mac_address='00:11:22:33:44:55',
- bus_address='some bus address',
- switch_name='switch1',
- port_name='port10',
- config=vlan1,
- host=self.host1
- )
- Interface.objects.create(
- mac_address='00:11:22:33:44:56',
- bus_address='some bus address',
- switch_name='switch1',
- port_name='port12',
- config=vlan2,
- host=self.host2
- )
-
- def test_acquire_host(self):
- host = InventoryManager.getInstance().acquireHost(self.gHost1, self.lab.name)
- self.assertNotEquals(host, None)
- self.assertTrue(host.booked)
- self.assertEqual(host.template, self.gHost1)
-
- def test_release_host(self):
- host = InventoryManager.getInstance().acquireHost(self.gHost1, self.lab.name)
- self.assertTrue(host.booked)
- InventoryManager.getInstance().releaseHost(host)
- self.assertFalse(host.booked)
-
-
-class ResourceManagerTestCase(TestCase):
- def test_singleton(self):
- instance = ResourceManager.getInstance()
- self.assertTrue(isinstance(instance, ResourceManager))
- self.assertTrue(instance is ResourceManager.getInstance())
-
- def setUp(self):
- # setup
- # create lab and give it resources
- user = User.objects.create(username="username")
- self.lab = Lab.objects.create(
- lab_user=user,
- name='test lab',
- contact_email='someone@email.com',
- contact_phone='dont call me'
- )
-
- # create hostProfile
- hostProfile = HostProfile.objects.create(
- host_type=0,
- name='Test profile',
- description='a test profile'
- )
- InterfaceProfile.objects.create(
- speed=1000,
- name='eno3',
- host=hostProfile
- )
- DiskProfile.objects.create(
- size=1000,
- media_type="SSD",
- name='/dev/sda',
- host=hostProfile
- )
- CpuProfile.objects.create(
- cores=96,
- architecture="x86_64",
- cpus=2,
- host=hostProfile
- )
- RamProfile.objects.create(
- amount=256,
- channels=4,
- host=hostProfile
- )
-
- # create GenericResourceBundle
- genericBundle = GenericResourceBundle.objects.create()
-
- self.gHost1 = GenericHost.objects.create(
- bundle=genericBundle,
- name='generic host 1',
- profile=hostProfile
- )
- self.gHost2 = GenericHost.objects.create(
- bundle=genericBundle,
- name='generic host 2',
- profile=hostProfile
- )
-
- # actual resource bundle
- bundle = ResourceBundle.objects.create(template=genericBundle)
-
- self.host1 = Host.objects.create(
- template=self.gHost1,
- booked=True,
- name='host1',
- bundle=bundle,
- profile=hostProfile,
- lab=self.lab
- )
-
- self.host2 = Host.objects.create(
- template=self.gHost2,
- booked=True,
- name='host2',
- bundle=bundle,
- profile=hostProfile,
- lab=self.lab
- )
-
- vlan1 = Vlan.objects.create(vlan_id=300, tagged=False)
- vlan2 = Vlan.objects.create(vlan_id=300, tagged=False)
-
- Interface.objects.create(
- mac_address='00:11:22:33:44:55',
- bus_address='some bus address',
- switch_name='switch1',
- port_name='port10',
- config=vlan1,
- host=self.host1
- )
- Interface.objects.create(
- mac_address='00:11:22:33:44:56',
- bus_address='some bus address',
- switch_name='switch1',
- port_name='port12',
- config=vlan2,
- host=self.host2
- )
-
- def test_convert_bundle(self):
- ResourceManager.getInstance().convertResoureBundle(self.genericBundle, self.lab.name)
- # verify bundle configuration
-
-
-class HostNameValidatorTestCase(TestCase):
-
- def test_valid_hostnames(self):
- self.assertTrue(HostNameValidator.is_valid_hostname("localhost"))
- self.assertTrue(HostNameValidator.is_valid_hostname("Localhost"))
- self.assertTrue(HostNameValidator.is_valid_hostname("localHost"))
- self.assertTrue(HostNameValidator.is_valid_hostname("LOCALHOST"))
- self.assertTrue(HostNameValidator.is_valid_hostname("f"))
- self.assertTrue(HostNameValidator.is_valid_hostname("abc123doreyme"))
- self.assertTrue(HostNameValidator.is_valid_hostname("F9999999"))
- self.assertTrue(HostNameValidator.is_valid_hostname("my-host"))
- self.assertTrue(HostNameValidator.is_valid_hostname("My-Host"))
- self.assertTrue(HostNameValidator.is_valid_hostname("MY-HOST"))
- self.assertTrue(HostNameValidator.is_valid_hostname("a-long-name-for-my-host"))
-
- def test_invalid_hostnames(self):
- self.assertFalse(HostNameValidator.is_valid_hostname("-long-name-for-my-host"))
- self.assertFalse(HostNameValidator.is_valid_hostname("546"))
- self.assertFalse(HostNameValidator.is_valid_hostname("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
-
- def test_invalid_chars(self):
- self.assertFalse(HostNameValidator.is_valid_hostname("contains!char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains@char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains#char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains$char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains%char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains^char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains&char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains*char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains(char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains)char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains_char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains=char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains+char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains|char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains\\char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains[char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains]char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains;char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains:char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains'char"))
- self.assertFalse(HostNameValidator.is_valid_hostname('contains"char'))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains'char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains<char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains>char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains,char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains?char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains/char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains`char"))
- self.assertFalse(HostNameValidator.is_valid_hostname("contains~char"))
diff --git a/src/resource_inventory/tests/test_models.py b/src/resource_inventory/tests/test_models.py
deleted file mode 100644
index 3f2d1d8..0000000
--- a/src/resource_inventory/tests/test_models.py
+++ /dev/null
@@ -1,173 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, 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.test import TestCase
-from django.contrib.auth.models import User
-from account.models import Lab
-from resource_inventory.models import (
- Scenario,
- Installer,
- Opsys,
- ConfigBundle,
- OPNFVConfig,
- OPNFVRole,
- Image,
- HostProfile,
- GenericResourceBundle,
- GenericResource,
- GenericHost,
- HostConfiguration
-)
-
-
-class ConfigUtil():
- count = 0
-
- @staticmethod
- def makeScenario():
- return Scenario.objects.create(name="testScenario")
-
- @staticmethod
- def makeInstaller():
- inst = Installer.objects.create(name="testInstaller")
- inst.sup_scenarios = [ConfigUtil.makeScenario()]
- return inst
-
- @staticmethod
- def makeOpsys():
- os = Opsys.objects.create(name="test Operating System")
- os.sup_installers = [ConfigUtil.makeInstaller()]
- return os
-
- @staticmethod
- def makeConfigBundle():
- user = User.objects.create(username="test_user" + str(ConfigUtil.count))
- ConfigUtil.count += 1
- return ConfigBundle.objects.create(owner=user)
-
- @staticmethod
- def makeOPNFVConfig():
- installer = ConfigUtil.makeInstaller()
- scenario = ConfigUtil.makeScenario()
- bundle = ConfigUtil.makeConfigBundle()
- return OPNFVConfig.objects.create(
- installer=installer,
- scenario=scenario,
- bundle=bundle
- )
-
- @staticmethod
- def makeOPNFVRole():
- return OPNFVRole.objects.create(
- name="Test role",
- description="This is a test role"
- )
-
- @staticmethod
- def makeImage():
- owner = User.objects.create(username="another test user")
- lab_user = User.objects.create(username="labUserForTests")
- lab = Lab.objects.create(
- lab_user=lab_user,
- name="this is lab for testing",
- contact_email="email@mail.com",
- contact_phone="123-4567"
- )
-
- return Image.objects.create(
- cobbler_id="profile1",
- from_lab=lab,
- name="an image for testing",
- owner=owner
- )
-
- @staticmethod
- def makeGenericHost():
- profile = HostProfile.objects.create(
- host_type=0,
- name="test lab for config bundle",
- description="this is a test profile"
- )
- user = User.objects.create(username="test sample user 12")
- bundle = GenericResourceBundle.objects.create(
- name="Generic bundle for config tests",
- xml="",
- owner=user,
- description=""
- )
-
- resource = GenericResource.objects.create(
- bundle=bundle,
- name="a test generic resource"
- )
-
- return GenericHost.objects.create(
- profile=profile,
- resource=resource
- )
-
- @staticmethod
- def makeHostConfiguration():
- host = ConfigUtil.makeGenericHost()
- image = ConfigUtil.makeImage()
- bundle = ConfigUtil.makeConfigBundle()
- opnfvRole = ConfigUtil.makeOPNFVRole()
- return HostConfiguration.objects.create(
- host=host,
- image=image,
- bundle=bundle,
- opnfvRole=opnfvRole
- )
-
-
-class ScenarioTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeScenario())
-
-
-class InstallerTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeInstaller())
-
-
-class OperatingSystemTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeOpsys())
-
-
-class ConfigBundleTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeConfigBundle())
-
-
-class OPNFVConfigTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeOPNFVConfig())
-
-
-class OPNFVRoleTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeOPNFVRole())
-
-
-class HostConfigurationTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeHostConfiguration())
-
-
-class ImageTestCase(TestCase):
-
- def test_save(self):
- self.assertTrue(ConfigUtil.makeImage())
diff --git a/src/resource_inventory/urls.py b/src/resource_inventory/urls.py
index a9a4d43..f9bd07e 100644
--- a/src/resource_inventory/urls.py
+++ b/src/resource_inventory/urls.py
@@ -7,30 +7,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
-"""
-laas_dashboard URL Configuration.
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/1.10/topics/http/urls/
-Examples:
-Function views
- 1. Add an import: from my_app import views
- 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
-Class-based views
- 1. Add an import: from other_app.views import Home
- 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
-Including another URLconf
- 1. Import the include() function: from django.conf.urls import url, include
- 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
-"""
-from django.conf.urls import url
-from resource_inventory.views import HostView, hostprofile_detail_view
-
-
-app_name = 'resource'
-urlpatterns = [
- url(r'^hosts$', HostView.as_view(), name='hosts'),
- url(r'^profiles/(?P<hostprofile_id>.+)/$', hostprofile_detail_view, name='host_detail'),
-]
diff --git a/src/resource_inventory/views.py b/src/resource_inventory/views.py
index 52f8c75..f903394 100644
--- a/src/resource_inventory/views.py
+++ b/src/resource_inventory/views.py
@@ -6,33 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
-from django.views.generic import TemplateView
-from django.shortcuts import get_object_or_404
-from django.shortcuts import render
-
-from resource_inventory.models import ResourceProfile, ResourceQuery
-
-
-class HostView(TemplateView):
- template_name = "resource/hosts.html"
-
- def get_context_data(self, **kwargs):
- context = super(HostView, self).get_context_data(**kwargs)
- hosts = ResourceQuery.filter(working=True)
- context.update({'hosts': hosts, 'title': "Hardware Resources"})
- return context
-
-
-def hostprofile_detail_view(request, hostprofile_id):
- hostprofile = get_object_or_404(ResourceProfile, id=hostprofile_id)
-
- return render(
- request,
- "resource/hostprofile_detail.html",
- {
- 'title': "Host Type: " + str(hostprofile.name),
- 'hostprofile': hostprofile
- }
- )